Skip to content

Git Merge Conflicts

Git Diff Highlighting

Git now comes with a useful diff highlighter that highlights both the line and word level changes to files.

When using git add -p the specified script in the interactive.diffFilter variable is called to show differences. This can be set to diff-highlight, so that the output looks the same as git diff.

However, there is a better option when diffing prose, or long, space-less code that is mistakenly interpreted as one word: git diff --color-words .

Unfortunately, this doesn’t work well with the interactive filter. A better solution is to install the diffr script (e.g. brew install diffr) and set the interactive.diffFilter to this.

I have 3 way diffs configured in gitconfig:

[merge]
  conflictstyle = diff3

Clarification

Just to be clear, there is two different things here:

  • merge.conflictstyle is how the diff is displayed within a conflicted file: how a file with merge conflicts in it is annotated. The default is merge, which is a 2-way diff. The better option is diff3. Note: and now zdiff3, which zealously trims away shared lines in the diff.
  • interactive.diffFilter and diff.algorithm are how the differences between two files are detected and what parts of a file are deemed to have changed. There are lots of options here, like histogram, patience and minimal The default is myers. If you have ever wondered why git has put that curly bracket up there in your diff, that makes no sense at all, it’s because of the intricacies of the diff algorithm used.

What the file annotations mean

Git nomenclature is pretty poor for this.

Given

[~/blog/branch_a]> git merge branch_b

The current branch, branch_a is known as the target branch. The branch that is to be merged, branch_b is known as the merge branch.

For a two-way diff,:

<<<<< branch_a # target branch: current content of file
file contents
=======
other file contents=
>>>>> branch_b # merge branch: content that needs to be applied

rebase:
original 1
original 2
original 3
<<<<<<< HEAD # the change on the target branch already applied
changed on master 4
changed on master 5
changed on master 6
=======
changed on branch_b 4
changed on branch_b 5
changed on branch_b 6
>>>>>>> 2710110 (Changed on branch_b) # the change on the merge branch you are trying to add
original 7
original 8

For a diff3 style, you have an extra section telling you the shared common ancestor to the conflicting commits. Here is what the three sections are telling you:

original 1
original 2
original 3
<<<<<<< HEAD   # the change on the target branch already applied
changed on master 4
changed on master 5
changed on master 6
||||||| parent of 2710110 (Changed on branch_b)  # the orignal file, the shared common ancestor
original 4
original 5
original 6
=======
changed on branch_b 4
changed on branch_b 5
changed on branch_b 6
>>>>>>> 2710110 (Changed on branch_b)  # the change on the merge branch you are trying to add
original 7
original 8

Vim has a useful plugin - vim-fugitive for dealing with all sorts of git interactions, including a useful difftool mode for managing conflicts. It’s a very effective way of dealing with complicated marge conflicts. I’ve blogged about it: Vim-Fugitive.