找到两个提交的第一个普通孩子

时间:2012-06-06 21:03:46

标签: git merge branch git-branch

           :
           A
T         / \
i        B   C
m        :   :
e        D   E
          \ /
|          F
V          :

git merge-base B E允许查找两个提交的共同祖先A的位置。有没有办法找到再次合并两个分支的提交F

3 个答案:

答案 0 :(得分:1)

糟糕。没有仔细阅读。

提交中的唯一信息是其父(或父项)的id。您无法从父提交中获取子项(这是存储库的定向部分是DAG)。

再看一下 - 看起来git log的--ancestry-path选项可以做到这一点。例如:

* 85d26ab When compiling vim, also compile & install gvim
*   3146e5d Merge remote-tracking branch 'origin/devel' into deve
|\
| * 28d08e5 rebasing-merge: specify all commits explicitly
* | 006d11d Help 'file' find its magic file
|/
* e68531d (tag: Git-1.7.6-preview20110720) Update submodules

我们可以使用

获取这两个提交的所有子项
git log --oneline --ancestry-path B..E

如果你然后反过来并选择第一个 - 那就是F。

git rev-list --reverse --ancestry-path 28d08e5..006d11d | head -1
在我的情况下,

返回3146e5d。

答案 1 :(得分:1)

此问题不一定有唯一的答案,因此您必须决定一些约束和/或启发式方法,或者接受不止一个“下游”合并的可能性。问题的核心与多个合并基础候选者的问题相同-使用git merge-base --all列出所有候选者,否则Git只会选择算法中首先弹出的那个。我们可以这样做,或者找到所有最佳合并候选者。

您已经绘制了我通常倾向于横向渲染的内容,例如:

  B--...--D
 /         \
A           F--G--H   <-- branch1
 \         /
  C--...--E   <-- branch2

但是我们可能有这个:

  B--C---D--E--...   <-- branch1
 /    \ /
A      X
 \    / \
  F--G---H--I--...   <-- branch2

在这种情况下,如果同时允许DH合并,则branch1branch2的合并都是“分支合并的地方”的良好候选者被考虑。即使您不这样做,branch2之后又合并回branch1

  B--C---D--E---J--...   <-- branch1
 /    \ /      /
A      X      /
 \    / \    /
  F--G---H--I--...   <-- branch2

然后从branch1开始(或结束),DH都是同样不错的候选人。

无论如何,我们在这里需要枚举以您要考虑的一个或所有分支结束的提交。为此,我们可以使用例如:

git rev-list --ancestry-path ^B ^E branch1 branch2

这将查找属于branch1 branch2的提交,也属于提交B 的后代E

要真正获得正确的答案,我们要添加--children。这样,我们将获得每个提交的哈希ID,以及沿着相同方向提交的子代。 Git在遍历链接时通过反转从孩子到父母的反向连接来实现--children,这已经足够了;但我们看不到BE的提交。这是一个问题。要显示它们,我们可以添加--boundary。这是不理想的:--boundary有时包含一些我们不希望的提交。幸运的是,它们都标记有-,因此我们可以通过剔除不是我们关心的提交来排除额外的边界提交。

我将不显示任何内容,但是如果您这样做了,您现在将获得一个列表,每行一个条目,每个节点(顶点)及其边缘连接到其子节点。您现在可以问这些(V,E)集形成的DAG的LCA是什么?

如果我们只使用Git的LCA算法,那就太好了,但是Git没有办法在任意图上调用它-我们只能在提交上调用它,而实际的提交有父级,而不是子级。因此,您将必须自己编写。请参见Algorithm to find lowest common ancestor in directed acyclic graph?(很遗憾,该答案没有可接受的答案)。 This algorithm looks correct at first blush;它在图中具有LCA的两个标准定义之一。

但是,如果我们愿意解决一个不太好的答案,那么在大多数情况下,我们可以通过添加--topo-order来获得足够的信息(以确保父母在他们所有的孩子中都出来了)子级)和--merges(忽略所有不是合并提交的内容)。这将获得所有合并的列表。

我在这里建立了一个带有简单案例的测试库:

$ git log --all --decorate --oneline --graph
* 91fcef6 (HEAD -> master) J
* d1e5905 I
*   5bf18a0 merge
|\  
| * 49b2ba7 (sidebr) D
| * 725e5ea C
| * 36b830d (tag: B) B
* | 198a982 (tag: G) G
* | 216bc01 F
* | e905e59 E
|/  
* 5df9428 initial

因此,我现在可以使用BG来命名提交B和G,而我想要“朝这个方向移动”的分支就是master。所以:

$ git rev-list --topo-order --merges --ancestry-path ^B ^G master
5bf18a0797dfd78107928a9a4095f357cfabe914

最后的 行是最接近两次提交的合并。在这种情况下,这也是唯一的一行,这就是我们想要的合并。

一旦我们绘制它,这里的缺陷就很明显了。假设我有一个更复杂的图,例如:

      I--J
     /    \
    H      M--N
   / \    /    \
  /   K--L      \
 /               \
A                 P--Q  <-- master
 \               /
  \   C--D      /
   \ /    \    /
    B      G--O
     \    /
      E--F

如果我现在运行git rev-list --topo-order --merges --ancestry-path ^B ^H master,我将列举提交P,然后依次列出GM。因此, last 行要么是提交G要么是提交M,并且当这两个合并时,它们并没有达到正确的准则:它们不合并BH。只有提交P会这样做。

因此,要检查您是否有正确的答案(不处理多个LCA问题),则应以相反的顺序从此git rev-list命令中提取每条输出线(考虑添加--reverse ),并查看两个提交是否都是每个提交的祖先。像GM这样的“内部”合并将只有一个提交作为祖先。要进行is-ancestor测试,请使用git merge-base --is-ancestor

if git merge-base --is-ancestor $commit1 $mergecommit &&
       git merge-base --is-ancestor $commit2 $mergecommit; then
    ... we've found a correct candidate
else
    ... move on to another candidate
fi

答案 2 :(得分:0)

this answer调整all.awk以同时携带每个参考号的行号,然后当您遇到父母时,请查看他们共同参考的参考号。