git pre-push hook:在每个新提交上运行测试 [英] git pre-push hook: run test on each new commit

查看:207
本文介绍了git pre-push hook:在每个新提交上运行测试的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

上下文

我想确保我进行的每个提交都通过测试.

我想在我的(客户端)端进行检查,即在提交提交之前(因此,我不想依赖CI工具).

问题

当前,我已经实现了一个 pre-commit 钩子来运行我的测试,因此我什至无法 commit 处于损坏状态.

但是,我的测试套件需要花费几秒钟的时间才能运行.在写入提交消息之前,我需要等待很多时间.这使得每天不切实际使用;两者都是因为我经常提交,有时我有意要提交一个破碎的状态以供以后使用(我知道 git commit --no-verify ,但这不是重点).

问题

因此,我不想一次检查每个提交(在创建时)(在创建时),而是想在推送之前对它们进行批处理测试.

如何实现一个 pre-push 挂钩,该挂钩在每次要提交的新提交中运行我的测试套件?

(为简单起见,说通过测试意味着 test/run_tests.sh 返回 0 .)

解决方案

感谢 phd 的提示(评论)和git自己的例子的无耻窃取,我起草了以下 ./.git/hooks/pre-push 钩子(我对 chmod + x小心翼翼).

这似乎在普通情况下就可以完成,我们将看看它随着时间的流逝如何.无论如何,欢迎改进!

 <代码>#!/usr/bin/sh#一个钩子脚本示例,用于验证即将被推送的每个提交#通过`./run_tests`套件.在检查完#远程状态,但未推送任何内容.#如果测试套件(以及脚本)以非零状态退出,则没有任何反应#将被推送.##在任何情况下,我们都将恢复到之前的$ git push状态.#检索参数remote ="$ 1"url ="$ 2"z40 = 0000000000000000000000000000000000000000#不存在的提交的SHA#保存当前的"git状态"current_branch = $(git rev-parse --abbrev-ref HEAD)STASH_NAME ="pre-push-$(date +%s)"git stash save -q --keep-index $ STASH_NAME#做奇迹同时读取local_ref local_sha remote_ref remote_sha做如果["$ local_sha" = $ z40]然后#处理删除继续#到下一个分支elif ["$ remote_sha" = $ z40]然后#新分支,检查所有提交range ="$ local_sha"别的#更新到现有分支,检查新提交range ="$ remote_sha .. $ local_sha"科幻#按时间顺序"检索提交列表commits = $(git rev-list --reverse $ range)#遍历每个提交在$ commits中提交做git checkout $ commit#运行测试./test/run_tests.sh#检索退出代码is_test_passed = $?#如果出错则停止迭代如果[$ is_test_passed -ne 0]然后echo -e正在中止推送:提交$ commit的测试失败," \具有以下错误跟踪:\ n"#像这样:tail test/run_tests.log休息2科幻完毕科幻完毕#恢复到预推状态git checkout $ current_branchSTASH_NUM = $(git存储列表| grep $ STASH_NAME | sed -re's/stash @ \ {(.*)\}.*/\ 1/')如果[-n"$ STASH_NUM"]然后git stash pop -q stash @ {$ STASH_NUM}科幻#返回退出代码退出$ is_test_passed 

Context

I want to ensure that each commit I push pass tests.

I want to check this on my (client) side, i.e. before commits are even pushed (so I don't want to rely on CI tools).

Problem

Currently, I have implemented a pre-commit hook that run my tests, so that I cannot even commit a broken state.

However, my test suite takes more than a few seconds to run. It is that much time I need to wait prior to writing my commit message. This makes it impractical to use on a daily basis; both because I frequently commit, and that I sometimes purposefully want to commit a broken state to be squashed later (I know about git commit --no-verify, but that is not the point).

Question

So instead of checking each commit one at a time (at creation), I want to batch-test them before pushing.

How to implement a pre-push hook that run my test suite for each new commit to be pushed?

(For the sake of simplicity, say that passing tests means test/run_tests.sh returns 0.)

解决方案

Thanks to phd's hint (in comments) and a shameless pillage of git's own example, I have drafted the following ./.git/hooks/pre-push hook (that I took care to chmod +x beforehand).

It seems to do the job in vanilla situation, we'll see how it goes over time. Anyway, improvements are welcome!

#!/usr/bin/sh

# An example hook script to verify that each commit that is about to be pushed
# pass the `./run_tests` suite. Called by "git push" after it has checked the
# remote status, but before anything has been pushed.
# If the test suite (and so the script) exits with a non-zero status, nothing
# will be pushed.
#
# In any case, we revert to the pre `$ git push` state.


# Retrieve arguments
remote="$1"
url="$2"

z40=0000000000000000000000000000000000000000 # SHA of a non existing commit


# Save current "git state"
current_branch=$(git rev-parse --abbrev-ref HEAD)

STASH_NAME="pre-push-$(date +%s)"
git stash save -q --keep-index $STASH_NAME


# Do wonders
while read local_ref local_sha remote_ref remote_sha
do
        if [ "$local_sha" = $z40 ]
        then
                # Handle delete
                continue # to the next branch
        elif [ "$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

        # Retrieve list of commit in "chronological" order
        commits=$(git rev-list --reverse $range)

        # Loop over each commit
        for commit in $commits
        do
            git checkout $commit

            # Run the tests
            ./test/run_tests.sh

            # Retrieve exit code
            is_test_passed=$?

            # Stop iterating if error
            if [ $is_test_passed -ne 0 ]
            then
                echo -e "Aborting push: Test failed for commit $commit,"\
                  "with following error trace:\n"
                # something like: tail test/run_tests.log
                break 2
            fi
        done

        fi
done


# Revert to pre-push state
git checkout $current_branch

STASH_NUM=$(git stash list | grep $STASH_NAME | sed -re 's/stash@\{(.*)\}.*/\1/')
if [ -n "$STASH_NUM" ]
then
    git stash pop -q stash@{$STASH_NUM}
fi


# Return exit code
exit $is_test_passed

这篇关于git pre-push hook:在每个新提交上运行测试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆