Skip to content

Latest commit

Β 

History

History
113 lines (84 loc) Β· 2.97 KB

File metadata and controls

113 lines (84 loc) Β· 2.97 KB

Episode 6: Git Hooks πŸͺ

The hidden gem: Automate anything on commit, push, merge β€” custom scripts that run at every Git lifecycle event.

The Problem

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.

Where Do Hooks Live?

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.sample

Remove .sample from any of these to activate them. They're just executable scripts.

The Most Useful Hooks

pre-commit β€” Run before every commit

#!/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."

commit-msg β€” Validate commit messages

#!/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

pre-push β€” Gate before pushing

#!/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."

post-commit β€” Notify after commit

#!/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)"

Making Hooks Shareable

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!

Pro Tips

  • 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-verify to skip hooks in emergencies: git commit --no-verify.
  • Combine pre-commit with lint-staged for only linting changed files.

Run the Demo

bash episodes/demos/06-hooks-demo.sh