问题
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
.)
回答1:
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
来源:https://stackoverflow.com/questions/61618486/git-pre-push-hook-run-test-on-each-new-commit