在pre-push hook中,获取"git push"命令的完整内容?

Sha*_*aun 6 git shell git-push githooks

当Git pre-push钩子脚本工作时,如何获取完整的git push命令内容?

例如,当我运行:git push origin master,执行预推钩.
我想originmaster这个挂钩.

我如何获得参数列表?

tor*_*rek 6

TL; DR摘要:你需要一个while循环

你的钩子脚本(假设sh/bash)应该包含一个形式的循环:

while read localname localhash remotename remotehash; do
    ... code here using $localname etc ...
done
Run Code Online (Sandbox Code Playgroud)

描述

所有的Git钩子都在githooks页面中描述.所述pre-push钩描述开始于:

这个钩子由git push调用,可用于防止发生推送.使用两个参数调用钩子,这两个参数提供目标远程的名称和位置,如果未使用命名远程,则两个值将相同.

有关要推送内容的信息在钩子的标准输入上提供了以下形式的行:

<local ref> SP <local sha1> SP <remote ref> SP <remote sha1> LF
Run Code Online (Sandbox Code Playgroud)

例如,如果命令git push origin master:foreign运行,钩子会收到如下所示的行:

refs/heads/master 67890 refs/heads/foreign 12345
Run Code Online (Sandbox Code Playgroud)

虽然将提供完整的40个字符的SHA-1....

第一段意味着在shell脚本中,$1并且$2是远程的名称 - 例如,origin- 及其URL,或者如果用户运行,则重复两次URL:

git push https://some.host.name/some/path ...
Run Code Online (Sandbox Code Playgroud)

第二段落是重要的.一个git push命令可以把多个分支.例如,我可以运行:

git push origin feature-A feature-B
Run Code Online (Sandbox Code Playgroud)

推动两者feature-A feature-B.您必须一次读取所有输入行,以发现要推送的内容.存储库中的当前分支并不重要:除非用户碰巧正在推送当前分支,否则读取HEAD将给出错误的答案.另一方面,大多数用户大多只是推动当前分支.这会让你觉得你的钩子100%可靠,当它实际上只有92.37%可靠时.1

如文档所述,钩子获取每个引用的全名.如果你正在推动一个分支,那个全名开头refs/heads/,但你可以推送一个标签,在这种情况下,全名开头refs/tags/.要编写可靠的钩子,您必须检查全名,而不是简单地剥离前两个组件.2


1像38.61%的统计数据一样,这一个是当场编制的.:-)

2有许多不良的样品钩(不是所有的预推钩)都使用:

branch=$(echo $ref | cut -d/ -f3)
Run Code Online (Sandbox Code Playgroud)

如果你正在推动标签v2.3,这个钩子会认为你正在推动一个名为的分支v2.3.如果你正在推动一个名为的分支bugfix/1234,它会认为你正在推动一个名为的分支bugfix!这种cut技术是错误的,但后者的快速修复是使用-f3-,至少产生bugfix/1234.最好验证ref的第一个组件,即执行以下操作:

case $ref in
refs/heads/*) ... it's a branch ...;;
refs/tags/*) ... it's a tag ...;;
*) ... it's something else entirely, such as refs/notes/* ...;;
esac
Run Code Online (Sandbox Code Playgroud)

一旦知道前缀,就可以使用${ref#refs/heads/}${ref#refs/tags/}删除已知前缀并获取完整但不合格的分支或标记名称.但是,如果遇到很多情况,您可以直接使用完整的参考名称.