Git中的交叉合并是如何产生的?

9

我一直在阅读“git merge-base”手册,但是我不明白多个合并基础是如何产生的。特别地,我卡在了手册中以下插图上:

When the history involves criss-cross merges, there can be more than one best common
ancestor for two commits. For example, with this topology:
  ---1---o---A
      \ /
       X
      / \
  ---2---o---o---B
both 1 and 2 are merge-bases of A and B. Neither one is better than the other (both
are best merge bases). When the --all option is not given, it is unspecified which
best one is output.

我真的无法理解如何会出现这样的情况。我尝试使用测试存储库重新创建分支之间的这种交叉合并情况,但我无法复制它。在所有情况下,我总是最终得到一个合并提交,A和B都指向它(而不是像图表所示的那样指向独立的合并提交)。

有人能说明一下这种情况是如何产生的吗? 这是常见情况还是错误情况?


相关概念:"Foxtrot Merge"。另请参见此SO问题 - Christian Long
还相关的内容:如何在Git中成功创建交叉合并? - Wai Ha Lee
4个回答

11
我就是无法理解这种情况是如何产生的。我尝试使用测试存储库重新创建分支之间的这种十字路口合并情况,但我无法复制它。在所有情况下,我都最终只得到了一个合并提交,A和B都指向它(而不是像图表所示的那样指向独立的合并提交)。
有人能说明一下这种情况是如何出现的吗?
十字路口合并可能以不同的方式出现。一个我想到的例子是,当您有两个分支引用指向同一个合并提交时(其中一个分支被检出),然后运行git commit --amend。以下玩具示例就是这样做的:
# set things up
cd ~/Desktop
mkdir crisscross
cd crisscross
git init

# make an initial commit
printf "bar\n" > README.md
git add README.md
git commit -m "add README"

# add a line at the top of the file and commit
sed -i '' '1s/^/foo\n/' README.md
git commit -am "add foo line"

# create and checkout a branch pointing at the initial commit
git checkout -b other master^

# add a line at the bottom of the file and commit
printf "baz\n" >> README.md
git commit -am "add baz line"

# do a merge and make both branches point to this merge commit
git merge master
git checkout master
git merge other

当前阶段,git log --graph --oneline --decorate --all的输出结果为:

*   30b9175 (HEAD, master, other) Merge branch 'master' into other
|\  
| * f318ead add foo line
* | 31875d9 add baz line
|/  
* 8b313a0 add README

现在修改最后一次提交(有关更多详细信息,请参见如何精确地使用git commit --amend?):

git commit --amend

此后,git log --graph --oneline --decorate --all命令的输出将会是:

*   69bbcbe (HEAD, master) Amended merge commit
|\  
| | *   30b9175 (other) Merge branch 'master' into other
| | |\  
| |/ /  
|/| /   
| |/    
| * f318ead add foo line
* | 31875d9 add baz line
|/  
* 8b313a0 add README

这就是:历史记录现在包含了交叉合并。

这是常见情况还是错误情况?

如上所述,这种情况可能会出现。虽然不太理想,但不应被视为“错误状态”。


谢谢您提供这个例子,这正是我正在寻找的!问题现在对我来说变得更加清晰了。 - MACS
@MACS 是的,你说得对;缺少 --all。我会编辑我的回答。 - jub0bs

3
这是一系列提交的序列,会生成交错合并。比使用--amend的步骤多,但只使用基本操作。运行此脚本以查看确实会得到交错合并。
#!/bin/sh
# Initialize 
git init .                                                                                                                       
echo date > file.txt; git add file.txt
git commit -m "Initial commit"

# Make branches                                                                                                                     
git branch A; git branch B

# Make change in A                                                                                                                  
git checkout A
echo date > fileA.txt; git add fileA.txt
git commit -m "first commit along branch A"

# Make change in B; add tag for later                                                                                                               
git checkout B
echo date > fileB.txt; git add fileB.txt
git commit -m "first commit along branch B"
git tag "tag-B"

# Merge A into B (still on B)                                                                                                       
git merge --no-edit A; git tag "M"

# Add another commit on B whose parent is M                                                                                         
echo date > fileB2.txt; git add fileB2.txt
git commit -m "second commit along B"

# Switch to A and add a commit; note that
# M is not an ancestor of this commit                                                                          
git checkout A
echo date > fileA2.txt; git add fileA2.txt
git commit -m "second commit along A"

echo "Best common ancestors (before criss-cross):"
# Should be only one
git merge-base --all A B

# Merge the commit tagged "tag-B" 
# into A generating the cris-cross-merge                                                            
git merge --no-edit tag-B

echo "Best common ancestors (after criss-cross):"
# Should be two
git merge-base --all A B

感谢您详细的回复。我会在我的端上尝试并查看修订历史记录以便实际操作。 :) - MACS
对我来说,这个脚本并没有创建一个交叉合并。 - mernst

2
这种情况发生在另一个开发者从你那里获取提交,你从他们那里获取,然后你们两个将彼此的分支合并到自己的分支中,在你们都获取后再合并。这可能会发生,并不是错误状态。

谢谢回复。我想我需要设置一个示例存储库来测试您的场景,以便我可以看到它的展开。 - MACS
可能我们从同一台服务器获取数据,而不是互相之间传输,如果没有问题的话。 - briefy

1
当历史记录涉及交错合并时,两个提交可能有多个最佳公共祖先。例如,对于以下拓扑结构:
---1---o---A
    \ /
     X
    / \
---2---o---o---B

上面的内容来自git网站,我猜它忘记添加一个箭头了。
让我们看下面的图片,很明显如何制造交叉情况。当分支A需要一些来自分支B的代码时,它从分支B合并;当分支B需要一些来自分支A的代码时,它从分支A合并;在这里,我们遇到了交叉情况。
更重要的是,很容易发现分支A和分支B共享祖先1和2。

enter image description here


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