Git merge vs rebase: Resolving conflict

There are lots of blogs and examples on the Internet explaining and comparing git merge and rebase. In this post I would like to take a closer look at what is actually happening with both methods when there is a conflict that has to be resolved.

Setting up a project with conflict

To make things easier and a little bit funny, let’s imagine that Jack and Jane are remotely working together on an assignment to gather facts about Austria, and they created a git project consisting of only one file Austria.txt which lists facts about Austria.

Initial commit includes some facts they managed to quickly google about the country:

jack$ cat Austria.txt
What we know about Austria?
  - Austria is officially called the Republic of Austria.
  - German is the official language of Austria.
  - Austria has plenty of kangaroo!!!

Then Jack creates a feature branch where he adds some more facts and make three commits:

jack$ git log --oneline
136c07e Add fact about Schwarzenegger
ce3df1f fix kangaroo issue                  <- Fixed by Jack
9ccbce8 add capital info
c6d03c8 Initial commit

Note that Jack fixed mistakenly googled fact about kangaroo in Austria (originated by George W. Bush).

In the meantime, while Jack was working hard gathering more facts, Jane also spotted the “kangaroo slip” and quickly fixed it in master:

jane$ git log --oneline
3d20714 Fixed kangaroo fact                 <- Fixed by Jane
c6d03c8 Initial commit

Now the issue is fixed in both master and feature branch, and Jack have to apply his work to master.

jack$ git log --oneline --graph --all
* 3d20714 Fixed kangaroo fact               <- Fixed by Jane
| * 136c07e Add fact about Schwarzenegger
| * ce3df1f fix kangaroo issue              <- Fixed by Jack
| * 9ccbce8 add capital info
|/
* c6d03c8 Initial commit

Should Jack do merge or rebase?

The source code with two unmerged branches can be cloned from github.

Trying merge

jack$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
jack$ git merge fb-jack
Auto-merging Austria.txt
CONFLICT (content): Merge conflict in Austria.txt
Automatic merge failed; fix conflicts and then commit the result.

As expected, automatic merge failed. Let’s have a look at the conflict.

jack$ cat Austria.txt
What we know about Austria?
  - Austria is officially called the Republic of Austria.
  - German is the official language of Austria and is spoken by over 88% of the population.
  - There are NO kangaroos in Austria!!!
<<<<<<< HEAD
=======
  - Vienna is the capital and largest city of Austria.
  - Arnold Schwarzenegger was born and raised in Austria before moving to America.
>>>>>>> fb-jack

Even though both Jack and Jane fixed “kangaroo line” with exactly the same wording, merge “didn’t get it”. No blame, merge algorithm tries to be on a safe side. Resolution is easy, Jack needs to remove 3 pesky lines

<<<<<<< HEAD
=======
>>>>>>> fb-jack

and complete merge with

jack$ git add .
jack$ git commit -m "merge fb-jack"
[master 7a1f484] merge fb-jack

All done. The final list looks like

jack$ cat Austria.txt
What we know about Austria?
  - Austria is officially called the Republic of Austria.
  - German is the official language of Austria and is spoken by over 88% of the population.
  - There are NO kangaroos in Austria!!!
  - Vienna is the capital and largest city of Austria.
  - Arnold Schwarzenegger was born and raised in Austria before moving to America.

Trying rebase

jack$ git checkout fb-jack
Your branch is up-to-date with 'remotes/origin/fb-jack'.
jack$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: add capital info
Using index info to reconstruct a base tree...
M	Austria.txt
Falling back to patching base and 3-way merge...
Auto-merging Austria.txt
CONFLICT (content): Merge conflict in Austria.txt
Failed to merge in the changes.
Patch failed at 0001 add capital info
The copy of the patch that failed is found in:
   /Users/jack/experiment/rebase/.git/rebase-apply/patch

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".

Again, as expected we got a merge conflict. Let’s have a look at it.

jack$ cat Austria.txt
What we know about Austria?
  - Austria is officially called the Republic of Austria.
  - German is the official language of Austria and is spoken by over 88% of the population.
<<<<<<< 3d20714684b59305aee60cc1241b5da64e4f4cb1
  - There are NO kangaroos in Austria!!!
=======
  - Austria has plenty of kangaroos!!!
  - Vienna is the capital and largest city of Austria.
>>>>>>> add capital info

Jack sees that Jane also fixed “kangaroo issue”. To resolve conflict, Jack needs to remove 3 pesky lines (similar to what he would have to do if he preferred merge)

<<<<<<< 3d20714684b59305aee60cc1241b5da64e4f4cb1
=======
>>>>>>> add capital info

and continue rebase.

jack$ git add .
jack$ git rebase --continue
Applying: add capital info
Applying: fix kangaroo issue
Using index info to reconstruct a base tree...
M	Austria.txt
Falling back to patching base and 3-way merge...
Auto-merging Austria.txt
CONFLICT (content): Merge conflict in Austria.txt
Failed to merge in the changes.
Patch failed at 0002 fix kangaroo issue
The copy of the patch that failed is found in:
   /Users/jack/experiment/rebase/.git/rebase-apply/patch

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".

Conflict again?? Let’s have a look at it:

jack$ cat Austria.txt
What we know about Austria?
  - Austria is officially called the Republic of Austria.
  - German is the official language of Austria and is spoken by over 88% of the population.
  - There are NO kangaroos in Austria!!!
<<<<<<< 5697ea812bb43ead367b779e695d5ba750eb83ed
  - Austria has plenty of kangaroos!!!
=======
>>>>>>> fix kangaroo issue
  - Vienna is the capital and largest city of Austria.

After fixing the conflict by removing lines below

<<<<<<< 5697ea812bb43ead367b779e695d5ba750eb83ed
  - Austria has plenty of kangaroos!!!
=======
>>>>>>> fix kangaroo issue

Jack completes rebase

jack$ git add .
jack$ git rebase --continue
Applying: fix kangaroo issue
Applying: Add fact about Schwarzenegger

and finishes the job with fast-forward merge of master with feature branch.

jack$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
jack$ git merge fb-jack
Updating 3d20714..4e20488
Fast-forward
 Austria.txt | 2 ++
 1 file changed, 2 insertions(+)

Now it’s all done. The final list is the same as after merge.

Merge vs rebase

In our special case, merge required only one trivial conflict resolution while rebase required two, not as trivial.

So should we prefer merge to rebase in this case? Personally, I wouldn’t. Even though rebase caused a little bit more pain, the effort still was quit trivial. And as a benefit, rebase would give me a clean history that goes with simplified release management.

Should we always prefer rebase to merge? It depends on rebase complexity. In case of a really complex rebase, a simpler merge could be a better choice.

About the case

To be honest, I should mention that this case was artificially complicated. Lines added after the erroneous “kangaroo” line caused merge and rebase raise conflicts. If our example were

What we know about Austria?
  - Austria is officially called the Republic of Austria.
  - Austria has plenty of kangaroo!!!                      <-- Error
  - German is the official language of Austria.

then both merge and rebase would be accomplished automatically.

However, from time to time, such cases happen in practice.