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.

comments powered by Disqus