这些 Git 合并标记的简单解释是什么?

aut*_*ech 5 git git-merge

下面参考代码段1、2、3解释Git合并标记的含义。

/* Code from beginning of file */

<<<<<<< HEAD
      /* code segment 1 */
||||||| merged common ancestors
      /* code segment 2 */
=======
      /* code segment 3 */
>>>>>>> master

/* code to end of file */
Run Code Online (Sandbox Code Playgroud)

这个问题旨在引出一个简单的解释,而不参考其他问题中发现的复杂因素。

tor*_*rek 10

这些 V 形标记(<<<<<<<等等)是冲突标记。这个特定的集合(包括|||||||标记)来自diff3冲突风格。默认merge冲突样式省略中间部分。

\n\n

第一部分中的各节,<<<<<<<|||||||,来自当前提交。第二部分中的部分,|||||||=======,来自合并基础(我们稍后将定义)。第三部分中的各个部分=======(到 )>>>>>>>来自其他提交。

\n\n

更长但更有用

\n\n

要理解这些含义,请记住任何git merge操作都不是两个而是三个输入。三个输入之一是您当前的结账,也称为HEAD@提交。通常,这是您签出的分支中的最新提交。第二个输入基于git merge您运行的命令。1 例如,如果您运行:

\n\n
git merge theirbranch\n
Run Code Online (Sandbox Code Playgroud)\n\n

那么第二个输入是分支顶端的提交theirbranch;如果你跑:

\n\n
git merge origin/master\n
Run Code Online (Sandbox Code Playgroud)\n\n

那么第二个输入是您指向的提交origin/master。无论哪种方式,这两个都是充满文件的 commits\xe2\x80\x94snapshots,其中您HEAD提交中的文件与其提交中的文件相同或不同。不过,您的文件与其文件之间的差异并不直接相关:关键提交是第三次提交,称为合并基础

\n\n

Git 自动为您找到合并基础提交。实际上,合并基础是位于其他两个提交之前的最佳共享提交。请记住,合并的目标是合并工作,为此,Git 需要找出您更改的内容以及它们更改的内容。但每个提交都是一个快照,而不是一组更改\xe2\x80\x94,因此 Git 必须从您的提交及其提交向后工作,以找到你们在开始时共享的提交。这就是合并基础。

\n\n

找到合并基础后,Git 现在执行两个差异。将合并基础与当前提交进行比较:这就是更改的内容。第二个差异将合并基础与他们的提交进行比较:这就是他们更改的内容。然后 Git 合并两组更改。组合的更改将应用​​于合并基础,以给出最终的合并结果。

\n\n

当您更改文件并且他们根本不触及该文件时,将您的更改与他们的无内容相结合意味着结果就是您的更改。将它们应用到基本文件会生成您的文件。同样,当他们更改文件而您没有更改时,将您的任何内容与他们的更改相结合意味着结果是他们的更改,并将这些更改应用于基本文件会生成他们的文件。所以这些都很容易。

\n\n

当你和他们都更改同一个文件时,困难的部分就会出现。现在 Git 确实必须结合不同的更改。如果您的更改是针对他们未触及的行,而他们的更改是针对您未触及的行,Git 可以合并这些行:它只需要这两项更改。如果您更改了某些行并且他们进行了完全相同的更改,Git 也可以将其合并:它只需要更改的一个副本。真正困难的部分会导致合并冲突,当您更改某些行,而它们以不同的方式更改相同的行时,就会发生这种情况。

\n\n

对于这种情况,Git 将冲突的更改写入文件的工作树副本,并由这些冲突标记包围。冲突标记区域上方的部分已经成功组合\xe2\x80\x94或者至少,Git认为它已经\xe2\x80\x94,标记区域下方的部分也是如此。中间的部分是 Git 无法决定是在第一段中选择您的更改,还是在最后一段中选择他们的更改。仅当您选择样式时才显示的中间部分diff3是原始线条的样子。

\n\n
\n\n

1请注意,如果你跑到git pull这一步,那就为你git pull跑。git merge所以你可能没有git merge直接运行,但你确实调用了git merge

\n\n

cherry-pick 和 revert 命令也使用 Git 合并机制。或git stash的某些情况也是如此。因此您也可以看到这些命令的合并冲突。不过,这些操作的合并基础的定义是不同的,因此很难看出冲突是如何产生的。git applygit am

\n\n

diff3 与 merge 的另一个副作用

\n\n

当发生冲突时,如果您diff3选择了样式,Git 必须向您显示基本版本\xe2\x80\x94 发生冲突的整个部分。但是,当您merge选择样式后,Git 可以省略基本版本,而仅显示--ours--theirs版本。这意味着,如果它可以部分合并冲突区域,它就会这样做,只留下被标记包围的未合并区域。例如:

\n\n
<<<<<<< HEAD\nplease fix a spelling error\nand ok, I changed this\n||||||| merged common ancestors\nplease fix a speeling error\nand change this\n=======\nplease fix a spelling error\nand change this to something different\n>>>>>>> theirs\n
Run Code Online (Sandbox Code Playgroud)\n\n

在这里,我们和他们以相同的方式修复了拼写错误(替换speeling为),但我们以不同的spelling方式更改了第二行。使用 diff3 样式,您可以看到基本版本和两个端点版本。

\n\n

如果我们选择样式merge,Git 会看到我们和他们以相同的方式修复了拼写错误,我们会看到:

\n\n
please fix a spelling error\n<<<<<<< HEAD\nand ok, I changed this\n=======\nand change this to something different\n>>>>>>> theirs\n
Run Code Online (Sandbox Code Playgroud)\n\n

通常,这很好\xe2\x80\x94,但有时这意味着你根本无法分辨原始基础是什么,因为冲突是在某事与无事之间,即,我们或他们中的一个人删除了一行,当我们或他们中的其他人改变了它。我发现这种diff3风格通常更容易阅读,但这种merge风格是默认的。

\n


aut*_*ech 1

我正在寻找的初学者级别的答案是这样的:

段 1 是当前签出分支中存在的代码 - 位于“HEAD”位置,如 <<<<<<< 所示 - 当前与段 3 冲突。

段 3 是您尝试合并的另一个分支中存在的代码 - 如 >>>>>>> 所示的“主”分支 - 与段 1 冲突。

段 2 是存在于两个要合并分支的共同祖先中的代码。它由 Git 显示,以帮助您更轻松地决定如何解决段 1 和 3 之间的合并。

您可以采用多种方法来解决段 1 和 3 之间的合并冲突。可能的方法有:

  1. 删除段1并保留段3;

  2. 删除段3并保留段1;

  3. 手动合并段 1 和 3 以包含两者中的更改。

  4. 从共同祖先恢复到段 2,丢弃段 1 和 3;

  5. 删除所有段并编写全新的代码

最终,您需要根据自己的判断来决定如何解决冲突。只需确保在提交合并之前从代码中删除所有合并标记即可。