Git pre-push hooks
I would like to run a unit-tests before every git push and i开发者_开发百科f tests fails, cancel the push, but I can't even find pre-push hook, there is pre-commit and pre-rebase only.
Git got the pre-push
hook in the 1.8.2
release.
Sample pre-push
script: https://github.com/git/git/blob/87c86dd14abe8db7d00b0df5661ef8cf147a72a3/templates/hooks--pre-push.sample
1.8.2 release notes talking about the new pre-push hook: https://github.com/git/git/blob/master/Documentation/RelNotes/1.8.2.txt
Git got the pre-push hook in the 1.8.2 release.
Pre-push hooks are what I needed along with pre-commit hooks. Apart from protecting a branch, they can also provide extra security combined with pre-commit hooks.
And for an example on how to use (taken and adopted and enhanced from this nice entry)
Simple example to login to vagrant, run tests and then push
#!/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
As you can see the example uses a protected branch, subject of the pre-push hook.
If you are using the command line, the easiest way to do this is to write a push script that runs your unit tests and, if they succeed, completes the push.
Edit
As of git 1.8.2 this answer is outdated. See manojlds's answer above.
There isn't a hook for it, because a push isn't an operation that modifies your repository.
You can do the checks on the receiving side though, in the post-receive
hook. That is where you would usually reject an incoming push. Running unit tests might be a little intensive to do in a hook, but that's up to you.
For the record, there is a patch to Git 1.6 that adds a pre-push hook. I don't know whether it works against 1.7.
Rather than mess with that, you could run push script like @kubi recommended. You could also make it a Rake task instead so it's in your repo. ruby-git could help with this. If you check the target repo, you could run tests only when pushing to the production repo.
Finally, you could run your tests in your pre-commit
hook but check for what branch is being committed to. Then you could have a, say, a production
branch that requires all tests pass before accepting a commit but your master
doesn't care. limerick_rake may be useful in that scenario.
I would rather run the test in a pre-commit-hook. Because the change is already recorded when committing. Push and pull only exchange information about already recorded changed. If a test fails you would already have a "broken" revision in your repository. Whether you're pushing it or not.
The script linked by the highly-voted answer shows the parameters etc to the pre-push
hook ($1
is remote name, $2
URL) and how to access the commits (lines read
from stdin have structure <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
精彩评论