如何在Git中查找包含某一次提交记录的所有引用历史记录

9
假设您在 Git 中有以下结构。
    A <-- refs/heads/somebranch
    |
    B 
    | \
    C  D <-- refs/tags/TAG1
    |  |
    E  F
    |  | \
    G  H  I <-- refs/heads/branch1                
    |
    J <-- refs/heads/master

现在我想要找到所有引用它们的历史中包含提交 B 的参考文献。
所以如果我能这样做就好了。
$ git refs --contains B
refs/tags/TAG1
refs/heads/branch1
refs/heads/master

我查看了 Git 文档,发现 git branch -a --contains <commit_id> 会列出包含 commit_id 的所有分支。
$ git branch -a --contains 4af9822
  master
  remotes/origin/someBranch
  ...

我发现了命令git tag --contains 9338f2d
$ git tag --contains 9338f2d
  someTag
  anotherTag
  ...

当然我可以做类似这样的事情。
$ git branch -a --contains 4af9822 && git tag --contains 9338f2d

但是有没有一个命令可以一次打印所有引用?

git for-each-ref --contains 将只能在2.7及以上版本中使用 ;) - VonC
3个回答

14
为了补充torek的回答,git 2.7 (2015年第四季度)将提供更完整的git for-each-ref版本,该版本现在支持--contains
git for-each-ref --contains <SHA1>


请参阅提交 4a71109, 提交 ee2bd06, 提交 f266c91, 提交 9d306b5, 提交 7c32834, 提交 35257aa, 提交 5afcb90, 提交 d325406, 提交 6841104, 提交 b2172fd, 提交 b2172fd, ... , 提交 b2172fd (2015年7月7日),以及由Karthik Nayak (KarthikNayak)提交 af83baf (2015年7月9日)进行的操作。
(由Junio C Hamano -- gitster --提交 9958dd8中合并,日期为2015年10月5日)

一些来自“git tag -l”和“git branch -l”的功能已经被应用到“git for-each-ref”中,以便最终可以在后续的一系列操作中共享统一的实现方式。请注意保留原文中的html标签。
* kn/for-each-tag-branch:
  for-each-ref: add '--contains' option
  ref-filter: implement '--contains' option
  parse-options.h: add macros for '--contains' option
  parse-option: rename parse_opt_with_commit()
  for-each-ref: add '--merged' and '--no-merged' options
  ref-filter: implement '--merged' and '--no-merged' options
  ref-filter: add parse_opt_merge_filter()
  for-each-ref: add '--points-at' option
  ref-filter: implement '--points-at' option  

请注意,从Git 2.13(2017年第二季度)开始,git for-each-ref --no-contains <SHA1>终于得到支持!

请查看提交 7505769, 提交 783b829, 提交 ac3f5a3, 提交 1e0c3b6, 提交 6a33814, 提交 c485b24, 提交 eab98ee, 提交 bf74804 (2017年3月24日), 提交 7ac04f1, 提交 682b29f, 提交 4612edc, 提交 b643827 (2017年3月23日), 以及 提交 17d6c74, 提交 8881d35, 提交 b084060, 提交 0488792 (2017年3月21日) 由 Ævar Arnfjörð Bjarmason (avar) 提交。
(由 Junio C Hamano -- gitster -- 合并于 提交 d1d3d46, 2017年4月11日)


5

没有内置的方法(2015年10月的更新:现在有了,请参见VonC的新答案),但是使用git branch -a --containsgit tag --contains命令可以获取您通常会发现“有趣”的所有引用。

有一种(非内置的)方法可以找到所有这样的引用。每当您问及“所有引用”时,都应该查看git for-each-ref。它允许您迭代所有引用或某些所有引用的子集:

$ git for-each-ref
996b0fdbb4ff63bfd880b3901f054139c95611cf commit refs/heads/master
740c281d21ef5b27f6f1b942a4f2fc20f51e8c7e commit refs/remotes/origin/maint
996b0fdbb4ff63bfd880b3901f054139c95611cf commit refs/remotes/origin/master
7327a17171fc87d5f8f5c790eb1ba1d0e031482d commit refs/remotes/origin/next
[... snip]
efe35e936c6c32a7630086a84b2c3b3471ea534f tag    refs/tags/v2.0.1
b4463ead04f1801104502ea087dbb6bdd21b4ef1 tag    refs/tags/v2.0.2
3c81e95201ece182e799709c91b15a3501919d26 tag    refs/tags/v2.0.3

在这种情况下,我在git存储库上运行了git for-each-refs,没有其他参数,因此它生成默认输出。

现在,您只需要在原始引用上运行--contains检测器。虽然没有任何管道命令将其作为动词,但可以使用git merge-base中的--is-ancestor测试来轻松表达--contains。正如git分支文档所述,--contains仅测试分支端点——在refs/heads/refs/remotes/引用左侧显示的SHA-1是否是指定提交的后代。 "是后代"实际上是与“是祖先”相同的测试,只是参数互换:

$ git branch --contains 996b0fd^
* master
$ git rev-parse 996b0fd^
6da748a7cebe3911448fabf9426f81c9df9ec54f

由于 master996b0fd...--contains 对于 996b0fd 和其第一个父提交 6da748a... 都有匹配项,因此我们可以确定是否已经正确使用了我们的 git merge-base --is-ancestor 参数:

$ git merge-base --is-ancestor 6da748a 996b0fd && echo ok
ok

请注意,996b0fd 被视为其自身的祖先。
$ git merge-base --is-ancestor 996b0fd 996b0fd && echo ok
ok

所以您需要做的就是将git for-each-ref和运行git merge-base --is-ancestor的shell命令或循环串在一起。

是的,我认为没有内置命令。我使用了一个可以带参数的别名并添加了我的答案来解决它。由于你的回答很详细,我会接受 :) - René Link

0

我有同样的问题,git for-each-ref --contains <SHA1> 对我来说不够,因为它似乎只列出了当前引用。我使用以下脚本遍历了引用日志,并使用上面答案中的merge-base技巧进行了--contains检查:

SEARCH_FOR=<SHA1>

for refname in `git for-each-ref --format='%(refname)'`; do
    for old in `git reflog --pretty='format:%H' $refname`; do
        git merge-base --is-ancestor $SEARCH_FOR $old && echo "FOUND reference from ref $refname / $old!"
    done
done

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接