如何在shell脚本中将当前git分支的名称存储到变量中?

126

我是shell脚本的新手,无法理解这个。如果您不熟悉,命令git branch会返回类似以下内容:

* develop
  master

星号代表当前所检出的分支。当我在终端中运行以下命令时:

git branch | grep "*"

我得到:

* develop

正如预期的那样。

但是,当我运行

test=$(git branch | grep "*")
或者
test=`git branch | grep "*"`

然后

echo $test

结果只是目录中文件的列表。我们如何使test="* develop"的值?

然后,下一步(一旦我们将“* develop”放入名为test的变量中)就是获取子字符串。那是否只需要以下内容?

currentBranch=${test:2} 
我玩了一下子字符串函数,但是经常遇到“bad substitution”错误,不知道为什么。

尝试阅读这篇博客文章:http://railstips.org/blog/archives/2009/02/02/bedazzle-your-bash-prompt-with-git-info/。 - Dominik Sandjaja
如果您在星号周围使用单引号'*'而不是双引号"*",会发生什么? - glenn jackman
@glenn,扩展没有发生在那里,正如Marco已经详细阐述的那样,在echo中发生。 - wich
6个回答

138

Noufal Ibrahim的答案的基础上展开,使用--short 标志与 git-symbolic-ref一起使用,无需烦恼于sed

我在钩子中一直在使用类似这样的东西,它运行良好:

#!/bin/bash

branch=$(git symbolic-ref --short HEAD)

echo
echo "**** Running post-commit hook from branch $branch"
echo

这将输出"**** Running post-commit hook from branch master"

请注意,git-symbolic-ref仅适用于您在存储库中的情况。幸运的是,.git/HEAD是Git早期版本遗留下来的内容,包含相同的符号引用。如果您想要获取多个git存储库的活动分支而不必遍历目录,则可以使用类似于以下的bash一行命令:

for repo in */.git; do branch=$(cat $repo/HEAD); echo ${repo%/.git} :  ${branch##*/}; done

输出结果大致如下:

repo1 : master  
repo2 : dev  
repo3 : issue12
如果你想深入了解,.git/HEAD 中包含的完整引用也是指向一个包含分支最后一次提交的SHA-1哈希值的文件的相对路径。

5
هˆ«هگچcurrentgitbranchè،¨ç¤؛git symbolic-ref --short HEADم€‚ - Pat
2
这是最好的答案,使用 --short 可以让你得到你要查找的字符串。谢谢! - 0atman

132

星号会被展开,你可以使用sed而不是grep,并立即获取分支名称的名称:

branch=$(git branch | sed -n -e 's/^\* \(.*\)/\1/p')

以下是 Noufal Ibrahim 建议使用 git symbolic-ref 的版本:

branch=$(git symbolic-ref HEAD | sed -e 's,.*/\(.*\),\1,')

关于展开的解释,(正如马可已经做过的那样),展开发生在echo中,当您使用$test包含* master时,*将按照正常展开规则进行展开。要抑制这个操作,必须对变量进行引用,就像马可所示:echo "$test"。或者,如果在echo之前去掉星号,一切都会没问题,例如:echo ${test:2}只会输出master。另外,您也可以像您已经提出的那样重新分配它:

branch=${test:2}
echo $branch
这将会输出 master,就像你想要的那样。

3
如果你在更新答案时使用了我的回答内容,提及一下我的回答会很礼貌。 - Noufal Ibrahim
当然,抱歉,我在离开之前快速更新了,错过了归属。 - wich
2
好的,我尝试了这两种方法,必须说symbolic-ref并不总是有效。例如:如果你检出一些旧提交,它会抛出错误,而sed命令则会给出类似于(HEAD detached at 1qw23er)的结果。 - zhirzh
3
请注意,即使分支名称中包含正斜杠(例如,因为它是问题URL),第一种方法仍然有效,而第二种方法在这种情况下只取最后一个片段。 - Aral Balkan
sed makes this even easier, actually!branch=$(git branch | sed '/^ /d;s/^\* //') - wilbur4321

38

我会使用git核心中的git-symbolic-ref命令。如果你输入git-symbolic-ref HEAD,你将会得到当前分支的名称。


15
在较新版本的 Git 中,你将需要使用 "git symbolic-ref HEAD" 来替代之前的方法。 - Jamey Hicks
6
你可以使用“--short”选项仅获取分支名称。例如:在主分支上,git-symbolic-ref HEAD 输出 refs/heads/master,但是 git-symbolic-ref --short HEAD 只输出 master - UTF_or_Death

23
我在我的 Git 辅助脚本中使用以下命令: git describe --contains --all HEAD 例如:
#!/bin/bash
branchname=$(git describe --contains --all HEAD)
git pull --rebase origin $branchname

我把它放在一个名为gpull的文件中,位于〜/scripts目录下。

编辑:

对于许多CI环境,它们会在“detached head”状态下检查您的代码,所以我会使用:

BRANCH=$(\
  git for-each-ref \
  --format='%(objectname) %(refname:short)' refs/heads \
  | awk "/^$(git rev-parse HEAD)/ {print \$2}"\
)

git describe --contains --all HEAD 无法像 git branch 显示的那样用星号突出当前分支。 - hakre
这在 Jenkins 构建脚本中非常有用,谢谢。 - nemesisdesign
1
注意:使用git describe命令时,如果当前HEAD指向一个标签,它将返回标签名称而不是分支名称。 - João Portela

8
问题出在:
echo $test

实际上,变量test包含一个通配符,该通配符由shell扩展。为了避免这种情况,只需用双引号保护$test:

echo "$test"

1

禁用子shell的通配符扩展

test=$(set -f; git branch)

1
扩展并不是发生在那里,正如Marco已经详细阐述的那样,它发生在回显中。 - wich

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