[英]git pre-push hook: run test on each new commit
我想確保我推送的每個提交都通過測試。
我想在我的(客戶端)端檢查這一點,即在提交之前甚至推送(所以我不想依賴 CI 工具)。
目前,我已經實現了一個pre-commit
掛鈎來運行我的測試,因此我什至無法提交損壞的 state。
但是,我的測試套件需要幾秒鍾才能運行。 在編寫提交消息之前,我需要等待很多時間。 這使得日常使用變得不切實際; 既是因為我經常提交,而且我有時故意要提交一個損壞的 state 以便稍后壓扁(我知道git commit --no-verify
,但這不是重點)。
因此,我不想一次檢查每個提交(在創建時),而是想在推送之前對它們進行批量測試。
如何實現一個pre-push
鈎子,為每個要推送的新提交運行我的測試套件?
(為簡單起見,假設通過測試意味着test/run_tests.sh
返回0
。)
感謝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
從 ebosi 的回答中,我添加了一些您可能感興趣的修改:
echo "`git log -1 --oneline $commit`\n"
# 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
recompile_test()
{
echo "\n\033[32mRecompiling tests suite...\033[0m"
make test 2>&1 > /dev/null
# Retrieve exit code
is_test_passed=$?
if [ $is_test_passed -ne 0 ]
then
echo "\033[31m[\033[mKO\033[31m] Aborting push: Tests failed for commit $commit\n"\
"\t\033[31mCOULD NOT COMPILE TEST SUITE\n\033[0m"
break
fi
}
# 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 > /dev/null 2>&1
recompile_test;
echo "\n\033[32mRunning tests...\033[0m"
# Run the tests
./tester 2>&1 > ./tests_logs/last_test_output.log
# Retrieve exit code
is_test_passed=$?
# Stop iterating if error
if [ $is_test_passed -ne 0 ]
then
echo "\033[31m[\033[mKO\033[31m]\033[0m Aborting push: \033[31mTests failed for commit $commit\n\033[0m"
echo "commit brief:"
echo "\033[38;5;160m`git log -1 --oneline $commit`\033[0m\n"
echo "\t\033[31mERROR TRACE:\n\033[38;5;132m"
cat ./tests_logs/last_test_output.log
echo "\033[0m"
else
echo "\033[32m[\033[0mOK\033[32m] \033[0mTrying to push: \033[32mTests Passed for commit $commit\n\033[0m"
echo "commit brief:"
echo "`git log -1 --oneline $commit`\n"
fi
done
done
#added so it doesnt prevent us to checkout... we rm if the file exists
[ -f tests_logs/output.xml ] && rm tests_logs/output.xml
# Revert to pre-push state
git checkout $current_branch 2>&1 > /dev/null
STASH_NUM=$(git stash list | grep $STASH_NAME | sed -e 's/stash@{\(.*\)}.*/\1/')
if [ -n "$STASH_NUM" ]
then
git stash pop -q stash@{$STASH_NUM}
fi
if [ $is_test_passed -ne 0 ]
then
echo "\033[31m[\033[0mKO\033[31m] \033[0mPUSH ABORTED (failed on last commit)\n"
else
echo "\033[32m[\033[0mOK\033[32m] \033[0mPUSH POSSIBLE (final commit successful)\n"
fi
# Return exit code
exit $is_test_passed```
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.