Git Rebase Versus Git Pull

Before continuing the GIT series, it is worthwhile to tangent a bit and write about using git rebase versus git pull. Using git pull will fetch any changes from the remote branch and merge them on to your local branch, creating a new merge commit. Using git rebase will remove each of your commits temporarily (stored in .git/rebase), update your local branch to match the remote branch, then apply each of your local changes again in the order they were originally applied. Both processes will lead to occasion merge conflicts, but the later will produce a cleaner history, without extraneous merge commits.

My personal experience is that git rebase is only more tedious when there are merges, but because it adjusts the git history, it is easier to track commits in a collaborative repository. Especially, when trying to figure out where a code change was originally introduced. Our team exclusively uses git rebase at Votizen.

Getting ready

For the examples today, assume you are working on your local branch master and a remote branch origin/master. The origin/master branch is collaborative and frequently committed to by multiple team members. You updated master from origin/master a few hours ago, made severals commits, and when you try to push, you see the following:

To [email protected]:votizen/common.git
 ! [rejected]        master -> master (non-fast-forward)
error: failed to push some refs to '[email protected]:{YOUR_REMOTE_REPO}.git'
To prevent you from losing history, non-fast-forward updates were rejected
Merge the remote changes (e.g. 'git pull') before pushing again.  See the
'Note about fast-forwards' section of 'git push --help' for details.

The Git commit history looks something like:

- C1 - C2 - C3 - C4 - C5 - C6 - C7 (master)
                  \
                   C5' - C6' - C7' (origin/master)

How do it…

If you follow the instructions in the Git message and pull:

git pull origin master

Git actually does the following two commands:

git fetch origin master
git merge origin/master

And the following happens to the commit tree:

- C1 - C2 - C3 - C4 - C5 - C6 - C7 (master)
                  \               \
                   C5' - C6' - C7' - C8 (origin/master)

On the other hand, if you are to rebase:

git fetch origin master
git rebase origin

The following happens to the commit tree:

- C1 - C2 - C3 - C4 - C5' - C6' - C7' - C5 - C6 - C7 (master)
                                  |
                                  (origin/master)

How it works…

In the first example, the remote changes are fetched and then merged together with your local into a new commit. In some circumstances this will cause the merging committer to be attributed with changes that they never made. In the latter example the remote changes are applied before your changes, and then your changes are applied afterwards. Any merges will be resolved when your changes begin to be applied.

There’s more…

For more information, there is another great article about rebasing on the online Git Book.