Git pre-push hooks

GitHook

Git Problem Overview


I would like to run a unit-tests before every git push and if tests fails, cancel the push, but I can't even find pre-push hook, there is pre-commit and pre-rebase only.

Git Solutions


Solution 1 - Git

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

Solution 2 - Git

> 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 [email protected] -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.

Solution 3 - Git

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.

Solution 4 - Git

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.

Solution 5 - Git

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.

Solution 6 - Git

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.

Solution 7 - Git

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionsheepwalkerView Question on Stackoverflow
Solution 1 - GitmanojldsView Answer on Stackoverflow
Solution 2 - GitJimmy KaneView Answer on Stackoverflow
Solution 3 - GitkubiView Answer on Stackoverflow
Solution 4 - GitJakob BorgView Answer on Stackoverflow
Solution 5 - GitTuradgView Answer on Stackoverflow
Solution 6 - GitordnungswidrigView Answer on Stackoverflow
Solution 7 - Gitserv-incView Answer on Stackoverflow