Home > Net >  How can I make git merge produce more granular conflicts when using `merge.conflictstyle diff3`?
How can I make git merge produce more granular conflicts when using `merge.conflictstyle diff3`?

Time:12-16

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.

  • Related