Last active
December 11, 2023 10:01
-
-
Save Konfekt/d9e86763b0f3febd7b2f7ca589f6c482 to your computer and use it in GitHub Desktop.
use global git hooks as fallback to local hooks
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# | |
# Adaption of https://github.com/majutsushi/etc/commit/e62904088c698e064c17522d54dff91b629ee253#diff-53b7e445a85984949f551c277d4cc4ee9682287cb234e075e6d352be887e7494 | |
# with https://github.com/pivotal-cf/git-hooks-core/blob/master/.base-hook | |
# | |
# This script is meant to be put into a directory pointed to by core.hooksPath | |
# in Git 2.9. | |
# Then for each hook you want to support, create a symlink "hookname -> multihook" | |
# and optionally a directory "hookname.d" where you can put all scripts for | |
# that hook | |
# | |
# This script linked to HOOKNAME in core.hooksPath executes, | |
# in ascending order of priority: | |
# | |
# - ./HOOKNAME.d/* | |
# - $GIT_CUSTOM_HOOKS_DIR/HOOKNAME.d/* | |
# - $GIT_DIR/hooks/HOOKNAME.d/* | |
# - $GIT_DIR/hooks/HOOKNAME | |
# | |
# The scripts found in those directories will be merged and | |
# executed in alphabetic order, with hooks in higher-priority directories | |
# overriding hooks in lower-priority directories. | |
set -eEu -o pipefail | |
shopt -s inherit_errexit | |
# optionally debug output by supplying TRACE=1 | |
[[ "${TRACE:-0}" == "1" ]] && set -o xtrace | |
IFS=$'\n\t' | |
PS4='+\t ' | |
error_handler() { echo "Error: In ${BASH_SOURCE[0]} Line ${1} exited with Status ${2}"; } | |
trap 'error_handler ${LINENO} $?' ERR | |
STDIN=$(cat) | |
[[ -n "${GIT_DIR:-}" ]] && git_dir="$GIT_DIR" | |
unset $(git rev-parse --local-env-vars || true) | |
GIT_DIR="$(git rev-parse --git-dir 2>/dev/null)" || GIT_DIR="$git_dir" | |
export GIT_DIR | |
[[ -n "${git_dir:-}" ]] && unset git_dir | |
# Array of the supported hook directories in ascending order of priority | |
declare -a HOOKDIRS | |
HOOKDIRS+=("$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)") | |
[[ -n "${GIT_CUSTOM_HOOKS_DIR:-}" ]] && HOOKDIRS+=("$GIT_CUSTOM_HOOKS_DIR") | |
HOOKDIRS+=("$GIT_DIR/hooks") | |
# The Git name of the hook to execute | |
HOOKNAME=$(basename "${BASH_SOURCE[0]}") | |
# run first hooks in $GIT_DIR as usual | |
# similar to https://github.com/pivotal-cf/git-hooks-core/blob/master/.base-hook | |
hookscript="${GIT_DIR}/hooks/${HOOKNAME}" | |
if [[ -f "$hookscript" && -x "$hookscript" ]]; then | |
# echo "##### GITHOOK: ${hookscript}" | |
echo "$STDIN" | "$hookscript" "$@" || exit $? | |
fi | |
# Associative array of the hook scripts to run | |
# - Key is the basename of the file | |
# - Value is the full path to the file | |
declare -A TO_RUN | |
# Assemble the array of scripts to run. Since the keys are the basenames of | |
# the scripts, scripts with the same name in higher-priority directories will | |
# override scripts in lower-priority directories. This allows replacing or | |
# disabling standard hooks without explicit support from the scripts. | |
for dir in "${HOOKDIRS[@]}"; do | |
hookdir="${dir}/${HOOKNAME}.d" | |
if [[ -d "$hookdir" ]]; then | |
for hook in "$hookdir"/*; do | |
hookname="$(basename "$hook")" | |
TO_RUN[$hookname]="$hook" | |
done | |
fi | |
done | |
# Iterate over the script array in alphabetic order, running all the scripts | |
# that have the executable bit set. | |
for key in "${!TO_RUN[@]}"; do | |
echo "$key" | |
done | sort | while read -r hookname; do | |
hookscript="${TO_RUN["$hookname"]}" | |
if [[ -f "$hookscript" && -x "$hookscript" ]]; then | |
# echo "##### GITHOOK: ${hookscript}" | |
echo "$STDIN" | "$hookscript" "$@" || exit $? | |
fi | |
done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment