I have a problem with an, in my opinion, unintuitive merge behavior of git (version 2.31.1). Let me demonstrate with a small toy repo.
git init
touch file1
git add file1
git commit -m "Initial commit"
git branch feature
Now I have a commit with an empty file and two branches, main
and feature
, pointing to that commit. Now I change the file in the main branch to
line1
line2
line3
line4
line5
and commit the change with
git add file1
git commit -m "Change file1"
Next, I go to the other branch
git checkout feature
change file1
there to
line1
line2
line
line4
line5
and again commit the change with
git add file1
git commit -m "Change file1"
The resulting commit tree looks like this:
* b1beb63 - Change file1 (HEAD -> feature)
| * 32ea83d - Change file1 (main)
|/
* 2952256 - Initial commit
Now I switch back to the main
branch and merge feature
into main
.
git checkout main
git config merge.conflictstyle diff3
git merge feature
As expected, there is a conflict in file1
. However, the conflict looks like this:
<<<<<<< HEAD
line1
line2
line3
line4
line5
||||||| 2952256
=======
line1
line2
line
line4
line5
>>>>>>> feature
As you can see, git acts like the entire file is one large conflict. I would have expected it to look like this:
line1
line2
<<<<<<< HEAD
line3
||||||| 2952256
=======
line
>>>>>>> feature
line4
line5
which would be infinitely more user-friendly.
I found some threads where people had similar problems because of different line endings. This is not the case here as I edited the file in both branches on the same platform with the same editor.
My current solution for this type of merge is to find changes manually by doing a diff of the files between the two branches, i.e. git diff main:file1 feature:file1
. This works, but is rather annoying.
Is there any way to get the behavior I would expect in this merge? If not, is there any good reason why this is not possible?
CodePudding user response:
tl;dr Your expected behavior is an invalid diff3 representation of the conflict. You can't have both.
The original commit explains in excruciating detail. I'll try to summarize.
Let's look at your case. You started with this.
# ancestor
# main
line1
line2
line3
line4
line5
# feature
line1
line2
line
line4
line5
You asked for a diff between all three files, and that's what you got.
<<<<<<< HEAD
line1
line2
line3
line4
line5
||||||| 2952256
=======
line1
line2
line
line4
line5
>>>>>>> feature
The original file is blank. HEAD changed it to added five lines. feature added five lines. That's a correct representation of the 3-way conflict.
Let's look at what you expect to see...
line1
line2
<<<<<<< HEAD
line3
||||||| 2952256
=======
line
>>>>>>> feature
line4
line5
This says the original file contained 4 lines. HEAD and feature added a line between line2 and line4.
In other words, it says you merged these files.
# ancestor
line1
line2
line4
line5
# main
line1
line2
line3
line4
line5
# feature
line1
line2
line
line4
line5
That is not true, the ancestor was blank.
The reduction you want is fine for 2-way conflict markers, but would defeat the point of 3-way conflict markers.