在我开始这个答案之前,让我强调一下,有两个不同的单词都拼写为“合并”。有一个动词形式,to merge,意思是“合并变化”。有一个名词或形容词形式,a merge 或 a merge commit,指的是特定种类的提交。
git merge 命令通常同时进行:它合并(动词)一些更改,然后进行合并(名词)或合并提交(形容词)。不过,Git 有很多方法可以进行动词形式的合并。它还允许git merge 命令省略最后的名词形式合并(但我们将在这里完全忽略这种情况)。
策略或扩展选项——短版
简短的版本是您几乎从不想要策略选项-s ours。
使用-X (eXtended) 选项,您可以告诉 Git 在发生冲突的情况下支持合并的“我们的”或“他们的”这一边。也就是说,假设文件wallaby.txt 的合并基础版本部分表示:
The Bennett's wallaby is
a variety of the red-necked wallaby.
两个分支提示版本之一——我们称之为左侧或本地或--ours版本——说:
The Bennett's wallaby is
a smaller variety of the red-necked wallaby.
另一个分支提示版本,即右侧或远程或其他或--theirs版本——说:
The Bennett's wallaby,
which is found on Tasmania,
is a variety of the red-necked wallaby.
当我们比较基本版本与两个分支提示时,比较结果相似但不完全相同,会更改为相同的行范围:
$ git diff $base $left
...
The Bennett's wallaby is
-a variety of the red-necked wallaby.
+a smaller variety of the red-necked wallaby.
...
但是:
$ git diff $base $right
...
-The Bennett's wallaby is
-a variety of the red-necked wallaby.
+The Bennett's wallaby,
+which is found on Tasmania,
+is a variety of the red-necked wallaby.
这两个变化冲突,你会得到一个合并冲突。
eXtended -X ours 选项的意思是“更喜欢我们的更改”:如果两个更改发生冲突,则完全忽略 他们的 更改,取而代之的是 我们的。 eXtended -X theirs 选项的意思是“更喜欢他们的改变”,即忽略我们的。
请注意,无论您喜欢哪种更改,您都会在这里失去一些东西。一个变化提到贝内特的小袋鼠要小一些,而另一个变化提到它是塔斯马尼亚的形式。如果你只选择一个,你就会失去另一个。您可能应该手动组合此更改,而不是让 Git 选择一侧。
您何时以及为什么可以使用-s ours
如果这是对文件的唯一更改,则使用-s ours 而不是-X ours 不会对这个特定的文件级合并产生影响:无论哪种方式,我们都会采用“我们的” (左侧)改变,忽略他们的。但进一步假设只有他们的版本修复了文件中其他地方的错字:
$ git diff $base $right
...
-walaby
+wallaby
...
在$left(本地或--ours)输出中没有类似的东西。如果我们使用-X ours 而不是-s ours,我们至少会接受这个修复。当然,右侧可能有一个更改,即添加错字而不是删除它们。 Git 不会知道或关心:它的工作是组合更改,而不是决定它们是否有意义,无论是单独还是组合!
此外,as Tim Beigeleisen mentioned in a comment above,-s ours 选项甚至不查看右侧差异。事实上,它根本不运行任何差异!它告诉 Git 在不以任何方式使用另一端的情况下进行合并提交。换句话说,它只是记录了有一个合并,并没有实际进行任何合并。
这样做的唯一原因是制作合并的提交图历史记录。正是这段历史记录在提交图本身中,决定了你和 Git 如何“看到”发生的事情,当你——或其他任何人——稍后回来查看时。 merge commit 的存在意味着您和 Git 将把合并视为已已经采用任何被合并的所有好的部分——即使“采用”的方法是忽略一切。已经采取了所有这些部分,你将永远不会再采取它们:所以-s ours允许你“杀死”一个分支,不仅仅是忽略它,而是通过记录所有人你故意杀死它的历史。
Git 只记录结果,不记录方法
Git 不记录合并策略,也不记录任何策略选项。因此,您可以判断是否有人使用此类选项的唯一方法是猜测。如果您遇到某个存储库中包含一些合并提交,您可以自己重复 合并,看看是否得到相同的结果。如果您确实得到了相同的结果,那么进行合并的人可能使用了您刚刚使用的完全相同的命令。如果你得到一个不同的结果,可能是合并的人使用了不同的命令,或者不同的参数集。或者他们手动解决了任何冲突!
更多背景
有关更多背景信息,请允许我引用我的 perennially-not-making-much-progress(因为我有工作 :-))一书。
合并的高级视图
合并的目标很容易理解。几个人或组,甚至一个人有两个或多个任务,从一个共同的代码库开始,并进行了一系列更改。例如,在有袋动物制造者中,爱丽丝可能正在研究袋熊,而鲍勃可能正在研究袋鼠。每个人或组(或什至只有一个人担任多个角色)在她或他的私有存储库和/或私有分支中工作。这两条发展路线——即我们在第 1 章中提到的哲学意义上的分支——与这个共同的起点相关。我们还不会担心他们如何分享他们的提交,但在某个时候,某个人——可能是 Alice 或 Bob,或者可能是第三人——将合并这些更改。组合应该包含两个变化的所有好的部分。最简单的合并方法是从同一章节执行三向合并。现在我们了解了提交图,并且对比较新提交和旧提交有了大致的了解,现在是时候简要了解一下 Git 和 Mercurial 如何执行合并了。
两个提交和一个合并基础
在第 2 章中,我们简要地指出(参见第 38 页和第 45 页)任何两个提交的 LCA 是它们的合并基础。在某些情况下,可能有多个合并基础,但这种情况很少见,我们暂时不会解决。相反,让我们注意,根据定义,可能是单一的合并基础不仅仅是其他两个提交的 a 共同祖先。事实上,正确是共同的起点:第一个提交可以从两个头(Mercurial)或分支提示(Git)都可以到达我们正在合并。 VCS 需要找到合并基础以找出我们做了什么和他们做了什么。
在 Git 和 Mercurial 中,我们通过正常的结帐流程选择两个提交中的一个。无论我们现在签出的任何提交——我们的 current 提交——都会参与合并。我们使用一些适当的提交标识符来选择第二次提交,通常是分支名称,但有时是哈希 ID,或者在 Mercurial 中,
一个简单的修订号(有时甚至什么都没有,并且
VCS 为我们解决了这个问题)。这将是“其他”或“他们的”提交。18 然后我们可以简单地运行 git merge otherbranch 或
hg merge otherbranch.
18这里我将三个提交称为base、current和other。 Git 对于当前和其他提交没有单一、一致的名称。 Mercurial 一直称它们为 local 和 other 提交。我还将两个非基本提交称为合并的 side。在几个地方,Git 确实调用了当前提交 ours 和另一个提交 theirs。
然而,ours/theirs 命名法存在一个问题,我们稍后会在讨论樱桃采摘和变基时看到这一点。