简体   繁体   English

使用“merge.conflictstyle diff3”时,如何使 git 合并产生更细粒度的冲突?

[英]How can I make git merge produce more granular conflicts when using `merge.conflictstyle diff3`?

I have a problem with an, in my opinion, unintuitive merge behavior of git (version 2.31.1).在我看来,我对 git(版本 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.现在我有一个空文件和两个分支mainfeature的提交,指向该提交。 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接下来,我 go 到另一个分支

git checkout feature

change file1 there tofile1更改为

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 .现在我切换回main分支并将feature合并到main中。

git checkout main
git config merge.conflictstyle diff3
git merge feature

As expected, there is a conflict in file1 .正如预期的那样, 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.如您所见,git 就像整个文件是一个大冲突。 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, ie git diff main:file1 feature:file1 .我目前对这种类型的合并的解决方案是通过对两个分支之间的文件进行差异来手动查找更改,即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?如果没有,有什么好的理由说明这是不可能的吗?

tl;dr diff3 wants to see all three files in the merge: the two branches and their common ancestor. tl;dr diff3希望查看合并中的所有三个文件:两个分支及其共同祖先。 More aggressive stages of Git's conflict reduction can strip out the ancestor defeating the point of a diff3 conflict marker. Git 冲突减少的更积极的阶段可以去除祖先击败 diff3 冲突标记的点。


Git will normally refine its conflicts, as you say.正如您所说,Git 通常会优化其冲突。 UNLESS you're using merge.conflictstyle diff3 .除非您使用的是merge.conflictstyle diff3

    if (style == XDL_MERGE_DIFF3) {
        /*
         * "diff3 -m" output does not make sense for anything
         * more aggressive than XDL_MERGE_EAGER.
         */
        if (XDL_MERGE_EAGER < level)
            level = XDL_MERGE_EAGER;
    }

level determines how aggressively Git refines the conflicts. level决定了 Git 改进冲突的积极程度。 If you're using diff3 it doesn't try very hard.如果您使用的是diff3 ,它不会很努力。 Why?为什么? The original commit explains in excruciating detail. 原始提交以令人痛苦的细节解释。 . . Here is the summary as best I understand it.这是我所理解的摘要。

Let's say you have a file like so.假设你有一个这样的文件。

1
2
3
4
5
6
7
8
9

And two branches both replace lines 5 and 6.两个分支都替换了第 5 行和第 6 行。

# branch1
1
2
3
4
A
B
C
D
E
7
8
9
# branch2
1
2
3
4
A
X
Y
E
7
8
9

The merge looks like this.合并看起来像这样。

     branch1:     1234ABCDE789
                     |    /
                     |   /
     ancestor:    123456789
                     |   \
     branch2:     1234AXYE789

With normal 2-way conflict markers, Git would notice they both start with A and end with E and clip that out.对于普通的 2 路冲突标记,Git 会注意到它们都以 A 开头并以 E 结尾并将其剪掉。

  A
++<<<<<<< HEAD
 +B
 +C
 +D
++=======
+ X
+ Y
++>>>>>>> feature
  E

This leaves out the ancestor from the conflict marker.这从冲突标记中排除了祖先。 This is fine for 2-way conflict markers, they only care about the two changed files.这对于 2-way 冲突标记很好,它们只关心两个更改的文件。 But you asked to see the conflict between between all three files, so that's what you get.但是您要求查看所有三个文件之间的冲突,这就是您得到的。

++<<<<<<< HEAD
 +A
 +B
 +C
 +D
 +E
++||||||| f8809d8
++5
++6
++=======
+ A
+ X
+ Y
+ E
++>>>>>>> feature

The extra conflict cleanup is incompatible with requesting diff3 style conflict markers.额外的冲突清理与请求 diff3 样式冲突标记不兼容。

Git 2.35.0 will have a new conflict style that behaves exactly as desired here called zdiff3 . Git 2.35.0 将具有一种新的冲突样式,其行为与此处所期望的完全一样,称为zdiff3 It can be configured with git config merge.conflictstyle zdiff3 .它可以配置为git config merge.conflictstyle zdiff3 Add --global to configure the conflict style globally.添加--global以全局配置冲突样式。

I tested it with git version 2.35.0.rc0 and it produces the following result.我用 git 版本2.35.0.rc0对其进行了测试,它产生了以下结果。

line1
line2
<<<<<<< HEAD
line3
||||||| 59df1b4
=======
line
>>>>>>> feature
line4
line5

Perfect!完美的!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM