From 67868f51bbab5bc3ef5c8ba15433ba401a297f1a Mon Sep 17 00:00:00 2001 From: sommerfeld Date: Tue, 19 May 2026 16:45:17 +0100 Subject: feat(git): user-level hooks auto-dispatch into /.githooks/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 /.githooks/ 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/ — 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. --- README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'README.md') diff --git a/README.md b/README.md index 83c621d..0a0c5f3 100644 --- a/README.md +++ b/README.md @@ -134,10 +134,14 @@ Verify with `sudo nft list ruleset`. ## Git hooks -Activated by `just init` via `git config core.hooksPath .githooks`: +The user-level hooks at `~/.config/git/hooks/` (set as `core.hooksPath` in `dot_config/git/config`) apply globally and auto-dispatch into any repo's `/.githooks/` if present — so projects can drop their own hooks at `.githooks/` without ever touching `core.hooksPath` or writing passthrough stubs. Per-event behavior: -- `pre-commit` → `just check`. Blocks commits that fail formatting or linting. Bypass with `git commit --no-verify`. -- `post-commit` → `chezmoi apply`. Keeps `$HOME` in sync whenever a tracked file changes in the repo. +- `pre-commit` → repo's `.githooks/pre-commit` (if any). No global logic. In this repo: `just check`. +- `commit-msg` → repo's `.githooks/commit-msg` (if any), then strips any `Co-authored-by:` whose identity matches an AI agent (Copilot/Claude/Codex/…) so they don't trip the push gate. +- `pre-push` → repo's `.githooks/pre-push` (if any), then rejects pushes that contain unsigned commits, commits with a foreign committer, or commits authored/co-authored by an AI agent. +- `post-commit` → repo's `.githooks/post-commit` (if any). No global logic. In this repo: `chezmoi apply`. + +Bypass any of these with `--no-verify` on `commit`/`push`. ## Disaster recovery -- cgit v1.3.1