来自差异排序差异的异常合并冲突?

时间:2021-03-08 16:26:49

标签: git git-merge git-diff

我在 Git 中有一个异常的合并冲突。这是一个我从未打算保留的测试合并,但我想了解 为什么 Git 将其报告为冲突。

我已使用十六进制编辑器在合并两侧获取受影响文件的 SHA-1 哈希值,并由此确认文件在合并两侧字节方向相同至少 7 行之前到至少 28 行之后,假定的冲突。这意味着实际上不存在冲突,并且不可能是空格、行尾、奇怪的 Unicode 规范化或不显示字符的问题。

冲突看起来是这样的:

<<<<<<< HEAD



#### Where To Put Untracked Intermediate Files

=======



#### Where To Put Untracked Intermediate Files

>>>>>>> theirs

根据@torek 的建议,我也尝试过使用 merge.conflictstyle=diff3,这会产生不同的标记,但本质上没有冲突:

<<<<<<< HEAD
This is a preceding big long line, added by both sides, simplified for this post.
||||||| 87377e6
=======
This is a preceding big long line, added by both sides, simplified for this post.

>>>>>>> theirs


<<<<<<< HEAD
||||||| 87377e6
#### Where To Put Intermediate Files
=======
#### Where To Put Untracked Intermediate Files
>>>>>>> theirs

<<<<<<< HEAD
#### Where To Put Untracked Intermediate Files

This is an edited big long line which I have simplified for this post.
||||||| 87377e6
This is a big long line which I have simplified for this post.
=======
This is an edited big long line which I have simplified for this post.
>>>>>>> theirs

(我为这篇文章更改了一些行,但这样不相同的行就保持不相同。)

仔细检查发现,在三个冲突之间,上面的HEADtheirs等同于同一行。在这种情况下,冲突标记的排列与我稍后注意到的 git-diff 行顺序差异一致,但其他方面没有任何意义。

在新存储库中复制

以下是复制它的过程,从新存储库的空目录中从头开始,手头有 base.txtours.txttheirs.txt——稍后会详细介绍:

$ git init
Initialized empty Git repository in //bla/bla/bla/bla/bla/repo/.git/

$ cp ../test-files/base.txt .
$ mv base.txt file.txt
$ git add file.txt
$ git commit -m "Set base"
[main (root-commit) 30f2d24] Set base
 1 file changed, 2169 insertions(+)
 create mode 100644 file.txt

$ git checkout -b theirs
Switched to a new branch 'theirs'

$ rm file.txt
$ cp ../test-files/theirs.txt .
$ mv theirs.txt file.txt
$ git commit -am "Set theirs"
[theirs d5abe2c] Set theirs
 1 file changed, 81 insertions(+), 46 deletions(-)

$ git switch main
Switched to branch 'main'

$ rm file.txt
$ cp ../test-files/ours.txt .
$ mv ours.txt file.txt
$ git commit -am "Set ours"
[main a7cde04] Set ours
 1 file changed, 217 insertions(+), 49 deletions(-)

$ git merge theirs --no-ff
Auto-merging file.txt
CONFLICT (content): Merge conflict in file.txt
Automatic merge failed; fix conflicts and then commit the result.

此时 file.txt 中有两个地方带有冲突标记(使用 merge.conflictstyle=diff3 时更多)。第一个位于第 408 行,如帖子顶部所示。第二个在第 2353 行的文件末尾附近,并且有合理的解释。

调查

我已经使用 gitk 确认所有提交中的文件模式都是 100644

为了试图解释发生了什么,在上述过程之后,我运行了这些:

git diff :1:file.txt :2:file.txt > 1-2.txt
git diff :1:file.txt :3:file.txt > 1-3.txt
git diff :2:file.txt :3:file.txt > 2-3.txt

文件 2-3.txt 不包含字符串 Where To Put Untracked Intermediate Files

1-2.txt1-3.txt do 包含字符串,分别作为 @@ -372,34 +389,42 @@@@ -372,34 +388,42 @@ 块的一部分。请注意,这些大块头比报告为冲突的区域要长得多。

我在十六进制编辑器中打开 ours.txttheirs.txt,仔细定位这些大块头的起点和终点,并获取它们的 SHA-1。 哈希值相同。

不过,我确实找到了一个可能的线索:

git-diff 行顺序差异

大块头的 a/b/ 文件在 1-2.txt1-3.txt 之间显示相同的内容,但它们的方式 在 diff 输出中呈现的并不完全相同。尽管它们的顺序在功能上是相同的,但有 7 行的跨度没有以相同的顺序列出:

对于 1-2.txt 这些行,加上以下为清楚起见,如下所示:

 
 
-#### Where To Put Intermediate Files
 
-This is a big long line which I have simplified for this post.
+#### Where To Put Untracked Intermediate Files
+
+This is an edited big long line which I have simplified for this post.

对于 1-3.txt,它们如下:

+
 
 
-#### Where To Put Intermediate Files
+#### Where To Put Untracked Intermediate Files
 
-This is a big long line which I have simplified for this post.
+This is an edited big long line which I have simplified for this post.

1-2.txt1-3.txt2-3.txt 文件在运行和不运行 merge.conflictstyle=diff3 时是相同的。

问题

  • 为什么这些行的顺序不同?
  • git diff 是否承诺将使用一组功能等效的排序中的哪一行排序?
  • git merge 是否假定如果 git diff文字文本输出对于一个大块头不相同,那么大块头是一个冲突?
  • 如果没有,为什么将其显示为冲突?
  • Git 用户如何避免这种情况?

文件

base.txtours.txttheirs.txt 在我的原始测试中是密切相关的 ~2300 行 Markdown 文件。

我最初在一个实际的存储库中遇到了这个冲突,在那里重复了两个分支之间的挑选和合并。我在不打算提交的情况下运行 git merge --squash 时遇到了它,以查看分支收敛了多少。起初我认为冲突是由于挑选樱桃(它可以做这样有趣的事情),但我越想越没有意义,因为当双方更改文件时同理,这不应该是冲突。

因此,在 merge 抱怨之后,在做出任何解决方案之前,我运行了这些:

git show :1:the-actual-file.md > base.txt
git show :2:the-actual-file.md > ours.txt
git show :3:the-actual-file.md > theirs.txt

在新的存储库中重现合并非常有效地排除了诸如挑选樱桃之类的复杂性作为解释。

ours.txttheirs.txt 有许多不冲突的变化,我无法仅根据异常冲突在一个微不足道的例子上重现。但是,我能够在三个文件的“清理”版本上重现,其中所有行都被替换为它们的 SHA‑1 十六进制字符串,这会产生相同的冲突模式和 git diff如上图所示。有兴趣的可以下载:base.txtours.txttheirs.txt

0 个答案:

没有答案
相关问题