In the previous example we pushed a new branch to GitHub, but what if someone subsequently adds some commits to the branch, how do I know? How do I get those changes? Well the way to bring in the new commits is to use
git pull but before we start using it we need to know a few more git internals to do with tracking changes in other repos.
Because git is distributed a given repo on a given ‘remote’ may contain branches and commits that your repo doesn’t, and vice versa. Git tries to monitor the differences between repos using ‘tracking branches’. For every remote that you define git will take a snapshot of the branches in that repo and store this in a tracking branch. These are distinct from the branches in your local repo. You cannot check them out to work them as such but you can inspect them and compare them to your local branches. You can tell a remote tracking branch apart form a local branch by its name; it will generally be prefixed with the name of the remote. So typically you would have something like
origin/master. When viewing your repo in gitg or gitk you can see the remote tracking branches in amongst everything else; they just appear as pointers like any other branch.
Why is this important? Well let’s say you commit to the feature1 branch. Your local branch is now 1 commit ahead of the remote branch as illustrated here:
Now let’s say you push your changes up to origin. Git will update it’s tracking branch to reflect the changes you have made to the remote repo which should resolve as a fast-forward (i.e. the branch pointer for origin/master is simply moved along the line of commits – no merging is required):
This is all good stuff but it assumes that no one else committed to the feature1 branch in the meantime. If they had then their commit may conflict with yours, so what do you do? Well when you ask git to push your branch to origin, git will first of all update it’s tracking branch, which in our example would look something like this. Note the divergence of your branch with the remote branch:
Git cannot resolve this as a fast forward merge as the branches have diverged (see section on merging) so it has to create a merge commit which is potentially risky (there could be conflicts) so git blocks the push. However if you had updated your local branch with the updates from origin beforehand (using git pull – more on that later) then git would have attempted the merge when updating your local branch. Because it is local git accepts the risk of running the merge and getting conflicts because you are there to resolve them. After updating your local branch the graph would look something like this:
Note how the origin/feature1 branch has been merged into the local branch yet the remote branch is still pointing at the same commit. Once your local branch is updated it should be fine to push it as the merge into the remote branch should resolve as a fast forward this time. After pushing this time the graph will look something like this:
Note that the only change is to move the origin/feature1 branch pointer forward as it was a fast-forward merge. This scenario is reasonably common when multiple people are working in the same branch. It can be avoided with careful use of the rebase command, but rebase is dangerous and complicated so we’ll steer clear for now. There is no danger in the above happening it just isn’t as neat as it could be.
As you can see the distributed nature of git makes it quite complex to push changes between different remote repos but git is configured with a careful set of defaults so it is quite hard to get yourself into trouble. If you follow the standard user guide you should be fine but if git blocks you from doing something it is probably for good reason so dont try and override it.