The concept of remote repositories is that there is a remote server and we take our changes and put them on the remote server so that people or contributors can see the changes and make use of them.
The only difference between repositories locally on your computer or on a publicly available server is that other users can see the repository that is on the server. There are many websites that can host your repository files and give you a public URI and Shell access, this will make a copy of your repository on an online server that other interested people can access.
Example websites include, GitHub, Gitlab and bitbucket and more. We’ll be using github, but they’re all somehow similar.
First, you have to understand some things, take a look at the following diagram:
From looking at the shapes:
And from looking at the numbers:
Some notes and clarifications:
Note: I’ll be referring to the word “repository” as “repo” from now on.
Pushing, fetching and merging
by pushing we copy the commits from our local repo to the remote repo, at the same time, git also makes another branch on our local computer that is typically called
origin/branch_name, origin is the name of the remote, demonstrated as the line in the previous diagram. And branch_name is the branch on the local mirror this branch references the remote branch and always tries to be synced with it.
Hence: origin is the name of the remote reference pointing to the remote repo (it can have a custom name). Remotes are defined in the local .git folder and refer to branches in the local mirror, which has its own tree of commits cloned from the remote (github) repo.
The local repository and local remote mirror are not actually that separate, you will see that the commit nodes are note really duplicated, however the branch pointers have different values.
by fetching changes from the remote, changes by contributors come to our local origin/master branch (in the mirror). we’re essentially saying sync up my origin/master with the remote server version but it does not bring it to our master branch until we make a merge.
the above illustration is just to make things clear, it is not like that with all those commits duplicated, see below:
Now let’s follow some real example of pushing fetching and merging with remotes. We’ll start with the following states for the local and remote repositories:
If we now make local changes on our repo (like all the things we did before), and then commit the changes creating a new commit node, this will move the master branch to point to the new commit (
HEAD will have a new value). But the
remote\master branch (the local clone of the remote tree) won’t be changed. Look below:
Now we want to put the change we made to Adam’s github repo, so we make a push. What push will do is that it will:
sometimes the remote might be already updated with more commits from another source (like Lynda or john!). in this case git will notify us before pushing. And we will have to first fetch those changes and apply them to our local repo and then upload changes we made. More on this later.
Next, the remote repository was added a new commit (commit 5), this commit may be added by a push from another user on the network (maybe john or Lynda).
We make a fetch to get that change to our local repo, the origin/master is updated. but the master is kept the same, like so:
And to apply the changes to the local repo, we do a simple fast forward merge:
And finally the local repo is synced with the remote again. notice that the origin/master is just a branch but the only difference is that it always tries to stay in sync with what’s on the remote server.
Connecting with a github repo
To create a connection (the remote) from Adam’s repo to GitHub’s repo, run
git remote add [remote name, usually “origin”] [repo URI], to show all of your remote connections, run
git remote, and to remove a remote, run
git remote rm [remote name].
To push a branch (master) to a remote repo (with remote connection “origin” already defined):
git push -u origin master. (it asks for github username and pass)
.git/config has the new remote UIR for the branch and in the remotes section and .git/refs/remotes/origin has all of the “origin” remote branches.
git branch -r shows the remote branches (in the local mirror), and
git branch -a shows both the local and remote branches.
Cloning a remote repository
So we decided to collaborate on an open source project, let’s assume that we are one of the collaborators, the github repository is already there and we have the URI.
Locally we don’t have anything about the project (we don’t have a local repo). So we start form copying or cloning the remote repository first.
to get a clone of the remote repo, got to a directory that is not a git repo, then type
git clone [remote repo URI] [(optional) a name for the directory other than the default repo name].
note: by default, the master branch is the one that is pulled locally, but you can specify which one.
This will create a new directory with the remote repo name, and inside is a git repository (local copy), that you can start working on, by creating new commits and pushing them.
Tracking remote branches
We mentioned tracking before as a good practice when working on feature branches, we merge the master (or the originating) branch to get latest commits (that may be created by other contributors). This reduces chances of merge conflicts at the end, when the feature branch is complete and finally merged back into master.
When working with remotes, the local master branch is setup to track the origin/master branch, this means that whenever you make a push the origin/master is always updated with the master branch and uploaded to the remote server. This happens automatically when you clone a repository.
However, when you create a new branch new_feature locally, it is not setup to track anything on the remote origin, this means if you want to upload that branch to the github repo with a push, and since your branch new_feature is not tracked with any branch on the local mirror, like origin/new_feauture, all commits on the branch won’t be pushed.
You can setup the new branch new_feature to be tracked in one of three ways:
[branch “master”] Remote = origin (this says the master tracks origin/master) Merge = refs/heads/master
Just do the same for new_feature
git config branch.new_feature.remote origin (or whatever the remote name is) git config branch.new_feature.merge refs/heads/mastergit branch –set-upstream new_feature origin/new_feature
this will make another branch called origin/new_feature that will track new_feature and pushed to the remote repo as well.
Git push –u (same as –set-upstream) origin new_feature
Some important notes:
git diff origin/master..master. you can do this after making a local commit and before pushing.
git fetch, or
git pushif you only have one remote repo (we only have origin), and the target branch is the master branch
Some guidelines on fetching
merging after a fetch
After fetching from the remote repo, and seeing the difference between the local branch, “master” for instance and the remote branch (in the local mirror) for example “origin/master”. After that we make a merge between the two, like so:
Note: git doesn’t allow us to checkout to a remote branch (select it), git is responsible of managing this branch.
There is a handy shortcut that combines both fetch and merge commands:
git pull = git fetch + git merge between local and remote branches.
git pull is very convenient, but harder if errors arise, and often people misunderstand it and not knowing that it's a fetch and a merge, so you have to be careful about this.
New branches form remotes
If we’re working as Adam again, and let’s assume that Lynda created a new branch lyndas_branch with some commits and pushed the changes to the Adam’s github repository. Now Adam fetches the changes and Adam’s remote is synced with the github repository. A new branch is added to Adam’s remote and is called origin/lyndas_branch. This branch is not yet on the local repository of Adam and we can’t merge this branch to anything because Adam’s repo doesn’t contain a branch called lyndas_branch. Adam has to create this branch manually and track it with origin/lyndas_branch:
In two steps:
Git branch lyndas_branch origin/lyndas_branch Git checkout lyndas_branch
Or with one step:
Git checkout –b(to create the branch) lyndas_branch origin/lyndas_branch(setup tracking). This will switch to the new branch automatically.
let’s say you are writing on the master branch and now you are ready to push to the remote server, so you have new local commits and there were also new commits added after you did your last fetch (by some other contribute), when you try to push git will refuse to push and will say that there are new changes to the remote repo that you haven’t fetched and I don’t know what to do.
The solution is to fetch then merge the new changes locally and then try and push again.
Deleting a remote branch
Adam wants to tell github to delete one of the branches on GitHub’s repository.
There are two ways, the old way and a new way:
old way (not intuitive)
using a colon:
git push origin :branchX. this however does not delete the local branchX branch
git push origin --delete branchX, this will remove the remote branch origin/branchX and the local branch, and will remove it from the github repository.
I will demonstrate collaboration using github. The same works also for other services like gitlab and bitbucket and others.
You can create an account on github, they will give you a place to put your project git repositories so that it is available on a place where other collaborators can reach.
You can set access limits to your github repo:
If you have a github repo of some project you’re interested in collaborating to, then you can clone this repo and add your own changes. However, you don’t have write access to the repo and you can’t push right away, in this case you make something called a “push request” to the owner of the repo, then the owner can then decide to accept the push and merge the changes to his github repo.
forking a repo makes your own copy of the project on your own github repository which you will have write access to, then you make changes to that and at the end when you finish implementing your ideas, you issue a pull request on the original github repository. you create a pull request with a message stating what you did, and then they decide whether to incorporate the things you did to their repo and maybe accept your changes and incorporate them into the main project. they’ll grab your branch and merge it in. and at that point everyone will have access to your new feature.
A collaboration workflow: Joe and Lynda.
As a recap for git collaboration and with github, let’s follow up with this story between Lynda and Joe. Both already defined the remotes and has read/write access to the repository at github.
From Joe’s point of view
Joe wants to contribute to a project created by Lynda by adding a feature to her project, so he does the following:
now Lynda can see my changes on the remote repo, so Joe send her an email to tell her hey see my changes.
From Lynda’s point of view
she does the following:
now Lynda sends an email to Joe to inform about the change.
Back to Joe
if you’re using long git commands often, then you can create aliases or shortcuts with a custom name, much like Linux aliases. With the command git config:
git config –global alias.st “status”. (see ~/.gitconfig to see added configurations)
now git st is the shorthand for git status.
You can set the aliases in any of the three git config levels (project, global or system).
The following is a list of handy aliases I use very often, which will make working with git easier:
just add them in the same way one by one, or just copy the following to your git config files:
[alias] st = status ci = commit br = branch co = checkout df = diff lg = log -p logg = log --graph --decorate --oneline --abbrev-commit –all