Reducing merge headaches: git meets diff3

git has an option to display merge conflicts in diff3 format (by default it only displays the two files to be merged). You can enable it like so:

$ git config --global merge.conflictstyle diff3

Now, when you have to resolve merge conflicts, git shows your side, the side being merged, and (here's what's new) the common ancestor in between them. Here's an example of the diff3-formatted output:

cauliflower
<<<<<<< HEAD
peas
potatoes
||||||| merged common ancestors
peas
=======
>>>>>>> topic
tomatoes

Having the merge ancestor readily available helps you to quickly determine what the correct merge is, since you can infer from it the changes that were made on both sides. Here you can see that the original state was peas. On your branch potatoes was added (compare the middle section to the top) and on the other branch peas was removed (compare the middle section to the bottom). Therefore the correct change is to both add potatoes and remove peas, leaving you with just potatoes in the conflicted section.

There's really no reason you shouldn't enable the diff3 style, because you frequently need the ancestor to determine what the correct merge is.

To see that this is true, even in the simple example above, look at what the conflict looks like under the standard style:

cauliflower
<<<<<<< HEAD
peas
potatoes
=======
>>>>>>> topic
tomatoes

There's an asymmetry between peas and potatoes: one was added and one was deleted, but this merge conflict doesn't tell you anything at all about which was which! You can't determine the correct merge unless you remember the sequence of changes that led up to this point. And why should you have to rack your brain to do that? That's exactly the sort of thing that your computer can, and should, help you with.

Bonus tip: rerere (reuse recorded resolution)

If your workflow finds you redoing the same merges over and over again you might also find git's rerere (reuse recorded resolution) feature to be useful.

One of the things that is wonderful about rerere is that it provides hardly any UI surface at all. Just set it...

$ git config --global rerere.enabled 1

...and forget it. Although there is a git rerere command, you can get a lot done without using it at all.

After enabling rerere, whenever you resolve a merge conflict, git automatically squirrels away the resolution in its database. You'll see a message like this one:

$ git commit
Recorded resolution for 'soup'
[...]

And the next time you encounter the same conflict, where you would have expected git to spit out a file with conflict markers, you will instead find that it has automatically resolved the merge for you, and printed the following message:

$ git merge topic
Auto-merging soup
CONFLICT (content): Merge conflict in soup
Resolved 'soup' using previous resolution.

Just double-check to make sure nothing has gone awry, add, and commit. Save your blood, sweat, and tears for other, more interesting problems than redoing merges.

Further reading: Pro Git on rerere

7 comments:

  1. Love the diff3 format. I can't see why anyone would not want to use it. Thanks for posting!

    ReplyDelete
  2. I also love diff3. Why the heck is that not the default?

    Thanks for the tip on rerere. It sounds too good to be true.

    ReplyDelete
  3. Awesome! I did not know about the diff3 option, yet always wanted it! Can't believe I didn't look for it. Thanks for writing this up!

    ReplyDelete
  4. My two cents on why it's not the default is because it's not clear at first how it works. I mean, what "common ancestor" represents. I needed this blog to understand the concept. Thanks for that.

    ReplyDelete
  5. Look at style #3 that BitKeeper could use:
    https://www.bitkeeper.org/man/smerge.html

    That requires a bit more editing, but the information is _much_ clearer.

    ReplyDelete
  6. I really fell happy to read this post thanks for sharing this very good
    THC Diamonds

    ReplyDelete