Reputation: 51545
As many of you probably know, there can be only one hook type in git. If two update hooks need to be evaluated. The git admin is left with two unmanageable solutions:
exec
I am looking for an elegant solution (written in BASH),something like a folder hooks/update.d
or hooks/post-receive.d
that will allow the loosely coupling of hook evaluations. The chaining should stop as soon as a hook fails.
I actually found an acceptable solution written in perl at this URL http://blog.bluefeet.net/2011/08/chained-git-hooks
The problem: my server runs different versions of perl and I am getting perllib version mismatches. It fails.
Upvotes: 33
Views: 5173
Reputation: 7769
I created a shell script based on OP and Olivier Refalo posts (with some modifications):
https://gist.github.com/amirbawab/e9f42ef8d441316707d9b90777e5718b
The script will generate a hook files that will execute scripts inside $hook_file_name.d/$hook_file_name.*
For example: for commit-msg
hook, the script will generate commit-msg
file under .git/hooks
that will execute all script under commit-msg.d/
.
Files under commit-msg.d
should match the pattern commit-msg.*
to allow placing helper shell files in those folder without executing them.
Script usage: ./extend_git_hooks.sh [REPO_PATH]
Upvotes: 0
Reputation: 30951
For those who're not willing to click on every link in comments below other answer, here's a practically unmodified version of the script by @HenrikN:
#!/bin/bash
# Runs all executable hookname-* hooks and exits after,
# if any of them was not successful.
#
# Based on
# http://osdir.com/ml/git/2009-01/msg00308.html
data=$(cat)
exitcodes=()
hookname=$(basename $0)
# Run each hook, passing through STDIN and storing the exit code.
# We don't want to bail at the first failure, as the user might
# then bypass the hooks without knowing about additional issues.
for hook in $GIT_DIR/hooks/$hookname-*; do
test -x "$hook" || continue
echo "$data" | "$hook"
exitcodes+=($?)
done
# If any exit code isn't 0, bail.
for i in "${exitcodes[@]}"; do
[ "$i" == 0 ] || exit $i
done
Upvotes: 3
Reputation: 51545
After further investigation and testing, here is a working solution:
create file .git/hooks/hook-chain
as follows
#!/bin/bash
#
# author: orefalo
hookname=`basename $0`
FILE=`mktemp`
trap 'rm -f $FILE' EXIT
cat - > $FILE
for hook in $GIT_DIR/hooks/$hookname.*
do
if test -x "$hook"; then
# echo $hook
cat $FILE | $hook "$@"
status=$?
if test $status -ne 0; then
echo Hook $hook failed with error code $status
exit $status
fi
fi
done
Now link any hook that requires chaining, for instance
finally, create the chains by renaming them as hookname
.action
-rwxr-xr-x. 1 git git 6710 functions
-rwxr-xr-x. 1 git git 280 hook-chain
-rwxr-xr-x. 1 git git 1524 post-mirror
lrwxrwxrwx. 1 root root 10 post-receive -> hook-chain
-rwxr-xr-x. 1 git git 8763 post-receive.1email
-rwxr-xr-x. 1 git git 1745 post-receive.2github
-rwxr-xr-x. 1 git git 473 post-upload-pack
-rwxr-xr-x. 1 git git 346 pre-receive
lrwxrwxrwx. 1 root root 10 update -> hook-chain
-rwxr-xr-x. 1 git git 2975 update.1acl
-rwxr-xr-x. 1 git git 328 update.2github
for instance, in the sample above, the update hook will run update.1acl followed by update.2github.
The post-receive hook with run post-receive.1email followed by post-receive.2github
Upvotes: 33