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 ismerge
, which is a 2-way diff. The better option isdiff3
. Note: and nowzdiff3
, which zealously trims away shared lines in the diff.interactive.diffFilter
anddiff.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, likehistogram
,patience
andminimal
The default ismyers
. 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.