From b014eb23608bd92128b767c51ccf3d51b6d776cb Mon Sep 17 00:00:00 2001 From: Andrew Kesterson Date: Thu, 30 Jul 2026 18:22:47 -0400 Subject: [PATCH] Document git hook installation in the README The pre-commit hook is version controlled under scripts/hooks/, but Git configuration is not cloned, so the hook does nothing until each clone is pointed at it. Nothing said so anywhere a new contributor would look. Cover installation, verification, what the hook actually does, the Emacs requirement, bypassing, and uninstalling. Two things worth calling out explicitly: - core.hooksPath replaces the hooks directory outright rather than adding to it, so any pre-existing .git/hooks entries silently stop firing. Document a symlink alternative for anyone who keeps local hooks. - The documented --check exit codes are 0 conforming, 1 non-conforming, 2 environment failure, so a CI job failing on any non-zero status will not mistake a missing Emacs for a clean tree. Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/README.md b/README.md index ed529de..cee0fa9 100644 --- a/README.md +++ b/README.md @@ -270,6 +270,98 @@ PASS(e, akgl_heap_release_string(width)); ``` +## Git hooks + +The repository ships a `pre-commit` hook that keeps committed C sources in the +project's canonical format (Emacs `cc-mode` "stroustrup"; see `AGENTS.md` for the +full style guide). The hook lives in `scripts/hooks/` and is version controlled, +but **Git configuration is not cloned**, so every clone has to be pointed at it +once: + +```sh +git config core.hooksPath scripts/hooks +``` + +Confirm it took effect: + +```sh +git rev-parse --git-path hooks # should print: scripts/hooks +``` + +That is the whole installation. The hook is already committed with its +executable bit set, so nothing needs `chmod`. + +### What the hook does + +On each commit it looks at the **staged** content of any added, copied, modified, +or renamed `.c`/`.h` file under `src/`, `include/`, `tests/`, or `util/`, and +reindents it if it does not already match the canonical style. Checking the +staged content rather than the working tree means what lands in the commit is +what was actually verified. + +When a file needs reindenting, the hook fixes it in the working tree and +re-stages it — but only when the index and working tree agree for that file. If +they differ, you have staged part of a file with `git add -p`, and re-staging +would sweep your unstaged work into the commit. Rather than do that silently the +hook stops and prints the commands to run yourself: + +```sh +scripts/reindent.sh path/to/file.c +git add path/to/file.c +``` + +`include/akgl/SDL_GameControllerDB.h` is generated and always skipped. + +### Requirements + +The hook drives Emacs in batch mode, because `cc-mode`'s indentation engine is +the definition of the style and `clang-format` cannot reproduce it exactly. If +`emacs` is not on `PATH` the hook prints a warning and allows the commit — a +hook that refuses to run without an optional tool only teaches people to reach +for `--no-verify`. Install Emacs to get the check; without it, formatting is on +you. + +### Bypassing and uninstalling + +Skip the hook for a single commit: + +```sh +git commit --no-verify +``` + +Remove it entirely: + +```sh +git config --unset core.hooksPath +``` + +### If you already have local hooks + +`core.hooksPath` **replaces** the hooks directory outright — once it is set, Git +stops reading `.git/hooks/` altogether, so any hooks you keep there will silently +stop firing. If that matters, leave `core.hooksPath` unset and symlink just this +one hook instead: + +```sh +ln -s ../../scripts/hooks/pre-commit .git/hooks/pre-commit +``` + +### Formatting without the hook + +The hook is a convenience, not the source of truth. The same check is available +directly, and is what you would run in CI: + +```sh +scripts/reindent.sh --check # list non-conforming files; exit 1 if any +scripts/reindent.sh # reindent every tracked C source in place +scripts/reindent.sh src/game.c # reindent specific files +``` + +`--check` exits `0` when everything conforms, `1` when a file needs reindenting, +and `2` if Emacs is missing or the script cannot run — so a CI job that treats +any non-zero status as failure will not mistake a broken toolchain for a clean +tree. + ## Mutation testing The mutation harness makes one deliberate source-code change at a time in a scratch copy, then rebuilds and runs the passing CTest suite to measure whether tests detect the change. The known-failing `character` test is excluded by default.