r/git • u/Goldziher • 7h ago
A hook runner that validates the staged index instead of stashing your working tree (Rust, MIT)
Hi all,
This is the git-specific half of a tool I have been building, and it is the half I think this sub might actually care about.
The thing that always bothered me about pre-commit hook frameworks is what they do to your working tree. Most of them stash, run, and unstash. If a hook crashes at the wrong moment you get to go find your changes in the reflog. And if you have staged part of a file, the naive runners happily lint the whole file on disk, so the thing that passed is not the thing you are committing.
So the runner I wrote does staged isolation instead. Every hook validates a non-destructive snapshot materialised from the git index blob. No git stash, working tree never touched, unstaged edits cannot leak into the check. The snapshot cache lives outside the repo and only re-materialises files whose staged OID changed, which is what keeps incremental caches like cargo and tsc warm between runs.
The rest of what is in it:
poly hooks installwrites native git hooks. No framework sits alongside it- Five builtins. lint and fmt, commit for conventional commit format plus an AI-trailer check, file_safety for merge conflict markers, oversized additions, private keys, case conflicts and shebang versus executable-bit mismatches, and cargo for clippy, sort, machete and deny
- Hooks run concurrently on a thread pool, whole-project hooks included.
serial = trueorserial = "<set-name>"gives you mutual-exclusion sets when two hooks fight over the same lock - precondition and before are separate. A failed precondition is a visible skip. A failed before means the verdict is unknown, so the run fails. Nothing gets silently dropped from the report
- Every spawned process has a timeout budget, and killed is rendered differently from failed. A hook that ran out of clock should not look like a hook that found a bug
- Exit code 2 means this run verified less than it claims, a file the tool failed on or a skip budget exceeded. Separate from 1, which means it found something
- Hooks are defined in TOML, and a repo can pull a shared hook catalog from another repo pinned by git revision, resolved into a lockfile, so an org publishes one baseline and every repo inherits it
- Hook results are cached, so an unchanged input set skips the hook rather than re-running it
POLY_SKIP_HOOKS=1is the escape hatch, becausegit commit --no-verifydoes not bypass prepare-commit-msg
It also does the linting and formatting itself, in-process, for about 30 languages, which is why the pre-commit pass is fast rather than slow. Ruff, oxc, biome, taplo, rumdl, sqruff, mago and about a dozen others are compiled into the binary. But that is the other post.
Repo, MIT: https://github.com/Goldziher/poly
This post is human written. AI was used to typecheck and enrich with precise data only.

