The Hook (The "Byte-Sized" Intro)
Client-side hooks are your personal quality gates. They run on your machine, before your code leaves it. pre-commit catches lint errors before they exist in history. commit-msg enforces message conventions. pre-push runs the test suite one last time. Three hooks, three layers of defense — all automatic.
📖 What are Client-Side Hooks?
Client-side hooks are scripts that run on the developer's machine during Git operations like commit, merge, and push.
Conceptual Clarity
The key client-side hooks:
| Hook | When It Fires | Common Use | Can Block? |
|---|---|---|---|
pre-commit | Before commit is created | Lint, format, tests | ✅ |
prepare-commit-msg | Before editor opens | Prepopulate message | ✅ |
commit-msg | After message is written | Enforce message format | ✅ |
post-commit | After commit is created | Notifications | ❌ |
pre-push | Before push starts | Full test suite | ✅ |
post-merge | After merge completes | Install deps, rebuild | ❌ |
post-checkout | After checkout/switch | Install deps, rebuild | ❌ |
pre-rebase | Before rebase starts | Warn about shared branches | ✅ |
The commit lifecycle:
pre-commit → prepare-commit-msg → editor → commit-msg → COMMIT → post-commit
Real-Life Analogy
Client-side hooks are like a personal assistant who reviews your work before you submit it. They check spelling (lint), formatting (prettier), and completeness (tests). You can override them in an emergency, but normally they prevent embarrassing mistakes.
Visual Architecture
Why It Matters
- Early detection: Catch issues on your machine, not in CI (faster feedback).
- Consistency: Every commit goes through the same checks.
- Message quality: Enforce conventional commit format for changelogs.
- Speed: Local checks are seconds; CI can take minutes.
Code
# ─── pre-commit: lint staged files ───
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/sh
echo "Running lint..."
npx lint-staged
if [ $? -ne 0 ]; then
echo "❌ Lint failed. Fix errors before committing."
exit 1
fi
EOF
chmod +x .git/hooks/pre-commit
# ─── commit-msg: enforce conventional commits ───
cat > .git/hooks/commit-msg << 'EOF'
#!/bin/sh
MSG=$(cat "$1")
PATTERN="^(feat|fix|chore|docs|style|refactor|test|ci)(\(.+\))?: .{1,}"
if ! echo "$MSG" | grep -qE "$PATTERN"; then
echo "❌ Commit message must match: type(scope): description"
echo " Example: feat(auth): add login validation"
exit 1
fi
EOF
chmod +x .git/hooks/commit-msg
# ─── pre-push: run tests ───
cat > .git/hooks/pre-push << 'EOF'
#!/bin/sh
echo "Running tests before push..."
npm test
EOF
chmod +x .git/hooks/pre-pushKey Takeaways
- pre-commit: Lint/format staged files — the most commonly used hook.
- commit-msg: Enforce commit message conventions (e.g., conventional commits).
- pre-push: Run the full test suite before sending code to remote.
- All pre-hooks block the action on non-zero exit code.
Interview Prep
-
Q: What are the three most important client-side hooks? A:
pre-commit(lint/format before commit),commit-msg(enforce message format), andpre-push(run tests before push). Together they form three layers of quality defense. -
Q: What is the difference between pre-commit and pre-push hooks? A:
pre-commitruns fast, lightweight checks (lint, format) on every commit.pre-pushruns heavier checks (full test suite) before pushing — it's the last gate before code reaches the remote. -
Q: How do hooks enforce conventional commit messages? A: A
commit-msghook reads the commit message, checks it against a regex pattern (e.g.,^(feat|fix|chore): .+), and exits with code 1 if the message doesn't match, blocking the commit.