Git – Everything about Merge and Rebase

This post is part of series of posts related to lesser known Git features, please visit the main post to check the series.

Everyone who has used Git would have had faced this question at some point. What’s the difference between Merge and Rebase? and if you can use either of them, which one should be preferred over another and why?

This post will explore the Git Merge and Rebase operations in detail with pros and cons that I have experienced as a developer.

Git Merge

To recap, Git merge operation creates a new commit called “merge commit” from the three-way merge if ancestors of branch being merged and branch merging into are different.

Sample Commit History

In this simplified example of commit chain (please ignore that the feature branch created from master directly, you will not do that in a real project), feature branch created when master was at “Commit 2”, while developer was working on a “commit 3”, another commit was performed in the master called “Commit 4”.

Kedar@MyMachine MINPQ55 ~/git-playground (feature1)
$ git checkout master
Switched to branch ‘master’
Your branch is up to date with ‘origin/master’.
Kedar@MyMachine MINPQ55 ~/git-playground (master)
$ git merge feature1
Merge made by the ‘recursive’ strategy.
file1.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

Here, merge operation from feature to master branch, automatically creates a new commit “commit 5” on master branch and moves the HEAD of master branch to this, this commit is called “merge commit”.

Git Rebase

Consider the above sample commit history drawing, instead of merging “Commit 3” onto “Commit 4” of master branch, you can take all the changes done in the feature branch and apply them on master branch, this git operation is called Rebase.

below example is exactly like the previous one, the only difference is that at the end, feature2 branch apply rebase onto master branch.

Important thing to note here is that, rebase command is applied onto master branch when feature2 branch is active (It did confuse me for long time)

Kedar@MyMachine MINPQ55 ~/git-playground (feature2)
$ git rebase master
First, rewinding head to replay your work on top of it…
Applying: commit 6
Kedar@MyMachine MINPQ55 ~/git-playground (feature2)
$ git checkout master
Switched to branch ‘master’
Your branch is up to date with ‘origin/master’.
Kedar@MyMachine MINPQ55 ~/git-playground (master)
$ git merge feature2
Updating 957dad0..2da27a2
Fast-forward
file2.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
Kedar@MyMachine MINPQ55 ~/git-playground (master)
$ git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 4 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 271 bytes | 271.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To https://github.com/kedarrkulkarni/git-playground.git
957dad0..2da27a2 master -> master

What’s the Difference?

Essentially, you can either use Merge or Rebase with the same result. So, what’s the difference?

The regular merge commit has two parents; which means after merge commits, the branch commit history becomes non-linear, as below

Whereas, as we simply “replay” patch of all changes from feature to master branch (in this case) in case of rebase operation, the commit history remains linear. Let’s look at the visual representation of commit history of rebase example below.

In this case, although “commit 6” was part of the feature2 branch, since we applied rebase onto master branch.

The below commit history by gitk illustrates this more accurately

Now, there are many advantages of the clean, linear commit history. It is easy to read, it’s easy to find out what commit introduced bugs in your repository, and it makes it easier to go back to specific commit in history.

However, rebase can create problems if not used correctly by everyone in the team. Let’s see below example.

Consider that you and another developer create two separate branches and “commit 6” is merged into master branch by another developer causing it to create merge commit.

Merge commit by another developer

Now, you fetch the merge remote branch into your branch to get the latest remote changes. Which means, you end up getting “Commit 7” in your commit history and perform more changes on top of it.

You merge the latest changes from master branch onto your branch

Thereafter, other developer who had initially merged his/her commit to master remote branch (in this case “Commit 7”) decide to rebase their changes instead.

The rebase operation would effectively abandon the “Commit 7” and instead apply the patch of all changes onto “Commit 5” instead

However, this will create problem for you when you try to merge “Commit 8” back onto master branch as the basis of that commit which was “Commit 7” is no longer valid.

There is a solution to this problem in the Git, but it could potentially be more time consuming to fix this situation; especially if it keeps happening again and again.

Then, Merge or Rebase?

Both merge and rebase operations are quite useful and has their pros and cons (as with everything else in the world)

Linear history and other advantages of rebasing are quite useful if you and your team understand and agree on the way to merge your work, Github also provides the option to enforce linear history so no one can simply submit merge request without rebasing their work first.

However, if your team is not “git mature” yet then allow people to use the merge commits to avoid the rebase issues that can consume bit of time to resolve.

I would be happy to know in the comments section, what your experiences have been dealing with merge and rebase 🙂