DEV Community

Kelly Stannard
Kelly Stannard

Posted on


This is my git-hook.d script. I will explain how to use it and run through what it does line by line.

#!/usr/bin/env bash

# Runs all executable hookname-* hooks and exits after,
# if any of them was not successful.
# Based on

hookname=$(basename $0)

if [ $DEBUG ]; then printf "$hookname\n"; fi

# Run each hook, passing through STDIN.
# 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 $PWD/.git/hooks/$hookname.d/*; do
  if [ $DEBUG ]; then printf "$hook\n"; fi
  test -x "$hook" || continue
  echo $hook
  $hook "$@" &

for pid in "${pids[@]}"; do
  wait $pid || exit $?

Enter fullscreen mode Exit fullscreen mode


  • copy the above, paste it in a file somewhere, make the file executable.
  • run: file_path=/PATH/TO/FILE
  • run: pushd .git/hooks; for f in *.sample; do ruby -e "base='$f'.sub('.sample', ''); puts %(mkdir #{base}.d && ln -s $file_path #{base})"; done | bash; popd

Now you can create hooks in individual files in any language. NOTE, this runs things in parallel. You may not want to use this with hooks for generating things, like prepare-commit-msg.


  • pids=() create an empty array of pids to keep track of subprocesses.
  • hookname=$(basename $0) set the name of the hook to the name of the file. In the installation I symlink the original executable, but $0 will be the path of the symlink so that is how we can differentiate between commit-msg.d and update.d.
  • for hook in $PWD/.git/hooks/$hookname.d/*; do Loop through every file under the appropriate hook directory.
  • test -x "$hook" || continue ignore files that aren't executable.
  • $hook "$@" & run the executable and forward the arguments passed to this handler. NOTE: hooks are run in parallel.
  • pids+=($!) add the pid to the list of pids.
  • for pid in "${pids[@]}"; do wait $pid || exit $? Wait for each subprocess to finish and bubble up any bad exit codes to git.
  • wait be extra sure that there are no hanging subprocesses.

Top comments (0)