Git Hooks
Automate checks and actions by running scripts on Git lifecycle events
Overview
Git hooks are scripts that Git runs automatically at defined points in its lifecycle, such as before a commit or after a push is received. Client-side hooks (like pre-commit and commit-msg) enforce local quality checks, while server-side hooks (like pre-receive) enforce policy on shared repositories. Hooks live in .git/hooks, are not committed by default, and must be executable to run.
Syntax / Usage
Create an executable script named after the hook event. A non-zero exit code aborts the operation for "pre" hooks.
# Hooks live in the repository's hooks directory
ls .git/hooks/ # sample hooks end in .sample
# Create a pre-commit hook
cat > .git/hooks/pre-commit <<'EOF'
#!/bin/sh
npm run lint || exit 1
EOF
chmod +x .git/hooks/pre-commit
# Share hooks with the team by pointing Git at a tracked directory
git config core.hooksPath .githooks
Examples
Block commits that fail linting or tests with a pre-commit hook:
#!/bin/sh
# .githooks/pre-commit
npm run lint && npm test
# exit code propagates: non-zero aborts the commit
Enforce a commit-message convention with commit-msg:
#!/bin/sh
# .githooks/commit-msg — $1 is the path to the message file
grep -qE '^(feat|fix|docs|chore): ' "$1" || {
echo "Message must start with feat:, fix:, docs:, or chore:"
exit 1
}
Bypass hooks intentionally for an emergency commit:
git commit --no-verify -m "hotfix: skip checks"
Common Mistakes
- Forgetting
chmod +x, so the hook is silently ignored - Expecting hooks in
.git/hooksto be shared—they are not tracked or pushed - Writing slow
pre-commithooks that frustrate developers into using--no-verify - Relying only on client-side hooks for security, which anyone can skip
- Not setting
core.hooksPathto distribute team hooks from a tracked folder
See Also
commit git-workflows remote