我猜这个问题的答案是,git不是为此而设计的。Git真的不喜欢“commit的子代”的想法,有一个非常好的理由:它没有很好地定义。因为一个提交并不知道它的子代,所以它是一个非常模糊的集合。您可能实际上没有所有分支在您的repo中,因此缺少一些子代。
Git的内部存储结构也使得查找提交的子代成为一个相当昂贵的操作,因为您必须遍历所有头的修订图,直到它们对应的根或者直到您看到所有要了解其子代的提交。
Git支持的唯一这种概念是一个提交包含另一个提交的想法。但是,这个功能只受到很少的git命令的支持(
git branch
是其中之一)。而且,当git支持它时,它并不支持任意提交,而只支持分支头。
所有这些可能看起来像是git的一个相当严格的限制,但实际上证明你不需要一个提交的“子代”,而通常只需要知道哪些分支包含特定的提交。
话虽如此:如果你真的想得到你的问题的答案,你将不得不编写自己的脚本来找到它。最简单的方法是从
git rev-list --parents --reverse --all
的输出开始。逐行解析,您将构建一棵树,并为每个节点标记它是否是您正在寻找的提交的子代。通过标记提交本身来实现这一点,然后将该属性传递给所有子代等等。
一旦你有一个已经被标记为包含所有提交的提交,你就可以将它添加到你的“解决方案列表”中,并将其所有子代标记为无效-它们不能再包含任何第一次提交了。这个属性也将传递给它的所有后代。
如果您不存储不包含您要求的任何提交的树的任何部分,则可以在这里节省一些内存。
编辑 黑客一些Python代码
import os
import sys
if len(sys.argv) < 2:
print ("USAGE: {0} <list-of-revs>".format([sys.argv[0]]))
exit(1)
rev_list = os.popen('git rev-list --parents --reverse --all')
looking_for = os.popen('git rev-parse {0}'
.format(" ".join(sys.argv[1:]))).read().splitlines()
solutions = set()
commits = {}
for line in rev_list:
line = line.strip().split(" ")
commit = set()
sha = line[0]
for parent in line[1:]:
if not parent in commits:
continue
commit.update(commits[parent])
if parent in solutions:
commit.add("dead")
if sha in looking_for:
commit.add(sha)
if not "dead" in commit and commit.issuperset(looking_for):
solutions.add(sha)
if len(commit) > 0:
commits[sha] = commit
print "\n".join(solutions)
git
在一些地方添加了--contains
选项,这可能会使这个过程变得更加容易。 - twalbergO(n)
的脚本来实现,其中n
是您存储库中提交的数量。但是,您为什么需要这个?您是否意识到这可能会有多个答案? - Chronial