aboutsummaryrefslogtreecommitdiffstats
path: root/hooks/pre-commit
blob: 7b1778c6eb2f93d0c9aeff02cb5e3cbda70d7ab4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#!/bin/sh
# pre-commit: clang-format the C lines this commit touches.
#
# Scoped to *changed lines* via git-clang-format, NOT whole files: the
# codebase deliberately packs multiple statements per line (opcode tables,
# ALU helpers) and a full-file reformat would destroy that. New/edited
# lines converge on .clang-format style; untouched history stays intact.
#
# Enable once per clone:   git config core.hooksPath hooks
# Bypass for one commit:   git commit --no-verify

# staged C files (added/copied/modified/renamed)
staged=$(git diff --cached --name-only --diff-filter=ACMR -- '*.c' '*.h')
[ -z "$staged" ] && exit 0

if ! command -v git-clang-format >/dev/null 2>&1; then
    echo "pre-commit: git-clang-format not found (install clang-format)." >&2
    echo "pre-commit: bypass with: git commit --no-verify" >&2
    exit 1
fi

# git-clang-format --staged applies fixes to the WORKTREE, so we must re-add
# the files afterwards. That is only safe when index == worktree for every
# staged C file; refuse partially-staged files instead of mixing changes.
partial=$(git diff --name-only -- $staged)
if [ -n "$partial" ]; then
    echo "pre-commit: partially staged C files (index != worktree):" >&2
    printf '    %s\n' $partial >&2
    echo "pre-commit: stage them fully (git add) or bypass with --no-verify." >&2
    exit 1
fi

out=$(git clang-format --staged --quiet --extensions c,h 2>&1)
rc=$?
if [ $rc -ge 2 ]; then                      # real error (1 = "changes made")
    echo "pre-commit: git-clang-format failed:" >&2
    echo "$out" >&2
    exit 1
fi
if [ $rc -eq 1 ]; then
    git add -- $staged
    echo "pre-commit: clang-formatted changed lines in:" >&2
    printf '    %s\n' $staged >&2
fi
exit 0