Git合并-conflictStyle diff3和Merge之间的区别



上下文

CCD_ 1在合并冲突的情况下考虑设置CCD_。可能的值为merge(默认值)和diff3

我注意到diff3有时会产生更大的冲突(参见下面的示例)。我找到了这篇文章,它非常详细地描述了diff3算法,但我找不到太多关于默认merge算法的信息。

问题

mergediff3算法之间的确切区别是什么?默认的git merge0算法究竟是如何工作的?

示例

我有这些文件:

  • 底座:
1
2
3
  • 您的:
1
change1
change2
input1OnlyChange1
change3
change4
change5
change6
input1OnlyChange2
change7
change8
change9
2
3
  • 他们:
1
change1
change2
input2OnlyChange1
change3
change4
change5
change6
input2OnlyChange2
change7
change8
change9
2
3

使用merge,我得到两个冲突标记:

1
change1
change2
<<<<<<< HEAD
input1OnlyChange1
=======
input2OnlyChange1
>>>>>>> input2
change3
change4
change5
change6
<<<<<<< HEAD
input1OnlyChange2
=======
input2OnlyChange2
>>>>>>> input2
change7
change8
change9
2
3

然而,对于diff3,我只得到一个冲突标记:

1
<<<<<<< HEAD
change1
change2
input1OnlyChange1
change3
change4
change5
change6
input1OnlyChange2
change7
change8
change9
||||||| 0fcee2c
=======
change1
change2
input2OnlyChange1
change3
change4
change5
change6
input2OnlyChange2
change7
change8
change9
>>>>>>> input2
2
3

这是我的测试脚本(powershell):

rm -Force -r ./repo -ErrorAction Ignore
mkdir ./repo
cd ./repo
git init
# git config merge.conflictStyle diff3
cp ../../base.txt content.txt
git add *; git commit -m first
git branch base
git checkout -b input2
cp ../../input2.txt content.txt
git add *; git commit -m input2
git checkout base
cp ../../input1.txt content.txt
git add *; git commit -m input1
git merge input2

merge算法是否会再次区分diffs以划分更大的冲突?很明显,merge算法也执行某种3向差异,因为当更新base以匹配yours时不会发生冲突。

官方文件

医生说:

指定在合并时将冲突的大块写入工作树文件的样式。默认为";合并";,其示出了<<<<<<<冲突标记、由一侧做出的改变、=======标记、由另一侧做出的变化,然后是>>>>>>>标记。另一种风格,";diff3";,在CCD_ 21标记之前添加CCD_ 20标记和原始文本。

显然,这并不能解释示例中观察到的差异。

是的,当双方都添加了以前没有的东西,但添加了不同的东西(显然是冲突)时,就会出现这种情况。

很明显,这并不能解释在示例中观察到的差异

事实上,我认为确实如此。在由两部分组成的合并冲突显示样式中,我们只是将我们的区域与他们的区域进行对比,因此内容相同的区域不会显示为冲突的一部分。但在三部分的diff3融合冲突显示风格中,我们通过区分我们的对基和他们的对基来显示冲突;在基数为"0"的情况下;什么都没有";,在这里,这意味着我们的显示块和他们的显示块都必须由整个插入的材料组成。

从实际的角度来看,当被视为困难时,这会让人类更难解决冲突——事实上,我所做的是用另一种方式重新区分它,让我们的人和他们的人来帮助我;发现差异";这需要思考。您可以在冲突中间通过说merge.conflictStyle2来交换显示样式。


附录考虑到您的意见,我认为您可能存在误解。merge/dif3的区别不会影响合并的工作方式或是否存在冲突。考虑到存在冲突,它影响的是在单个文件标记中的显示方式。

这主要是对matt答案的补充。

记住CCD_ 23的工作原理是将某个文件的两个版本与一个共同的起始"进行比较;合并基";同一文件的版本。也就是说,我们有三个而不是两个输入,在伪代码中,我们执行以下操作:

tip1=$(git rev-parse HEAD)
tip2=$(git rev-parse "$merge_argument")
base=$(git merge-base --all $tip1 $tip2)
# make sure there's only one "base", by whatever means;
# this code is omitted as it's complicated.
git show $base:$path > tmp.base
git show $tip1:$path > tmp.tip1
git show $tip2:$path > tmp.tip2
diff tmp.base tmp.tip1   # figure out what "we" changed in --ours
diff tmp.base tmp.tip2   # figure out what they changed in --theirs
# combine the changes (code not shown)
# apply the combined changes to tmp.base
# put tmp.base into place as the merge result, perhaps with conflicts

在所有情况下,git merge都检测到冲突,因为两个diff产生了重叠或邻接(但不完全相同)的diff块。

使用diff3设置,Git:

  • 将tip1内容放在<<<<<<< HEAD之后的块顶部
  • |||||||ID之后,将整个冲突范围中的原始文件(tmp.base)内容置于中间
  • 添加=======
  • 将tip2内容放在>>>>>>>ID之前的块底部

(所有这些都位于文件中未冲突解决的部分的中间)。两个ID是散列ID或字符串(Git插入字符串代替散列ID的方法特别令人讨厌,但这是一个无关的实现细节,除非你想手动运行git-merge-recursivegit-merge-ort"原始"/")。

然而,在merge冲突样式中,Git接受冲突部分,尽其所能合并常见部分。这导致CCD_;向下";而CCD_ 35线";向上";。它甚至可以将冲突拆分为多个较小的冲突,每个冲突都有自己的单独标记。这使得合并冲突";看起来更小";,人类经常觉得这很有帮助(尽管我个人有时也觉得很困惑!)。

正如马特在评论中提到的,有一个新的选项zdiff3,首次出现在Git 2.35中(请参阅commit4496526f80b3e4952036550b279eff8d1babd60a。这里的z代表"热心":diff3的这个变体试图进行与普通merge相同的合并冲突收缩,但不拆分冲突。也就是说,它只会在相同的更改之间"上移"one_answers"下移",收缩合并基部分,但将其保留为单个块。如前所述,常规的merge样式可以将其分解为多个部分,如果这会进一步缩小冲突的话;zdif3不会。

提交消息中有一个很好的例子,所以点击GitHub上的提交,看看zdiff3如何呈现给定的冲突。

最新更新