如何判断一个提交是否是另一个提交的后代?
如何判断一个提交是否是另一个提交的后代?
从Git 1.8.0开始,可以通过merge-base
选项来实现:
git merge-base --is-ancestor <maybe-ancestor-commit> <descendant-commit>
从man页面中可以得知:
--is-ancestor
检查第一个提交是否是第二个提交的祖先,
如果是则返回0,否则返回1。非1的非零状态表示错误。
例如:
git merge-base --is-ancestor origin/master master; echo $?
很好!下面是一个包装了上述命令并以易读的方式输出结果的shell脚本:gist.github.com/simonwhitaker/6354592
换句话说:
git merge-base THING --is-ancestor OF_THING && echo yes || echo no
例如:git merge-base my-feature-branch --is-ancestor master && echo yes || echo no
git merge-base --is-ancestor -- commit commit
在我这边使用git
2.1.4 (Debian/Devuan 7.10 jessie)和1.9.1 (Ubuntu 14.04 trusty)进行测试,这些版本都比较古老。如果你使用Debian wheezy,可以通过sudo apt-get install git/wheezy-backports
安装。
为什么在没有echo yes || echo no
的情况下,git bash什么都不返回?
它实际上是有返回值的,只是没有将任何内容打印到stdout
。如果你熟悉C语言,这就像return 0
和printf("All good")
之间的区别。第一个返回值是为了调用函数(可以在if
语句中使用),而第二个是为了用户。因为git merge-base
主要是用于脚本内部使用的,所以用户需要自己包装打印部分的代码。
如何判断一个commit是否是另一个commit的子孙?
如果你想以编程的方式进行检查(例如在脚本中),你可以检查git merge-base A B
是否等于git rev-parse --verify A
(则A可以通过B达到),或者是否等于git rev-parse --verify B
(则B可以通过A达到)。 git rev-parse
在此需要将提交名称转换为提交SHA-1 /提交ID。
使用git rev-list
的方式,如VonC的回答也是一种可能的选择。
编辑:在现代Git中,可以使用git merge-base --is-ancestor
来显式支持此查询。
如果你所询问的其中一个commit是一个分支尖端,那么git branch --contains <commit>
或git branch --merged <commit>
可能是更好的非编程解决方案。
可能最快的方法是git checkout -b quickcheck <more-recent-commit-ID>
,然后git branch --contains <older-commit-ID>
(然后使用git branch -D quickcheck
来删除临时分支)。
两种可能的方法,都比's的答案要差很多。
:MattR的答案更好,但是这个答案(可能是被接受的答案)在git 1.8.0和git merge-base --is-ancestor
之前2年就存在了。
在一个大型代码库(200万个提交)中,我比较了git branch --contains <commit>
和git merge-base --is-ancestor ...
的速度:3分40秒 vs 0.14秒
如何判断一个提交是否是另一个提交的后代?
要解决这个问题,可以使用git rev-list
命令。该命令可以从一个提交开始,直到另一个提交,如果可达的话。
例如,可以尝试以下命令:
git rev-list --boundary 85e54e2408..0815fcf18a 0815fcf18a19441c1c26fc3495c4047cf59a06b9 8a1658147a460a0230fb1990f0bc61130ab624b2 -85e54e240836e6efb46978e4a1780f0b45516b20
(边界提交以-
为前缀)
如果显示的最后一个提交与git rev-list
命令中的第一个提交相同,那么这个提交是可达的。
如果第一个提交不可达,git rev-list
将不返回任何内容。
另外,git rev-list --boundary A..B
的结果会以A
结尾,如果A
是可达的。
这与git rev-list --boundary B --not A
是一样的,其中B
是一个正向引用,A
是一个负向引用。
它会从B
开始遍历图形,直到遇到一个从A
可达的修订。
如果A
直接从B
可达,它将会遇到(并显示,因为有--boundary
选项)A
本身。
这似乎是一个常见的用例,我很惊讶git还没有发布一个能够完全满足这一需求的"porcelain"命令。
需要注意的是,要以编程方式检查某些内容时,不应该使用"porcelain"命令,而应该使用"plumbing"命令。
总结一下,如果第一个提交是从第二个提交可达的,那么第一个提交是第二个提交的祖先。如果不可达,则两个提交没有关联。