diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 0000000..a75cecd --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,51 @@ +#!/usr/bin/env bash + +set -euo pipefail + +repo_root="$(git rev-parse --show-toplevel)" +cd "$repo_root" + +if ! command -v clang-format >/dev/null 2>&1; then + echo "pre-commit: clang-format not found in PATH" >&2 + exit 1 +fi + +if ! git clang-format -h >/dev/null 2>&1; then + echo "pre-commit: git clang-format is not available" >&2 + exit 1 +fi + +mapfile -d '' staged_files < <( + git diff --cached --name-only --diff-filter=ACMR -z -- \ + '*.c' '*.h' '*.cc' '*.cpp' '*.cxx' '*.hh' '*.hpp' '*.hxx' +) + +if ((${#staged_files[@]} == 0)); then + exit 0 +fi + +if git rev-parse --verify HEAD >/dev/null 2>&1; then + base_commit=HEAD +else + empty_tree="$(git hash-object -t tree /dev/null)" + base_commit="$( + printf 'pre-commit empty base\n' | \ + GIT_AUTHOR_NAME=pre-commit \ + GIT_AUTHOR_EMAIL=pre-commit@example.invalid \ + GIT_COMMITTER_NAME=pre-commit \ + GIT_COMMITTER_EMAIL=pre-commit@example.invalid \ + git commit-tree "$empty_tree" + )" +fi + +set +e +git clang-format --staged --binary clang-format --quiet "$base_commit" -- \ + "${staged_files[@]}" +status=$? +set -e + +if [[ $status -ne 0 && $status -ne 1 ]]; then + exit "$status" +fi + +git add -- "${staged_files[@]}" diff --git a/Makefile b/Makefile index 4d4c323..cdc72b4 100644 --- a/Makefile +++ b/Makefile @@ -44,4 +44,8 @@ test: cmake --build $(BUILD_DIR) ctest --test-dir $(BUILD_DIR) -.PHONY: clean run wm prepare build install uninstall reinstall test +install-hooks: + chmod +x $(MAKEFILE_DIR).githooks/pre-commit + git -C $(MAKEFILE_DIR) config core.hooksPath $(MAKEFILE_DIR).githooks + +.PHONY: clean run wm prepare build install uninstall reinstall test install-hooks