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.
What are the exact differences between the merge
and diff3
algorithm?
How does the default merge
algorithm work exactly?
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
.
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.
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.