git pre-push hook:在每个新提交上运行测试

ebo*_*osi 14 git git-push githooks

语境

我想确保我推送的每个提交都通过测试。

我想在我的(客户端)端检查这一点,即在提交之前进行检查(所以我不想依赖 CI 工具)。

问题

目前,我已经实现了一个pre-commit运行测试的钩子,因此我什至无法提交损坏的状态。

然而,我的测试套件运行需要花费几秒钟的时间。在编写提交消息之前我需要等待很多时间。这使得日常使用变得不切实际;两者都是因为我经常提交,而且我有时故意想要提交一个损坏的状态以供稍后粉碎(我知道git commit --no-verify,但这不是重点)。

问题

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

如何实现一个钩子来为每个要推送的新提交pre-push运行我的测试套件?

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

ebo*_*osi 3

感谢 的phd提示(在评论中)和对 git 自己示例的无耻掠夺,我起草了以下./.git/hooks/pre-push钩子(我事先注意到了chmod +x)。

它似乎在普通情况下完成了工作,我们将看看随着时间的推移它会如何进行。无论如何,欢迎改进!

#!/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
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
#removed fi

# Return exit code
exit $is_test_passed
Run Code Online (Sandbox Code Playgroud)

  • 主要是好的。我改进了一些小的 shell 和 git 习惯用法:使用 `continue` 而不是 `exit`,使用 `elif` 并避免过多缩进,使用 `git rev-parse` 获取当前分支,直接使用 `$?` 而不是`回声$?`。 (3认同)