如何从提交哈希值中获取git分支名称?

21

我有一个Bash脚本,接受一个字符串,可以是分支名称(例如“master”或“feature/foo”)或提交哈希(例如“1234abcd”)。

我已经检出了存储库,所以可以调用git。

最好的方法是确定该字符串是分支名称还是提交哈希?

#!/bin/bash
commit_or_branch="$1"
cd /path/to/my_repo
git fetch
if <is_branch $commit_or_branch>
then
    echo "it's a branch"
else
    echo "it's a commit"
fi

这对于这个问题来说真的很重要吗?我想为分支和提交做不同的事情。例如,如果它是一个分支,我可能想记录当前头部的实际提交哈希值。如果它是一个分支,我可能需要 git pull,而提交则不需要。如果它是一个提交,我可能想检查它属于哪些分支。还有其他许多事情。 - k107
4个回答

18
如果你想要一个强大的机制来告诉相对名称(例如,你的SHA1可能落后于一个命名分支一到多个提交),你可以使用 git name-rev 去解决它。
例子:
$ git config remote.upstream.url
https://github.com/RobotLocomotion/drake.git

$ git log --oneline -n 5
7530a95 Merge pull request #5743 from soonho-tri/pr-reformat-mathematical_program
ebc8f25 Suppresses console output of speed_bump.obj genrule. (#5726)
d8b9a0b Merge pull request #5735 from david-german-tri/namespaces
79e10e8 Remove redundant 'symbolic::' prefix from mathematical_program code
b68b590 Clean up mathematical_program code by adding using std::*

$ git name-rev HEAD
HEAD master
$ git name-rev 79e10e8
79e10e8 master^2
$ git name-rev HEAD~20
HEAD~20 remotes/origin/issue/5646_return_binding~3

参考: Git Tips (旧版本)

更新:如@kporter所提到的,还有一个--name-only标志(截至2020/04/21的新提交):

$ git name-rev HEAD
HEAD tags/last_sha_with_original_matlab~313
$ git name-rev --name-only HEAD~20
tags/last_sha_with_original_matlab~333

命令行参考:git name-rev


1
谢谢!这个命令完美地运行了:git name-rev $COMMIT_HASH | awk '{print $2}' - barakbd
5
你可以不使用awk命令:git name-rev --name-only $COMMIT_HASH - kporter
1
如何在仅当引用是命名对象的顶部时执行相同操作?因此,在上面的示例中,“79e10e8”和“HEAD〜20”应返回“未定义”或失败。 - 0andriy
@0andriy 看起来 git 只会找到最接近的命名对象并使用它?这是从上面检出 7530a95 的新克隆的输出:https://gist.github.com/EricCousineau-TRI/feaf5f8f1ef5e0f491454350fc73cc8b - Eric Cousineau

3
你可以使用git show-ref命令:
git show-ref --head | grep refs

如果为空,则它是一个SHA1(或无效对象,这不好)。 如果不为空,则它是一个分支名称。
更好的技术来自于 "确认提交是否存在",使用 git merge-base
分支名称将导致不同的字符串(SHA1)。
C:\Users\vonc\prog\b2d>git merge-base master master
de4accfd28c5f25fcc057d56996b83450be5dc60

一个SHA1将产生相同的结果(或者至少以相同的结果开头):
C:\Users\vonc\prog\b2d>git merge-base 03949c3d3f88a378c6a08e57daa97059b52813f1 03949c3d3f88a378c6a08e57daa97059b52813f1
03949c3d3f88a378c6a08e57daa97059b52813f1

foobar将会失败:

C:\Users\vonc\prog\b2d>git merge-base xxx xxx
fatal: Not a valid object name xxx

这句话的意思大概是:“那意味着类似于...”。
if [[ git merge-base $string $string ]]; then
  if [[ $(git merge-base $string $string) == $string* ]]; then
    echo "SHA1"
  else
    echo "branch"
  fi
else
  echo "Not a valid object name '$string'"
fi

1
@hek2mgl 是的,我已经相应地更新了过滤器。 - VonC
当我执行以下命令时:git checkout -b $(git rev-parse HEAD),会发生什么?点赞还是属于你的,但是嘿! :D - hek2mgl
这些都使用HEAD,它不是一个SHA。 - nullsteph
为什么使用 git show-ref --head --sha | grep -q ^argument 来确定参数是否为提交哈希值比使用 git merge-base 更糟糕?在哪些情况下? - Rihad
1
@Rihad 任何脚本都不应该尝试 grep 命令输出,而应该能够比较它们的文档结果,就像 git merge-base 一样。在脚本编写方面,这更加健壮。话虽如此,两者都可以工作。 - VonC

2

我认为您无法可靠地检查此内容,因为哈希值也可以作为有效的分支名称。请尝试以下方法:

git checkout -b 0c8158f47d7dda89226d4e816fee1fb9ac6c1204

这意味着可能存在一个名为该名称的分支和一个提交。由于在大多数git命令中可以传递分支名称或提交,在它们之间不需要区分。

1
值得注意的是,有些命令(例如 git rev-parse)将把此项视为原始 SHA-1,而其他命令(例如 git checkout)将把其视为分支名称。要强制将其视为分支名称,请在前面添加 refs/heads/git checkout 没有强制将其视为 SHA-1 的方法,但这也不需要,因为 git checkout --detach 将会从分支中分离出来,这是您想让 git checkout 将名称视为原始 SHA-1 的唯一情况。 - torek
@torek 我离 git 大师还很遥远,只是熟悉日常操作和一点点更多的知识。我的回答是支持还是反对你的评论?这可能是个愚蠢的问题。 - hek2mgl
不是反对,只是在扩展它 - 名称的确可能会有歧义,而且 git 并不能总是很好地消除歧义,但你是正确的,大多数情况下也没关系。在那些确实有关系的情况下,你必须采取类似于特别情况的方法,因此需要注释。 - torek

-1

正如@VonC和@hek2mgl所提到的,这可能不是一个二选一的测试。您可以稍微修改您的脚本,使其类似于以下内容(从this SO answer中借鉴):

#!/bin/bash
commit_or_branch="$1"
cd /path/to/my_repo
git fetch
if git branch | grep $commit_or_branch 2> /dev/null
then
    echo "it's a branch"
fi

if git cat-file -e $commit_or_branch 2> /dev/null
then
  echo "it's a commit"
fi

请注意,这仅测试本地分支...如果您对远程分支感兴趣,请参阅此帖子

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