【问题标题】:How do criss-cross merges arise in Git?Git 中的交叉合并是如何产生的?
【发布时间】:2014-12-09 19:12:31
【问题描述】:

我一直在浏览“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 都指向的 1 个合并提交结束(而不是 A 和 B 指向独立的合并提交,如图所示)。

谁能说明这种情况是如何出现的? 这是常见情况还是错误情况?

【问题讨论】:

标签: git git-merge least-common-ancestor


【解决方案1】:

我只是不明白怎么会造成这种情况。我试图使用测试存储库在分支之间重新创建这种纵横交错的合并情况,但我无法复制它。在所有情况下,我总是以 A 和 B 都指向的 1 个合并提交结束(而不是 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

现在修改最后一次提交(详情请参阅How does git commit --amend work, exactly?):

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 是的,你是对的; --all 不见了。我将编辑我的答案。
【解决方案2】:

当其他开发人员从您那里获取提交,您从他们那里获取,然后你们都将彼此的分支合并到您自己的分支中时,就会发生这种情况。你们都获取了。这可能会发生,这不是错误状态。

【讨论】:

  • 感谢您的回复。我想我必须设置一个示例 repo 来测试你的场景,这样我才能看到它展开。
  • 如果没有问题,我们可能从同一个服务器而不是从彼此获取
【解决方案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

【讨论】:

  • 感谢您的详细回复。我将在我的最后尝试这个并查看修订历史以查看它的实际效果。 :)
【解决方案4】:

当历史涉及交叉合并时,两次提交可能有多个最佳共同祖先。例如,使用这种拓扑:

---1---o---A
    \ /
     X
    / \
---2---o---o---B

以上来自git website,估计忘记加箭头了。

让我们看看下面的图片,很明显如何制作一个纵横交错的情况。当分支 A 需要来自分支 B 的一些代码时,它会从分支 B 合并;当分支 B 需要来自分支 A 的一些代码时,它会从分支 A 合并;在这里,我们遇到了纵横交错的情况。

更重要的是,很容易找出分支 A 和分支 B 共享祖先 1 和 2。

【讨论】:

    猜你喜欢
    • 2013-06-17
    • 1970-01-01
    • 2011-12-21
    • 2013-07-16
    • 2012-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多