Git 预推钩子

Posted

技术标签:

【中文标题】Git 预推钩子【英文标题】:Git pre-push hooks 【发布时间】:2011-05-10 21:59:23 【问题描述】:

我想在每次 git push 之前运行一个单元测试,如果测试失败,取消推送,但我什至找不到 pre-push 钩子,只有 pre-commit 和 pre-rebase。

【问题讨论】:

相关:***.com/questions/31681746 【参考方案1】:

Git 在 1.8.2 版本中获得了 pre-push 钩子。

Pre-push 钩子是我需要的,还有 pre-commit 钩子。除了保护分支之外,它们还可以结合预提交挂钩提供额外的安全性。

还有一个关于如何使用的例子(取自this nice entry)

登录 vagrant、运行测试然后推送的简单示例

#!/bin/bash
# Run the following command in the root of your project to install this pre-push hook:
# cp git-hooks/pre-push .git/hooks/pre-push; chmod 700 .git/hooks/pre-push

CMD="ssh vagrant@192.168.33.10 -i ~/.vagrant.d/insecure_private_key 'cd /vagrant/tests; /vagrant/vendor/bin/phpunit'"
protected_branch='master'

# Check if we actually have commits to push
commits=`git log @u..`
if [ -z "$commits" ]; then
    exit 0
fi

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

if [[ $current_branch = $protected_branch ]]; then
    eval $CMD
    RESULT=$?
    if [ $RESULT -ne 0 ]; then
        echo "failed $CMD"
        exit 1
    fi
fi
exit 0

如您所见,该示例使用了一个受保护的分支,即 pre-push 挂钩的主题。

【讨论】:

什么是 RESULT=$?在 bash 中做什么? @Jwan622 它提供了最后一次操作的结果。 #false #echo $? 1 #true #echo $? 0 # 【参考方案2】:

为了记录,有一个patch to Git 1.6 that adds a pre-push hook。我不知道它是否适用于 1.7。

您可以像@kubi 推荐的那样运行推送脚本,而不是搞砸。您也可以将其改为 Rake 任务,以便它在您的存储库中。 ruby-git 可以帮助解决这个问题。如果您检查目标 repo,则只能在推送到生产 repo 时运行测试。

最后,您可以在 pre-commit 挂钩中运行您的测试,但检查正在提交的分支。然后你可以有一个 production 分支,它需要在接受提交之前通过所有测试,但你的 master 不在乎。 limerick_rake 在这种情况下可能很有用。

【讨论】:

谢谢,实际上我已经选择了最后一个变体(最后,您可以在 pre-commit 挂钩中运行测试......)【参考方案3】:

script linked by the highly-voted answer 显示pre-push hook 的参数等($1 是远程名称,$2 URL)以及如何访问提交(来自标准输入的行read 具有结构<local ref> <local sha1> <remote ref> <remote sha1>

#!/bin/sh

# An example hook script to verify what is about to be pushed.  Called by "git
# push" after it has checked the remote status, but before anything has been
# pushed.  If this script exits with a non-zero status nothing will be pushed.
#
# This hook is called with the following parameters:
#
# $1 -- Name of the remote to which the push is being done
# $2 -- URL to which the push is being done
#
# If pushing without using a named remote those arguments will be equal.
#
# Information about the commits which are being pushed is supplied as lines to
# the standard input in the form:
#
#   <local ref> <local sha1> <remote ref> <remote sha1>
#
# This sample shows how to prevent push of commits where the log message starts
# with "WIP" (work in progress).

remote="$1"
url="$2"

z40=0000000000000000000000000000000000000000

while read local_ref local_sha remote_ref remote_sha
do
    if [ "$local_sha" = $z40 ]
    then
        # Handle delete
        :
    else
        if [ "$remote_sha" = $z40 ]
        then
            # New branch, examine all commits
            range="$local_sha"
        else
            # Update to existing branch, examine new commits
            range="$remote_sha..$local_sha"
        fi

        # Check for WIP commit
        commit=`git rev-list -n 1 --grep '^WIP' "$range"`
        if [ -n "$commit" ]
        then
            echo >&2 "Found WIP commit in $local_ref, not pushing"
            exit 1
        fi
    fi
done

exit 0

【讨论】:

【参考方案4】:

如果您使用命令行,最简单的方法是编写一个推送脚本来运行您的单元测试,如果测试成功,则完成推送。

编辑

从 git 1.8.2 开始,这个答案已经过时了。请参阅上面 manojlds 的回答。

【讨论】:

你的意思是根本不使用钩子吗?只需将“git pull”替换为“git uinttestspull”?这不是我所需要的 @sheepwalker: s/pull/push/,并使用别名使其美观而简短。 @sheepwalker 是的,这不是你所要求的,但就像@calmh 所说,没有预推挂钩。【参考方案5】:

Git 在1.8.2 版本中获得了pre-push 挂钩。

示例pre-push 脚本:https://github.com/git/git/blob/87c86dd14abe8db7d00b0df5661ef8cf147a72a3/templates/hooks--pre-push.sample

1.8.2 发布说明谈论新的 pre-push hook:https://github.com/git/git/blob/master/Documentation/RelNotes/1.8.2.txt

【讨论】:

@manojlds 你知道这个钩子是用来做什么的吗?我想在推送到特定分支时使用它来将我的二进制文件推送给我的客户(即在推送之前构建每晚版本并使用 curl 上传)。问题是构建和上传需要一段时间,并且远程关闭连接。所以我最终构建了我的二进制文件并上传给了客户,但没有推送到仓库,因为远程仓库关闭了连接。知道如何解决这个问题吗?或者它的根源可能是一个坏主意。 @igrek 你找到解决连接关闭问题的方法了吗? @MarioEstrada,是的,我不记得具体是怎么做的,但我让它推送了两次:第一个 git 命令运行单元测试,然后如果它没有断开连接,它会推送并开始另一个推送线程,如果第一个推送超时,来自另一个线程的第二个推送对我有用。如果第一个和第二个成功,则第一个推送更改,第二个不推送任何内容。诀窍是我添加了一些参数,它绕过了单元测试(用于第二次 git push,所以它没有再次开始单元测试)【参考方案6】:

我宁愿在 pre-commit-hook 中运行测试。因为提交时已经记录了更改。推送和拉取仅交换有关已记录更改的信息。如果测试失败,您的存储库中已经有一个“损坏”的修订。不管你是不是在推动它。

【讨论】:

我大体上同意,但如果您习惯于稍后进行大量增量提交以进行压缩,并且测试套件很大,那么这可能是不切实际的。 投了反对票,因为 - 虽然信息丰富 - 它完全忽略了 OP 的问题。 @calder.ty - 不。 manojlds 更好地解决了重要问题。事实上,运行测试的预提交钩子通常是个坏主意。它假定所有提交的东西都必须通过测试。这对于专注于协作的常见工作流程来说是不利的。所以是的……我不同意;它不是做“它”的更好方法,也没有解决问题。 这不是问题的答案。这只是个人意见,因此不属于答案 如果响应者希望简单地分享他们的意见,则适当的响应是 1) 回复评论,或 2) 按预期回答问题,然后在其下方提供他们的个人意见以更好可见性。【参考方案7】:

它没有钩子,因为推送不是修改存储库的操作。

您可以在接收方进行检查,在 post-receive 挂钩中。这就是您通常会拒绝传入推送的地方。在 hook 中运行单元测试可能有点密集,但这取决于你。

【讨论】:

以上是关于Git 预推钩子的主要内容,如果未能解决你的问题,请参考以下文章

三分钟教你学Git(十七) - 钩子

git钩子

如何删除远程 git 钩子?

在Gitlab中防止单个repo的链式git钩子

git-svn 的钩子

用 git 钩子,检测代码规范性(eslintstandard)