Writing a Hook
Writing Your First Hook
After this page you have a pre-commit that refuses a commit containing a tab-only whitespace error, and a commit-msg that checks a conventional subject.
pre-commit
cat > ~/desk/desk-api/.git/hooks/pre-commit <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
git diff --cached --check
EOF
chmod +x ~/desk/desk-api/.git/hooks/pre-commitgit diff --cached --check flags leftover conflict markers and bad whitespace in the index. Exit status non-zero aborts the commit.
Try:
cd ~/desk/desk-api
printf 'bad \n' >> README.md # trailing space
git add README.md
git commit -m "docs: trigger hook"Fix the line, git add, commit again.
commit-msg
Git passes the path to a temporary file that holds the message:
cat > ~/desk/desk-api/.git/hooks/commit-msg <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
file="$1"
subject="$(head -n1 "$file")"
if [[ "$subject" == WIP* ]]; then
echo "commit-msg: subject cannot start with WIP" >&2
exit 1
fi
EOF
chmod +x ~/desk/desk-api/.git/hooks/commit-msgA stricter conventional check can wait until the team agrees on types. Start by blocking WIP.
pre-push
Git passes the remote name and URL, and on stdin: local ref, local hash, remote ref, remote hash.
cat > ~/desk/desk-api/.git/hooks/pre-push <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
while read -r local_ref local_sha remote_ref remote_sha; do
if [[ "$remote_ref" == refs/heads/main && "$local_sha" != "$remote_sha" ]]; then
echo "pre-push: running tests before updating main" >&2
# python -m pytest -q || exit 1
fi
done
EOF
chmod +x ~/desk/desk-api/.git/hooks/pre-pushUncomment the test command when desk-api has tests.
--no-verify
git commit --no-verify -m "docs: skip hooks"Emergency only. Treat it as breaking the seatbelt.