Git中的合并是否对称?

45
假设我们有两个分支(BC),它们已经从共同的祖先A分叉了。从B合并到C会产生与从CB合并相同的结果吗?
  A
  |
 / \
B   C

澄清一下- 我假设任何手动合并冲突的解决方案都会在两个方向上发生。但是,任何自动合并是否会导致选择相同的代码?这是我假设的,因为提交日期在两个方向上都是相同的。

进一步澄清- 我知道实际合并会基于方向产生"镜像" 。我只是询问自动解决的冲突。


在相关内容上,您可能会发现这篇来自Git邮件列表的现在三年的帖子相关,其中提到Git记录合并的方式对合并的“方向”敏感:http://thread.gmane.org/gmane.comp.version-control.git/127361 - seh
有人知道@seh提供的链接的缓存版本吗?该页面似乎已经无法访问/消失了。 - tomosius
哎呀,今晚我做了一些搜索,仍然找不到可行的链接。 - seh
@seh 我认为缺失的讨论是这个?https://marc.info/?t=123326808700001&r=1&w=2 - MikeBeaton
那是一个不错的帖子,包括一些精美的ASCII艺术,但我认为它不是我提到的那个——差不多十年前。 - seh
4个回答

32
默认合并是可以的。三方合并找到一个公共祖先,然后应用来自两侧的差异,这个操作不受顺序影响。关于合并顺序和可交换性的讨论在git list上引起了一场有趣的讨论(如果你对这种事情感兴趣的话)。注意B into CC into B应该是对称的,但不能保证(B into C) into AB into (C into A)相同。

[编辑注,2020年4月:如果添加像-X ours-X theirs这样的选项,则答案变为“否”,请参见twalberg's answer和其他附加警告。]


为了更详细地解释,基于Vince在下面的评论和seh在问题上的评论,B into CC into B之间将会有两个明显的区别,两者都不会影响问题中提到的自动合并解决方案。
首先,历史记录将会不同。合并提交的父节点将根据合并顺序而改变。对于这些示例,我将使用“first_branch”和“second_branch”,以便我可以保留字母来表示提交。
git checkout first_branch && git merge second_branch

E <- merge commit
|\
| D <- second_branch's tip
| |
| C <- another commit on second_branch 
| |
| B <- and another
|/
A <- first_branch's tip before the merge

在这种情况下,E的“第一个父级”,E^1,是合并之前first_branch的最新版本。second_branch是合并提交的“第二个父级”,也就是E^2。现在考虑反过来:
git checkout second_branch && git merge first_branch

E <- merge commit
|\
| D <- first_branch's tip
| |
| C <- another commit on first_branch 
| |
| B <- and another
|/
A <- second_branch's tip before the merge

父级关系被颠倒。在合并之前,E^1 是第二分支的顶端。E^2 是第一分支的顶端。
其次,冲突的显示顺序将会颠倒。在第一个情况中,冲突可能如下所示:
<<<<<<< HEAD
This line was added from the first_branch branch.
=======
This line was added from the second_branch branch.
>>>>>>> second_branch

在第二种情况下,相同的冲突将如下所示:
<<<<<<< HEAD
This line was added from the second_branch branch.
=======
This line was added from the first_branch branch.
>>>>>>> first_branch

这些差异不会影响自动合并解决,但在反转三方合并顺序时会出现。


如果您已经测试过,请提供一个简短的示例,不介意吗?我的意思是,如果您只是合并而没有提供任何合并策略/选项,但强制自动合并(如果可能),如果一行上存在冲突,会发生什么?你会有:`<<<<<<< yours:sample.txt Conflict resolution is hard; let's go shopping.

Git makes conflict resolution easy.
theirs:sample.txt`?还是其中之一被选择了,哪一个呢?
- Vince
@Vince 对于历史记录和冲突呈现方式,顺序很重要,但自动合并解决结果将是相同的。我添加了更多细节和一些示例。 - Christopher
Hamano在这个回答中链接的讨论中的帖子是必读的 http://thread.gmane.org/gmane.comp.version-control.git/107737/focus=107895 - 不确定它是否提供了问题的答案。 - Mr_and_Mrs_D
@Mr_and_Mrs_D 链接失效 :( - Tobias Kienzler
所以git-merge是可交换的(在历史元数据和合并冲突解决方案上)。为什么它不是结合律? - Jasha

6
这取决于你所说的“相同结果”的含义。从内容角度来看,假设没有冲突或所有现有的冲突都以完全相同的方式被细致地解决,那么生成的新合并提交的内容应该是相同的。
然而,从历史拓扑结构的角度来看,这两者非常不同。如果你在分支B上合并C,则B的HEAD移动到指向合并提交,但C保持不变。相反,如果你在C上合并B,则C的HEAD移动,而B保持不变。因此,最终的拓扑结构非常不同,这对未来在任一分支上的开发都具有影响,即使新提交的内容在任一情况下都是相同的。

在拓扑结构中的另一个差异,取决于你进行了哪种合并操作,在我下面的回答中已经给出了(我认为这个差异还没有被你提到的正确和有用的差异所涵盖)。 - MikeBeaton

2
合并后文件的内容是“对称的”(无论你做哪个合并,内容都相同),但如果你使用GitHub、Bitbucket或你喜欢的本地Git查看器来查看合并时所做的更改,你会发现在合并提交中显示的文件更改取决于你做了哪个合并(除了上述其他差异和我的编辑之外)。
git checkout b
git merge c

合并时显示的文件更改将是自cb分叉以来在c上进行的更改。
git checkout c
git merge b

在合并过程中显示的文件更改将是自bc分叉以来在b上进行的更改。

编辑:

另一个区别是,如果您稍后执行git reset [--hard] HEAD~{n}来回滚一些提交,而这个操作超过了合并点,则返回的分支取决于您进行的合并,始终是您合并到的分支(在上面的示例中被检出的分支)。


0

不,我认为它不会对称。首先,您可以设置合并策略选项,特别是在您的情况下,theirsours,这将确定选择B还是C。

在自动合并中,例如,如果您正在将远程分支合并到自己的分支中,则远程分支优先于您的更改,这意味着从B调用git merge C将导致选择C的更改。但我不是100%确定,所以您可以自己尝试并让我们知道结果。


设置合并策略选项的命令:git merge -s<strategy> -X<option> - Vince
一些参考页面:http://www.kernel.org/pub/software/scm/git/docs/git-merge.html - Vince

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接