gitgit-mergediff3

Git Merge - Difference Between conflictStyle diff3 and merge


Context

git merge considers the setting merge.conflictStyle in case of merge conflicts. Possible values are merge (default) and diff3.

I noticed that diff3 sometimes produces much bigger conflicts (see example below). I found this paper, which describes the diff3 algorithm in great detail, but I couldn't find much about the default merge algorithm.

Question

What are the exact differences between the merge and diff3 algorithm? How does the default merge algorithm work exactly?

Example

I have these files:

1
2
3
1
change1
change2
input1OnlyChange1
change3
change4
change5
change6
input1OnlyChange2
change7
change8
change9
2
3
1
change1
change2
input2OnlyChange1
change3
change4
change5
change6
input2OnlyChange2
change7
change8
change9
2
3

With merge I get 2 conflict markers:

1
change1
change2
<<<<<<< HEAD
input1OnlyChange1
=======
input2OnlyChange1
>>>>>>> input2
change3
change4
change5
change6
<<<<<<< HEAD
input1OnlyChange2
=======
input2OnlyChange2
>>>>>>> input2
change7
change8
change9
2
3

However, with diff3 I only get 1 conflict marker:

1
<<<<<<< HEAD
change1
change2
input1OnlyChange1
change3
change4
change5
change6
input1OnlyChange2
change7
change8
change9
||||||| 0fcee2c
=======
change1
change2
input2OnlyChange1
change3
change4
change5
change6
input2OnlyChange2
change7
change8
change9
>>>>>>> input2
2
3

This is my test script (powershell):

rm -Force -r ./repo -ErrorAction Ignore
mkdir ./repo
cd ./repo
git init

# git config merge.conflictStyle diff3

cp ../../base.txt content.txt
git add *; git commit -m first

git branch base

git checkout -b input2
cp ../../input2.txt content.txt
git add *; git commit -m input2

git checkout base
cp ../../input1.txt content.txt
git add *; git commit -m input1

git merge input2

Does the merge algorithm diff the diffs again to split up the bigger conflict? Clearly the merge algorithm also performs some kind of 3 way diff, as you don't get a conflict when you update base to match yours.

Official documentation

The docs say this:

Specify the style in which conflicted hunks are written out to working tree files upon merge. The default is "merge", which shows a <<<<<<< conflict marker, changes made by one side, a ======= marker, changes made by the other side, and then a >>>>>>> marker. An alternate style, "diff3", adds a ||||||| marker and the original text before the ======= marker.

Clearly this does not explain the observed difference in the example.


Solution

  • Yes, this arises particularly when both sides added something where there was nothing before, but they added different things (hence the conflict, obviously).

    Clearly this does not explain the observed difference in the example

    Actually, I think it does. In the two-part merge conflict display style, we just contrast ours against theirs, so regions of identical content are not shown as part of the conflict. But in the three-part diff3 merge conflict display style, we display the conflict by diffing ours against base and theirs against base; in a case where base is "nothing", as here, that means that both the ours display hunk and the theirs display hunk must consist of the entire inserted material.

    From a practical point of view, this makes the conflict a lot harder for a human to solve when viewed as diff3 — and in actual fact, what I do is re-diff it the other way, diffing the ours hunk against the theirs hunk to help me "spot the difference" that needs thinking about. You can swap display styles in the middle of the conflict by saying git checkout --conflict <diff3|merge> <filepath>.


    Addendum Consideration of your comments leads me to suggest you may have a possible misunderstanding here. The merge/diff3 distinction doesn't affect how the merge works or whether there is a conflict. What it affects, given that there is a conflict, is how it is displayed in the single-file markup.