我想这个问题的答案是 git 不是为此而生的。Git 真的不喜欢“提交的子节点”的想法,这是有充分理由的:它的定义不是很好。因为提交不知道它的子节点,所以它是一个非常模糊的集合。您可能实际上并没有在您的 repo 中拥有所有分支,因此缺少一些孩子。
Gits 的内部存储结构也使得查找提交的子节点成为一项相当昂贵的操作,因为您必须将所有头的修订图遍历到它们相应的根,或者直到您看到所有您想知道其子节点的提交。
git 支持的唯一这种概念是一个提交包含另一个提交的想法。但是只有极少数 git 命令(git branch
其中之一)支持此功能。在 git 支持的地方,它不支持任意提交,只支持分支头。
这一切似乎是 git 的一个相当严格的限制,但在实践中,您不需要提交的“孩子”,而通常只需要知道哪些分支包含特定的提交。
这就是说:如果您真的想得到问题的答案,您将不得不编写自己的脚本来找到它。最简单的方法是从git rev-list --parents --reverse --all
. 逐行解析,您将构建一棵树,并为每个节点标记它是否是您正在寻找的提交的子节点。您可以通过在遇到提交后自己标记提交,然后将该属性传递给他们的所有孩子等等来做到这一点。
一旦你有一个标记为包含所有提交的提交,你就将它添加到你的“解决方案列表”中并将它的所有子节点标记为死——它们不能再包含任何第一个提交。然后,此属性也将传递给它的所有后代。
如果您不存储不包含您要求的任何提交的树的任何部分,则可以在此处节省一些内存。
编辑破解了一些python代码
#!/usr/bin/python -O
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)
# only keep commit if it's a child of looking_for
if len(commit) > 0:
commits[sha] = commit
print "\n".join(solutions)