如何查找git SHA指向分支HEAD?

12
为了避免使用(python)脚本检出特定的git SHA时不必要地分离HEAD,如果该SHA恰好是分支的当前HEAD,则我希望检出一个分支。
理想情况下,当我提供一个SHA时,我希望git返回一个分支名称,如果该SHA在分支的当前尖端,否则报错。 git describe --all --exact-match <SHA> 已经非常接近我需要的功能了,但它主要是针对标签的,所以如果一个分支和一个标签都指向我的SHA(例如,在我们的发布分支中经常发生这种情况),只有标签会被给出。这不太有用,因为即使分支也指向相同的SHA,检出标签也会导致分离的HEAD。
请注意,我不想执行 git branch --contains - 我不需要知道哪些分支包含我的提交。
如果没有像git describe这样专门针对分支的命令,我知道可以通过git show-ref交叉检查我的SHA与分支SHA。但这不是最优雅的解决方案。
我还可以使用git name-rev --name-only <hash>,但我必须手动检查输出中的~字符,如果有一个git命令可以做同样的事情,这会感觉不太优雅。

使用 git 2.7 (Q4 2015) 版本,将可以使用 git for-each-ref --points-at <branch_name> 命令。请参见下面的答案 - VonC
8个回答

8
你可以尝试像这样做:

你可以尝试如下方法:

git for-each-ref --format="%(refname:short) %(objectname)" 'refs/heads/' |
    grep SHA1 | cut -d " " -f 1

这应该会给你一个目前在修订版本SHA1的分支列表。


1
这几乎是正确的,但模式应该是“refs/heads/”,否则像“foo/bar”这样的分支将无法匹配。 - FelipeC
@felipec 您说得完全正确。我已经更新了我的答案。谢谢! - John Szakmeister
谢谢。这与我使用 git show-refgit name-rev 做的类似。我猜希望只用 git 就能做到,就像 git describe 一样。 - Christoph
@Christoph 是的,很遗憾这里没有更好的答案。 - John Szakmeister

3

随着即将到来的git 2.7(2015年第四季度),您将获得更完整的{{link1:git for-each-ref}}版本,该版本现在支持--points-at参数。

git for-each-ref --points-at <branch_name>

使用文档:

--points-at <object>:

只列出指向给定对象的引用。

这将允许检查提交是否属于该列表。



请查看由Karthik Nayak (KarthikNayak)于2015年7月7日提交的commit 4a71109, commit ee2bd06, commit f266c91, commit 9d306b5, commit 7c32834, commit 35257aa, commit 5afcb90, ..., commit b2172fd,以及于2015年7月9日提交的commit af83baf
此代码已于2015年10月5日被Junio C Hamano -- gitster --合并至commit 9958dd8

一些来自于 "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  

2

如果你要使用Python,我建议你这样做:

import subprocess

def get_name(target):
    p = subprocess.Popen(['git', 'for-each-ref', 'refs/heads/'], stdout=subprocess.PIPE)
    for line in p.stdout:
        sha1, kind, name = line.split()
        if sha1 != target:
            continue
        return name
    return None

另一种选择是利用eval的威力构建一个字典:
d = '{' + subprocess.check_output(['git', 'for-each-ref', '--python', '--format=%(objectname): %(refname),', 'refs/heads/']) + '}'
name = eval(d)[sha1]

稍微简单一点的方法(不显示“kind”条目)是 git show-ref --heads - Christoph
谢谢。这与我使用 git show-refgit name-rev 做的类似。我猜希望只用 git 就能做到,就像 git describe 一样。 - Christoph
我会接受这个答案,而不是@jszakmeister几乎相同的答案,因为它不需要管道(因此在subprocess中不需要Shell=True)。 - Christoph
是的,我猜 git show-ref --heads 会更简单,而且基本上会产生相同的结果。唯一的区别是,如果存储库包含无效 ref(指向不存在的提交),那么 git show-refs 将会失败,而 git for-each-ref 仍然可以工作。 - FelipeC
1
建议在返回之前添加 p.communicate(),以丢弃任何剩余的 p.stdout 数据并等待进程完成。这不总是必要的(也许甚至不常见),但这样可以使 get_name() 在自己完成后进行清理。 - torek

1

获取分支名称

要获取提交或标签引用的分支名称,可以使用以下.gitconfig配置:

[alias]
  branch-name = !"git for-each-ref --format=\"%(refname:short) %(objectname)\" 'refs/heads/' | sed -En 's/^(\\S+)\\s+'\"$(git rev-parse \"$1\" 2>/dev/null)\"'$/\\1/p' | grep . || { echo \"$1: not a branch\" >&2; false;}

例:

$ git branch-head-name da8ecd90
master

边缘情况:

  • 如果commit-ish不是一个分支,则输出为空。
  • 如果没有参数,则列出所有分支名称。

注意:没有引用.gitconfig字符串的别名为:
branch-name=!git for-each-ref --format="%(refname:short) %(objectname)" 'refs/heads/' | sed -En 's/^(\S+)\s+'"$(git rev-parse "$1" 2>/dev/null)"'$/\1/p' | grep . || { echo "$1: not a branch" >&2; false;}

1
对于我的情况,我想知道HEAD是否指向一个名为post-checkout的分支。如果指向分支,则退出0;如果未指向分支,则退出非零值,这很可能是分离HEAD状态(假设检出成功)。git symbolic-ref --short HEAD可以实现此功能,并且在指向分支时还返回分支名称。显然,有更好的解决方案适合OP的需求,但我想提供这个信息以备不时之需。

0

grep 是什么意思:

grep -R [sha] .git/refs/heads/

或者既然你是从 Python 脚本中调用它,为什么不创建一个函数来搜索这些文件,并返回你想要的分支名称或异常呢。


@jszakmeister 我认为你可以轻松地使用grep命令搜索.git/packed-refs,或者它被压缩了吗? - Emil Davtyan
不,packed-refs 没有被压缩。你可以使用 grep 命令搜索它,但是你的命令会漏掉这个文件。此外,你需要一个稍微不同的策略来提取匹配引用的名称(你不能像上面的例子那样使用文件名)。 - John Szakmeister
@jszakmeister 是的,这是可以理解的,你需要解析文件,但它是一个非常简单的文件。在Python脚本中解析OP提到的文件可能比运行命令更容易。 - Emil Davtyan
我不得不承认,我更喜欢使用 git 命令而不是裸命令并在 .git/ 中搜索。 - Christoph
深入了解git内部也会遇到未来的变化问题,例如当.git/packed-refs最终被压缩时。 :-) - torek

0
尝试像这样针对您给定的$sha测试每个分支端点哈希:
 git branch -a| cut -d " " -f 2,3 | xargs -n 1 git rev-parse | grep $sha

或者只是本地的

git branch | xargs -n 1 git rev-parse | grep $sha

0
我认为其中一个解决方案,可能不是非常优雅,但相当可靠:就是列出所有包含 $sha 的分支branch --contains $sha,然后检查git rev-parse $branch == $sha

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