Aside from my comment, the short answer is "no". One of the things I think is a small defect in Git is that each hook is just one thing:
- the pre-commit hook is
.git/hooks/pre-commit
- the update hook is
.git/hooks/update
and so on. But what if you, or your team or groups, have written several cool / useful / whatever pre-commit, prepare-commit-msg, etc., hooks and you would like to run all of them?
Git won't and can't do that. Indeed, there's no fully automatic solution that could work, because hooks might alter state, which means that running Hook A followed by Hook B would produce a different result from running Hook B followed by Hook A.
What you can do is write your hooks as useful but stand-alone scripts, then invoke each desired hook, in the desired order, from .git/hooks/name
. For instance, suppose you have a "check for leftover debug junk" pre-commit hook, and a separate "spaces vs tabs" pre-commit hook. (This example is not order-dependent, since I just made it up on the fly now and couldn't think of a better example.) Your .git/hooks/pre-commit
might then read:
#! /bin/sh
# run pre-commit checks
/home/shared/teamX/scripts/git-check-leftover-debug-junk || exit $?
/home/shared/teamY/scripts/git-check-spaces-vs-tabs || exit $?
(the final || exit $?
is not required, it's just there so we can add more hooks later).
There is a secondary problem here, though. For some hooks, we can just add "$@"
to pass arguments, and all is well. For the pre-receive
and post-receive
and pre-push
hooks, though, important data show up on standard input. This input comes from a pipe, so when one hook reads through it, the data have been consumed and are no longer available to the remaining hooks. The workaround is to copy the input to a file, then redirect each hook to read from that file. (It's necessary to restart the reads from offset 0 each time as well, so this is not as big as bug in Git as it seems: just offering the input as a temp file, instead of a pipe, does not suffice, you still must re-seek-to-zero each time.)
In a previous $job I once wrote a "master hook" that would run "sub-hooks", using the name it was invoked by to find all the sub-hooks. The sub-hooks came from a parallel set of directories with the same name-pattern as the hooks. The master hook would copy stdin to a temp file and pass on the arguments, so that each hook could act as though it were the only hook. This allowed us to add and remove hooks at whim, controlling the order through file names: multihook/pre-receive/10.foocheck
would run before multihook/pre-receive/20.barcheck
even though foo
sorts before bar
, because 10
sorts before 20
. It turns out that we didn't need that much fanciness after all, though: we never had more than three pre-receive hooks at a time and did not change them up frequently, at least while I was there.
(The code is pretty simple though. As a shell script, it's about six lines.)