The hidden gem: Automate anything on commit, push, merge β custom scripts that run at every Git lifecycle event.
Code gets committed without tests passing. Commit messages are inconsistent. Secrets accidentally get pushed. Deployments happen without lint checks.
Git hooks let you intercept Git events and run custom scripts before or after them.
ls .git/hooks/
# applypatch-msg.sample post-update.sample pre-push.sample
# commit-msg.sample pre-applypatch.sample pre-rebase.sample
# fsmonitor-watchman.sample pre-commit.sample prepare-commit-msg.sample
# post-commit.sample pre-merge-commit.sample update.sampleRemove .sample from any of these to activate them. They're just executable scripts.
#!/bin/sh
# .git/hooks/pre-commit β Block commits with syntax errors
echo "Running pre-commit checks..."
# Check Python syntax
python -m py_compile tasks.py
if [ $? -ne 0 ]; then
echo "β Syntax error detected. Fix before committing."
exit 1
fi
# Check for debug prints
if grep -rn "breakpoint()\|import pdb" tasks.py; then
echo "β Debug statements found. Remove before committing."
exit 1
fi
echo "β
Pre-commit checks passed."#!/bin/sh
# .git/hooks/commit-msg β Enforce conventional commits
MSG=$(cat "$1")
if ! echo "$MSG" | grep -qE "^(feat|fix|docs|style|refactor|test|chore|hotfix)(\(.+\))?: .{3,}"; then
echo "β Commit message must follow Conventional Commits format:"
echo " feat: add search feature"
echo " fix(tasks): handle empty list"
exit 1
fi#!/bin/sh
# .git/hooks/pre-push β Run tests before push
echo "Running tests before push..."
python -m pytest tests/ -q
if [ $? -ne 0 ]; then
echo "β Tests failed. Push blocked."
exit 1
fi
echo "β
All tests pass. Pushing."#!/bin/sh
# .git/hooks/post-commit β Show stats after each commit
echo ""
echo "π Commit stats:"
echo " Files changed: $(git diff --cached --numstat HEAD~1 | wc -l)"
echo " Total commits: $(git rev-list --count HEAD)"Hooks in .git/hooks/ are not tracked by Git (they're inside .git/). To share them:
# 1. Store hooks in a tracked directory
mkdir -p .githooks/
# 2. Tell Git to use that directory
git config core.hooksPath .githooks/
# 3. Now hooks in .githooks/ are version-controlled!- Exit code 0 = success (allow the action). Non-zero = failure (block the action).
- Hooks can be any executable: shell, Python, Ruby, Node, etc.
- Use
--no-verifyto skip hooks in emergencies:git commit --no-verify. - Combine
pre-commitwithlint-stagedfor only linting changed files.
bash episodes/demos/06-hooks-demo.sh