aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/dot_config/git/hooks/executable_pre-push
diff options
context:
space:
mode:
authorLibravatar sommerfeld <sommerfeld@sommerfeld.dev>2026-05-19 16:45:17 +0100
committerLibravatar sommerfeld <sommerfeld@sommerfeld.dev>2026-05-19 16:45:17 +0100
commit67868f51bbab5bc3ef5c8ba15433ba401a297f1a (patch)
treea349eb49a8ab859dd02ed7a73e793a580da53475 /dot_config/git/hooks/executable_pre-push
parent1f6dc84f68b4631e77ebc11a452cb0b03eecde57 (diff)
downloaddotfiles-67868f51bbab5bc3ef5c8ba15433ba401a297f1a.tar.gz
dotfiles-67868f51bbab5bc3ef5c8ba15433ba401a297f1a.tar.bz2
dotfiles-67868f51bbab5bc3ef5c8ba15433ba401a297f1a.zip
feat(git): user-level hooks auto-dispatch into <repo>/.githooks/
Inverts the hook delegation model. Previously per-repo hooks required a project to either (a) write the entire hook themselves and lose the global signed-commit / agent-author gate, or (b) override core.hooksPath and write passthrough stubs that exec back to $HOME/.config/git/hooks/*. Both are ergonomically miserable. Now: the global hooks at ~/.config/git/hooks/ are *always* the entry point. Each one calls a shared dispatcher (_dispatch.sh) that runs <repo>/.githooks/<hookname> if it exists, propagating its exit status, and then continues with whatever the global hook itself wants to do. Projects just drop an executable file at .githooks/<name> — no core.hooksPath, no stubs, no boilerplate. Repos that don't have a .githooks/ dir keep working exactly as before. GIT_HOOK_DISPATCHED guards against re-entry so legacy repos using the old stub-and-exec pattern don't loop. pre-push tees stdin so both the repo hook and the global ref-list loop see the full push payload. Adds two new always-no-op global hooks (pre-commit, post-commit) purely so the dispatch happens for those events too — previously only commit-msg and pre-push existed globally. Refactors this dotfiles repo to use the new pattern: drops the self-delegating .githooks/pre-push stub and removes the per-repo core.hooksPath override from `just init` (now an idempotent unsetter to clean up the override from past bootstraps). The remote-dev VM's home-manager profile symlinks all four hooks plus _dispatch.sh.
Diffstat (limited to 'dot_config/git/hooks/executable_pre-push')
-rwxr-xr-xdot_config/git/hooks/executable_pre-push11
1 files changed, 10 insertions, 1 deletions
diff --git a/dot_config/git/hooks/executable_pre-push b/dot_config/git/hooks/executable_pre-push
index ba7dc60..286958b 100755
--- a/dot_config/git/hooks/executable_pre-push
+++ b/dot_config/git/hooks/executable_pre-push
@@ -15,6 +15,15 @@
set -eu
+# shellcheck source=./_dispatch.sh
+. "${0%/*}/_dispatch.sh"
+# Buffer stdin so both the per-repo hook and our own loop below get the
+# full ref list (git only feeds it once).
+_stdin_buf=$(mktemp)
+trap 'rm -f "$_stdin_buf"' EXIT INT TERM
+cat >"$_stdin_buf"
+dispatch_repo_hook pre-push "$@" <"$_stdin_buf"
+
zero=$(git hash-object --stdin </dev/null | tr '0-9a-f' '0')
expected_name=$(git config user.name || true)
@@ -108,7 +117,7 @@ while read -r _local_ref local_sha remote_ref remote_sha; do
printf '\non %s:\n%s\n' "$remote_ref" "$bad" >&2
fail=1
fi
-done
+done <"$_stdin_buf"
if [ "$fail" -ne 0 ]; then
printf '\nfix sig + committer:\n' >&2