Files
iw4L/xtask/githooks/pre-commit
T
brennop a2b52f8b26 Read unstaged paths without mapfile in the pre-commit hook
macOS ships bash 3.2, which has no mapfile, and expanding an empty
array under `set -u` fails before bash 4.4. With rustfmt on PATH the
hook therefore aborted every commit there once `cargo xtask mr
new|fmt|ship` had installed it.

Read `git diff --name-only` with a while-read loop, the way the
context/ check above it already reads its list. Behaviour is unchanged.
2026-10-01 14:18:21 -03:00

69 lines
2.3 KiB
Bash
Executable File

#!/usr/bin/env bash
# Refuse to commit working memory, and refuse format-only dirt nobody staged.
#
# `context/` is local: artifacts, externals, agent clones and the RE tooling and
# its bases live there and `.gitignore` keeps them out. This is the second wall
# — `git add -f` walks through the first. Nothing under `context/` is an
# exception: the shipped repo is the runtime, and the RE side of the work is not
# published.
#
# Installed from xtask/githooks/pre-commit by `cargo xtask mr new|fmt|ship`.
set -euo pipefail
ROOT="$(git rev-parse --show-toplevel)"
# Deletions (untracking something that should never have been in) are fine.
staged=()
while IFS= read -r rel; do
[[ -n "$rel" ]] || continue
staged+=("$rel")
done < <(git diff --cached --name-only --diff-filter=ACMR -- context/ || true)
if ((${#staged[@]} > 0)); then
printf '%s\n' \
'pre-commit: context/ is working memory, not the product. Do not git add it (not even -f).' \
'The files stay on disk. Unstage with:' \
' git restore --staged -- context/' \
'Nothing under context/ belongs in git.' \
'Staged paths:' >&2
printf ' %s\n' "${staged[@]}" >&2
exit 1
fi
# Unstaged .rs whose working tree equals rustfmt(index) is the cargo-fmt-all
# leftover: files the agent did not stage, rewritten in place. Real WIP does
# not compare equal. Do not guess "my files" — refuse the leftover.
if command -v rustfmt >/dev/null; then
dirt=()
tmpdir="$(mktemp -d)"
cleanup() { rm -rf "$tmpdir"; }
trap cleanup EXIT
while IFS= read -r rel; do
[[ -n "$rel" && "$rel" == *.rs ]] || continue
wt="$ROOT/$rel"
[[ -f "$wt" ]] || continue
idx="$tmpdir/idx.rs"
if ! git show ":$rel" >"$idx" 2>/dev/null; then
continue
fi
if ! rustfmt --edition 2024 "$idx" >/dev/null 2>&1; then
continue
fi
if cmp -s "$idx" "$wt"; then
dirt+=("$rel")
fi
done < <(git diff --name-only --diff-filter=ACMR || true)
if ((${#dirt[@]} > 0)); then
printf '%s\n' \
'pre-commit: unstaged .rs files match rustfmt(index) — likely cargo fmt --all / -p.' \
'Restore them; format only the files you own:' \
" git restore -- ${dirt[*]}" \
' cargo xtask mr fmt <your .rs paths>' \
'Unstaged format-only paths:' >&2
printf ' %s\n' "${dirt[@]}" >&2
exit 1
fi
fi
exit 0