Why does a seemingly possible merge using Git have conflicts?
In short, why does a file
foo.txt having content
failed to merge with the branch with
foo.txt having content:
The longer version is: to experiment with Git and merging, I did the following:
mkdira new directory and
cdinto it and
- create a file
foo.txtand add the line
a(first time just like that, and second time with 2 empty lines after
- commit it, and now
git checkout -b featureand
git checkout -b sprint(so as far as I know, it makes no difference to do it in a row instead of first switching back to
sprint, because branches are created based on commitID, and either case, they are exactly the same.
git co featureand make the file look like
aand then empty line and then
con the third line
- Now commit it, and do
git checkout sprintand make the file look like
aand then next line
bon its own.
- Now commit it, and now do a
git merge feature
and supposedly, the merge could have succeeded, with the content
a b c
but why did it fail? Instead of guessing “maybe the lines are too close”, can a more definitive answer from docs or reference be given?
I also tried adding an empty line between
b, and between
c so they are more spaced out, but without the initial empty lines in the first version of
foo.txt, and the result was the same: merge conflict.
4 Solutions collect form web for “Why does a seemingly possible merge using Git have conflicts?”
Git checks for differences on a line level. So lets say your fist version of the file looks like this:
Now you create a second version of the file on a branch named
In this version you have changed two lines. Why two lines do you think? Well there was a newline character added
\n at the end of
In the branch
sprint you added another version of the file:
Now you modified three lines.
If you now try to merge the two modifications git recognizes that you changed line 1 and 2 in both commits, so git doesn’t know which version to take and a conflict occurs.
As you posit, they’re too close. Let’s use a common-ancestor that doesn’t have empty lines to avoid confusion. If you have some common ancestor:
line 1 line 2 line 3 line 4
And you edit both sides:
line 1 LINE TWO line 3 line 4
line 1 line 2 LINE THREE line 4
Then the git diff/merge engine (xdiff) will produce a conflict. Though I could not find documentation on this, it can obviously be seen empirically. This is an implementation detail, though a common one.
If you think of the automerge as describing changed regions, described in relation to other regions (instead of lines, addressed by numbers) this might make more sense: the first side changes data after
line 1 and before
line 3. The second side changes data after
line 2 and before
When you try to correlate these changes, though, you run into trouble: the first side has removed the context (
line 3) that the second side relies on to describe its changes.
As long as there’s some context (ie, the changes are not “too close”) this should work, even if that context is a single line. To extend your example to where you are not changing immediately adjacent lines, if your common ancestor is:
And one side is:
a b d
The other side is:
a c d
Then this will automerge successfully as:
a b c d
The merge resulted in conflicts because on each of your branches you modified the file in almost the same way. Git could attempt to resolve the conflict itself but because any decision that it makes could be wrong, it is going to make you do it.
Git doesn’t look at what was changed for each line. Only that a line was changed or added. In one branch you added a new line and on the other you added a character and a new line. In both branches you are adding a line immediately after the first and git wants you to determine which line should be in the file upon merge.
You would not have had a conflict because git would have seen independent changes in the file on each branch. But because there were changes on the same line in each branch git has a conflict and asks for your resolution.
Basically to git I believe every merge that has different histories is a conflict. But it will try to resolve as much it can by it’s own. It will try to apply(using
git apply) every change from the diff to the files of the target branch, but when the apply fails, it won’t do anything anymore and it will let the programmer resolve it.
In your files, in branch
feature, you have changed
a\n\nc and in branch
sprint you have changed
a\nb\nc. To git by default empty lines and white space changes are significant. It cannot decide which change to apply, because they are both different and conflicting.