如何查找给定提交是否在分支的第一个父链上

时间:2017-03-28 20:06:33

标签: git git-history-graph

我试图编写一种方法来判断给定提交是否在给定分支的第一个父链上。因此,例如,merge-base不会飞,因为提交可能已合并。我想知道确切的提交是否曾经是分支的一角。

注意:有问题的分支受非快进合并策略的约束。

2 个答案:

答案 0 :(得分:1)

无快进策略意味着你可以在git log --first-parent中进行grep。您可能只想要哈希值,因此您可以使用git rev-list代替

git rev-list --first-parent | grep <commit hash>

否则使用--formatgit log来显示所需的数据。

编辑:这篇文章可以给你一些想法

How can I tell if one commit is an ancestor of another commit (or vice-versa)?

答案 1 :(得分:0)

奇特的方式

一个简单的&#34;是祖先&#34;测试显然不会做,因为承诺下来的父母连锁店也是祖先:

...o--o--A--o--o--o--T
    \            /
 ...-o--*--B----o
         \
          C

AB都是T的祖先,但您希望在拒绝AB时接受C。 (假设--first-parent是这里的第一行。)

然而,使用git merge-base实际执行部分工作。您不需要--is-ancestor的{​​{1}} 模式,而需要一些额外的处理。

请注意,无论git merge-base与某些祖先之间的路径如何,T合并基础和该祖先(例如T或{{ 1}})或者是祖先本身(这里分别是AB),或祖先的一些祖先,如果我们查看A并提交B *作为一对。 (即使在多个合并基础的情况下也是如此,尽管我会向您构建一个证明。)

如果测试提交的合并基础和分支提示的所有一组中的任意一个或任意选择的一个已经不是测试提交,那么我们有一个类似T和可以拒绝它。 (或者,我们可以使用C来拒绝它,或者......好吧,请参阅下文。)如果没有,我们必须枚举有关提交之间的祖先路径中的提交。分支小费。对于C,这是:

--is-ancestor

对于B,这是:

A

如果任何此类提交是 merge 提交,标记为 o--o--*--T 的提交,我们需要确保第一个父提交包含此列出的提交之一路径。最棘手的案例是拓扑上类似于:

               *--T
              /
             o

因为它们之间的*包括合并和两种方式到达 o--o / \ ...--A o--T \ / o--o ,其中一种是第一父路径,其中一种不是。 (如果--ancestry-path本身也是合并,那也是如此。)

但是,我们实际上并不需要首先找到合并基础。我们只使用合并库来检查祖先路径。如果合并基础本身不是测试提交,则测试提交不是提示提交的祖先,A本身不包括T。此外,添加testcommit..tipcommit - 丢弃所有本身不属于左侧子项的提交 - 将丢弃testcommit输出中的所有提交:类似{ {1}}没有后代是--ancestry-path的祖先(如果确实如此,git rev-list将是合并基础。)

因此,我们想要的是检查C中的提交。如果此列表为空,则测试提交首先不是分支提示的祖先。我们有一个像提交T的案例;所以我们有答案。如果列表非空,则将其减少为其合并组件(使用C再次运行,或将列表提供给git rev-list --ancestry-path testcommit..branchtip,以生成缩小的列表)。如果此列表是 -empty,请通过查找其C ID并确保结果位于第一个列表中来检查每个合并。

实际(尽管未经测试)shell脚本代码:

--merges

蛮力方式

以上测试尽可能少提交提交,但从某种意义上说,运行起来会更加实用:

git rev-list --stdin --merges

如果输出本身包含--first-parent,那么TF=$(mktemp) || exit 1 trap "rm -f $TF" 0 1 2 3 15 git rev-list --ancestry-path $testcommit..$branch > $TF test -s $TF || exit 1 # not ancestor git rev-list --stdin --merges < $TF | while read hash; do parent1=$(git rev-parse ${hash}^1) grep "$parent1" $TF >/dev/null || exit 1 # on wrong path done exit 0 # on correct path 可以访问,仅来自git rev-list --first-parent ${testcommit}^@..$branch 的第一个父项。 (我们使用$testcommit排除 $testcommit的所有父项,这样即使对于根提交也是如此;对于其他提交,branch就足够了,因为我们可以使用^@。重新使用$testcommit。)此外,如果我们确保这是以拓扑顺序完成的,那么从${testcommit}^命令发出的 last 提交ID将是--first-parent本身当且仅当git rev-list可以从$testcommit访问时。因此:

$testcommit

应该做的伎俩。 $branch周围的引号以防万一它扩展为空字符串。