我想这个问题的答案是git不是为此而做的。 Git真的不喜欢“承诺的孩子”的想法,并且有一个很好的理由:它没有很好的定义。因为提交并不知道它的子节点,所以它是一个非常模糊的集合。你可能实际上没有回购所有的分支,所以错过了一些孩子。
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)
我想不出一个简单的(有效)的方式来做到这一点,短生成所有的列表的合并提交,每一个测试分别查看是否可以从那里访问所述提交中的每个提交。可以相对容易地编写脚本,但它会*慢*。我认为最近(即1.8+版本)的'git'在几个地方增加了一个'--contains'选项,这可能会让这个更容易一些。 – twalberg
B和C属于不同的分支吗? – ShadyKiller
@ShadyKiller:在具体的例子中,是的;一般来说,没有。所有这三个人可能都在同一个分支(在这种情况下,答案只会是最新的提交)或不同的分支。地狱,可能会多于或少于三次提交;这是一个相对任意的数字。 –