Git Merging vs. Rebasing: How Git History is Affected with Real Example

Overview

Git merge and git rebase affect git history differently, complete experiment of git merge and rebase and the resulted git history is given to convince people.

Git Merging vs. Rebasing: How Git History is Affected with Real Example

I will first give the complete git merge and git rebase experiment process as well as the resulted git history comparison with comments on the fly. Then I will recap the conceptual knowledge about git merge and git rebase.

Git Merge and the Git History

So let’s go, if we git merge branch A into Branch B, and say B is branched from A at point p0, then all the history of A after p0 will be added into history of branch B, this is verified by the following example and note the correct command to do the git merge would be: “git checkout B” followed by “git merge A”. Some people argue the history issue is the unpleasing part about using git merge and one reason for them to use git rebase instead:

1) init the git repository

[~/path/to/gitMergeDemo.d]$git init
Initialized empty Git repository in ~/path/to/gitMergeDemo.d/.git/

2) create one source code file say main.cpp and do initial commit to master branch

[~/path/to/gitMergeDemo.d]$git log
commit xxx
Author: Sigmainfy info@tech-wonderland.net
Date: Tue Apr 7 21:12:16 2015

master: init repo

3) create a feature branch and do 3 commits so the history would be 3 commits ahead of the original master branch. Also note it is a good habit to start with the feature branch name like feature-1 here in the comment, because if you merge the feature branch back into master, it would be more clear since there could be more history logs in master branch when different branches merged back. If the comment is clear, it would be easier to check the history.

[~/path/to/gitMergeDemo.d]$git checkout -b feature-1-test-git-merge
Switched to a new branch ‘feature-1-test-git-merge’

commit xxx
Author: Sigmainfy info@tech-wonderland.net
Date: Tue Apr 7 21:14:24 2015

feature-1: 4th commit

commit xxx
Author: Sigmainfy info@tech-wonderland.net
Date: Tue Apr 7 21:13:57 2015

feature-1: 3rd commit:wq

commit xxx
Author: Sigmainfy info@tech-wonderland.net
Date: Tue Apr 7 21:13:30 2015

feature-1: 2nd commit in feature branch

commit xxx
Author: Sigmainfy info@tech-wonderland.net
Date: Tue Apr 7 21:12:16 2015

master: init repo

4) Check out the master and do 2 commits so the two branches depart from each other:

[~/path/to/gitMergeDemo.d]$git checkout master
Switched to branch ‘master’
[~/path/to/gitMergeDemo.d]$git log
commit xxx
Author: Sigmainfy info@tech-wonderland.net
Date: Tue Apr 7 21:15:18 2015

master: 3rd commit

commit xxx
Author: Sigmainfy info@tech-wonderland.net
Date: Tue Apr 7 21:14:55 2015

master: 2nd commit

commit xxx
Author: Sigmainfy info@tech-wonderland.net
Date: Tue Apr 7 21:12:16 2015

master: init repo

5) Merge master branch into branch feature-1:

[~/path/to/gitMergeDemo.d]$git checkout feature-1-test-git-merge
Switched to branch ‘feature-1-test-git-merge’
[~/path/to/gitMergeDemo.d]$git merge master
Auto-merging main.cpp
CONFLICT (content): Merge conflict in main.cpp
Automatic merge failed; fix conflicts and then commit the result.

6) So we need to resolve the conflict in main.cpp, after editing and finalizing the content, do not forget to use “git add” first before “git commit” (other wise the “git commit” will report unresolved merge conflict error), same for git rebase, but use “git rebase –continue” instead of “git commit”:

[~/path/to/gitMergeDemo.d]$git add main.cpp
[~/path/to/gitMergeDemo.d]$git commit
[~/path/to/gitMergeDemo.d]$git log
commit xxx
Merge: aaa bbb
Author: Sigmainfy info@tech-wonderland.net
Date: Tue Apr 7 21:17:35 2015

Merge branch ‘master’ into feature-1-test-git-merge

Conflicts:
main.cpp

commit xxx
Author: Sigmainfy info@tech-wonderland.net
Date: Tue Apr 7 21:15:18 2015

master: 3rd commit

commit xxx
Author: Sigmainfy info@tech-wonderland.net
Date: Tue Apr 7 21:14:55 2015

master: 2nd commit

commit xxx
Author: Sigmainfy info@tech-wonderland.net
Date: Tue Apr 7 21:14:24 2015

feature-1: 4th commit

commit xxx
Author: Sigmainfy info@tech-wonderland.net
Date: Tue Apr 7 21:13:57 2015

feature-1: 3rd commit:wq

commit xxx
Author: Sigmainfy info@tech-wonderland.net
Date: Tue Apr 7 21:13:30 2015

feature-1: 2nd commit in feature branch

commit xxx
Author: Sigmainfy info@tech-wonderland.net
Date: Tue Apr 7 21:12:16 2015

master: init repo

As we can see, the history of the feature branch is sort of “polluted” by the master history and it becomes worse if master history is much more than here because of different other feature branch merged back.

Git Rebase and the Git History

Well, Git Rebase is pretty much the same as Git Merge, just different commands, and the resulted final history is something like the following:

commit xxx
Author: Sigmainfy info@tech-wonderland.net
Date: Tue Apr 7 20:52:28 2015

feature-1: 3 commits ahead of original master

commit xxx
Author: Sigmainfy info@tech-wonderland.net
Date: Tue Apr 7 20:50:07 2015

feature-1: add 2 more hello

commit xxx
Author: Sigmainfy info@tech-wonderland.net
Date: Tue Apr 7 20:49:31 2015

feature-1: add 1 more hello

commit xxx
Author: Sigmainfy info@tech-wonderland.net
Date: Tue Apr 7 20:53:40 2015

master: 2 commits ahead

commit xxx
Author: Sigmainfy info@tech-wonderland.net
Date: Tue Apr 7 20:53:20 2015

master: 1 commit ahead

commit xxx
Author: Sigmainfy info@tech-wonderland.net
Date: Tue Apr 7 20:47:49 2015

master: add main.cpp hello

As we can see from the the above resulted history, git rebase actually change the history of the feature branch and achieved totally different git log history. The next section explains the reasoning and internal conceptual mechanism happened.

Conceptual Recap about Git Merge and Get Rebase

There is a very good post “Merging vs. Rebasing” explaining the basic conceptual about git merge and git rebase, the following directly quoted from the conceptual overview section from that post and I only have one comment about the “merging master into the feature branch” figure, i.e., the second figure, it would be more accurate to imagine the star node (* merge commit) as a group of 3 nodes (node1<–node2<–node3) where node1 and node 2 contains the history from master branch, because git merge operation would insert the whole master branch history after the branching point at which the feature-1 branch is created from master branch:

The first thing to understand about git rebase is that it solves the same problem as git merge. Both of these commands are designed to integrate changes from one branch into another branch—they just do it in very different ways.

Consider what happens when you start working on a new feature in a dedicated branch, then another team member updates the masterbranch with new commits. This results in a forked history, which should be familiar to anyone who has used Git as a collaboration tool.

Now, let’s say that the new commits in master are relevant to the feature that you’re working on. To incorporate the new commits into your feature branch, you have two options: merging or rebasing.

The Merge Option

The easiest option is to merge the master branch into the feature branch using something like the following:

git checkout feature
git merge master

Or, you can condense this to a one-liner:

git merge master feature

This creates a new “merge commit” in the feature branch that ties together the histories of both branches, giving you a branch structure that looks like this:

Merging is nice because it’s a non-destructive operation. The existing branches are not changed in any way. This avoids all of the potential pitfalls of rebasing (discussed below).

On the other hand, this also means that the feature branch will have an extraneous merge commit every time you need to incorporate upstream changes. If master is very active, this can pollute your feature branch’s history quite a bit. While it’s possible to mitigate this issue with advanced git log options, it can make it hard for other developers to understand the history of the project.

The Rebase Option

As an alternative to merging, you can rebase the feature branch onto master branch using the following commands:

git checkout feature
git rebase master

This moves the entire feature branch to begin on the tip of the master branch, effectively incorporating all of the new commits in master. But, instead of using a merge commit, rebasing re-writes the project history by creating brand new commits for each commit in the original branch.

The major benefit of rebasing is that you get a much cleaner project history. First, it eliminates the unnecessary merge commits required by git merge. Second, as you can see in the above diagram, rebasing also results in a perfectly linear project history—you can follow the tip of feature all the way to the beginning of the project without any forks. This makes it easier to navigate your project with commands like git loggit bisect, and gitk.

Summary

Git merge differs from git rebase by affecting git history differently, I give the complete experiment of running git merge and rebase and check the git history to show the difference so people including myself could be more convinced.

Written on April 7, 2015