99 lines
2.9 KiB
Plaintext
99 lines
2.9 KiB
Plaintext
|
|
#!/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
|
||
|
|
|
||
|
|
staged=$(git diff --cached --name-only --diff-filter=ACMR \
|
||
|
|
| grep -E '^(src|include|tests|util)/.*\.[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
|