Files
libakgl/scripts/hooks/pre-commit
Andrew Kesterson 2766372f37 Reindent tools/ and examples/ too, in both copies of the scope list
`scripts/reindent.sh` and `scripts/hooks/pre-commit` each carried their own
copy of the directory list, and neither included `tools` or `examples`. Both
hold ordinary hand-written C, so `--check` reported a clean tree without having
looked at them and the hook let them through unformatted -- which defeats the
one thing that keeps this tree a fixed point of `indent-region`.

The comment in `reindent.sh` already said the scope is "directories whose C
sources are hand-maintained", so this is the list catching up with the stated
intent rather than a new policy.

The two copies are now marked as being two copies of one decision, because they
have already disagreed once and nothing detects it when they do.

`scripts/reindent.sh --check` is clean with the wider scope.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 20:57:37 -04:00

103 lines
3.2 KiB
Bash
Executable File

#!/bin/sh
#
# Reindent staged C sources to the canonical style before the commit is made.
# See AGENTS.md -> "Coding Style".
#
# Enable with:
# git config core.hooksPath scripts/hooks
# Bypass a single commit with `git commit --no-verify`.
#
# The hook checks the *staged* content, not the working tree, so what gets
# committed is what was verified. When a file needs reindenting it is fixed in
# the working tree and re-staged -- but only when the working tree and the index
# agree for that file. If they disagree (a partial `git add -p`), re-staging
# would sweep unstaged work into the commit, so the hook stops and asks you to
# do it yourself.
set -eu
root=$(git rev-parse --show-toplevel)
reindent="$root/scripts/reindent.sh"
# Leave conflict resolution alone.
if [ -e "$root/.git/MERGE_HEAD" ]; then
exit 0
fi
if [ ! -x "$reindent" ]; then
echo "pre-commit: $reindent missing or not executable; skipping style check" >&2
exit 0
fi
# Without Emacs the canonical style cannot be applied or even checked. Warn
# rather than block: a hook that fails closed on a missing optional tool just
# teaches everyone to pass --no-verify.
if ! command -v emacs >/dev/null 2>&1; then
echo "pre-commit: emacs not found; skipping reindent (see AGENTS.md)" >&2
exit 0
fi
# Keep this list in step with SCOPE in scripts/reindent.sh. They are two copies
# of one decision, and they have already disagreed once: `tools` and `examples`
# arrived with the documentation harness and were reindentable by hand while
# neither this hook nor `--check` could see them.
staged=$(git diff --cached --name-only --diff-filter=ACMR \
| grep -E '^(src|include|tests|util|tools|examples)/.*\.[ch]$' \
| grep -v -x -F 'include/akgl/SDL_GameControllerDB.h' || true)
if [ -z "$staged" ]; then
exit 0
fi
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT INT TERM
# Reindent a copy of each file's staged content and see whether it moves.
needs=''
for f in $staged; do
copy="$tmp/$(printf '%s' "$f" | tr '/' '_')"
git show ":$f" >"$copy"
cp "$copy" "$copy.orig"
if ! "$reindent" "$copy" >/dev/null 2>&1; then
echo "pre-commit: reindent failed on $f; commit aborted" >&2
exit 1
fi
if ! cmp -s "$copy" "$copy.orig"; then
needs="$needs $f"
fi
done
if [ -z "$needs" ]; then
exit 0
fi
# Refuse to touch anything if even one file is partially staged.
blocked=''
for f in $needs; do
if ! git diff --quiet -- "$f"; then
blocked="$blocked $f"
fi
done
if [ -n "$blocked" ]; then
echo "pre-commit: these files need reindenting but have unstaged changes," >&2
echo "so re-staging them would pull unstaged work into the commit:" >&2
for f in $blocked; do echo " $f" >&2; done
echo >&2
echo "Reindent and stage them yourself, then commit again:" >&2
echo " scripts/reindent.sh$(for f in $blocked; do printf ' %s' "$f"; done)" >&2
echo " git add$(for f in $blocked; do printf ' %s' "$f"; done)" >&2
exit 1
fi
# Safe: for every file needing work, the index and working tree agree.
# shellcheck disable=SC2086
"$reindent" $needs
# shellcheck disable=SC2086
git add $needs
echo "pre-commit: reindented and re-staged:" >&2
for f in $needs; do echo " $f" >&2; done
exit 0