# Pre-commit Hooks Git hooks that run before each commit to enforce code quality, formatting, and security checks. ## Exit code controls behavior The hook's **exit code** determines what happens: - **Exit 0** → commit proceeds (even if files were modified) - **Exit non-zero** → commit aborted This creates three behavior types: | Type | Modifies files? | Exit code | Effect | Examples | |------|:-:|:-:|--------|---------| | Auto-fix | Yes | 0 | Files re-staged, commit proceeds | Black, Prettier, isort | | Fix-then-fail | Yes | non-zero | Commit fails — review changes, re-commit | Custom hooks for deps/security | | Check-only | No | non-zero | Commit fails — fix manually | flake8, mypy, bandit | **Fix-then-fail** is the key insight: a hook can modify files AND still fail the commit, forcing you to review the changes before re-committing. The [pre-commit framework docs](https://pre-commit.com/) don't clearly explain this — it's learned from source code and community knowledge. Use fix-then-fail for critical files: dependency manifests, security configs, build scripts, database migrations. ## Frameworks - **[Lefthook](https://github.com/evilmartians/lefthook)** — fast, zero-dependency, written in Go. Config in `lefthook.yml`. Recommended for new projects. - **[pre-commit](https://pre-commit.com/)** — Python-based, large ecosystem of community hooks. Config in `.pre-commit-config.yaml`. Both run linters, formatters, and custom scripts on staged files. See also: [[JDF Hooks]] (curated hooks for common projects), [[Python Code Quality]]