在 pre-push 钩子中,如何获取 "git push" 命令的完整内容?

8
当Git的pre-push钩子脚本在工作时,如何获取完整的git push命令内容呢?例如,当我运行git push origin master时,将会执行pre-push钩子。我想在这个钩子中获取originmaster,该怎么做呢? 我应该如何获取参数列表呢?
2个回答

7

简短概述:您需要一个while循环

您的钩子脚本(假设是sh/bash)应该包含以下形式的循环:

while read localname localhash remotename remotehash; do
    ... code here using $localname etc ...
done

描述

所有Git钩子的说明都在githooks页面中。 pre-push钩子的说明如下:

This hook is called by git push and can be used to prevent a push from taking place. The hook is called with two parameters which provide the name and location of the destination remote, if a named remote is not being used both values will be the same.

Information about what is to be pushed is provided on the hook’s standard input with lines of the form:

<local ref> SP <local sha1> SP <remote ref> SP <remote sha1> LF

For instance, if the command git push origin master:foreign were run the hook would receive a line like the following:

refs/heads/master 67890 refs/heads/foreign 12345

although the full, 40-character SHA-1s would be supplied. ...

第一个段落的意思是,在一个shell脚本中,$1$2分别代表远程仓库的名称(例如origin)以及其URL,或者如果用户运行了$ git remote add <name> <url>命令,$1$2则表示URL重复两次。

git push https://some.host.name/some/path ...

第二段是重要的内容。 git push 命令可以推送一个以上的分支。例如,我可以运行:

git push origin feature-A feature-B

需要同时推送feature-Afeature-B。必须逐行阅读所有输入行,以发现要推送的内容。仓库中的当前分支并不重要:读取HEAD将得到错误的答案,除非用户恰好正在推送当前分支。另一方面,大多数用户大多只推送当前分支。这会让你产生你的钩子是100%可靠的错觉,实际上它只有92.37%的可靠性。1

正如文档所述,钩子获取每个引用的完整名称。如果你在推送一个分支,那么该完整名称以refs/heads/开头,但你也可以推送一个标记,在这种情况下,完整名称以refs/tags/开头。为编写可靠的钩子,你必须检查完整名称,而不仅仅是删除前两个组件。2


1像38.61%的统计数据一样,这个是临时捏造的。:-)

2有很多糟糕的示例钩子(不是所有都是预提交钩子),它们使用了:

branch=$(echo $ref | cut -d/ -f3)

如果你正在推送标签v2.3,这个钩子会认为你正在推送一个名为v2.3分支。如果你正在推送名为bugfix/1234的分支,它会认为你正在推送一个名为bugfix的分支!使用cut技术是错误的,但后者的快速解决方法是使用-f3-,至少可以生成bugfix/1234。最好验证引用的前几个组件,例如:

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

一旦你知道前缀,你可以使用${ref#refs/heads/}${ref#refs/tags/}来去掉已知的前缀并获取完整但未限定的分支或标签名称。然而,在许多情况下,你可以直接使用完整的引用名称。


-1

这些参数被传递到钩子中

在钩子中,您可以通过以下方式获取分支名称:

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

如果您希望在终端中查看这些设置而不是作为脚本的一部分,请使用以下命令:
# get the name of the tracking branches
git branch -a -vv

# or: 
git status -b --porcelain

# to get the name of the current branch:
git symbolic-ref -q HEAD

如何绕过pre-push钩子?

git push --no-verify

谢谢CodeWizard,我也想获取分支信息,例如:“master”,但是$1和$2都不是。如何获取分支信息? - Shaun
1
我更喜欢使用 git status -b --short 而不是 git status -b --porcelain。如果启用了 color.status 配置(默认),它以更易读的方式提供相同的信息。porcelain 选项默认禁用颜色。 - clickMe
同意这里 :-) - CodeWizard
@SebTu:--porcelain 用于脚本,其中颜色会妨碍脚本解释输出。--short 用于人类消费,其中颜色很有用,除非你是色盲 :-) - torek
如果分支名称包含“/”,则您的sed命令返回错误结果。最好使用git rev-parse --abbrev-ref HEAD直接获取当前分支名称(不带“refs/heads”前缀),避免进行此类操作。 - Mark Amery

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