pre-commit Hook
5 second se zyada? Developers --no-verify use karenge. Fast rakhho, effective rakhvo.
The pre-commit hook runs BEFORE git commit creates the commit. It is your last chance to validate code before it enters history.
If the hook exits with code 0, the commit proceeds. If non-zero, the commit is BLOCKED and the code stays in your working directory.
It receives NO arguments — it just runs, and you check whatever you want.
Most common uses: lint code, format code, check for debug statements, prevent large files, validate configurations.
The pre-commit hook is the MOST IMPORTANT client-side hook. It is your automated code reviewer for mechanical issues.
The golden rule: pre-commit must be FAST. Under 5 seconds. If it is slow, developers will use --no-verify to bypass it.
# Create a pre-commit hook
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/bash
echo "🔍 Pre-commit checks running..."
# Check 1: No console.log in staged files
if git diff --cached | grep -q "console.log"; then
echo "❌ Found console.log in staged files. Remove before committing."
exit 1
fi
# Check 2: No TODO or FIXME left behind
if git diff --cached | grep -qE "TODO|FIXME"; then
echo "⚠️ Found TODO/FIXME. Consider resolving before committing."
# Warning only — do not block (exit 0)
fi
echo "✅ All checks passed!"
exit 0
EOF
chmod +x .git/hooks/pre-commit
# Now try to commit with console.log
echo "console.log('debug');" > app.js
git add app.js
git commit -m "add app"
# ❌ Found console.log. Commit BLOCKED!
# Fix it
echo "function app() {}" > app.js
git add app.js
git commit -m "add app"
# ✅ All checks passed! Commit created.Linting: run ESLint, Pylint, or equivalent on staged files. Block if errors.
Formatting: run Prettier, Black, or equivalent. Auto-format and re-stage.
Debug statements: check for console.log, debugger, print statements.
Large files: prevent committing files over a size limit (use Git LFS instead).
Secrets: check for API keys, passwords, tokens using tools like detect-secrets or gitleaks.
Trailing whitespace: prevent committing files with trailing spaces or missing newline.
Broken JSON/YAML: validate configuration files before they break your deployment.
For each check, decide: should it BLOCK (exit 1) or WARN (echo message but exit 0)?
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/bash
set -e # Exit on any error
# 1. Check for debug statements (BLOCK)
if git diff --cached --name-only | grep -qE '\.(js|ts)$'; then
if git diff --cached -- '*.js' '*.ts' | grep -qE 'console\.(log|debug)|debugger'; then
echo "❌ Debug statements found. Remove them."
exit 1
fi
fi
# 2. Check for secrets (BLOCK)
if git diff --cached | grep -qE "api_key|api_secret|password"; then
echo "❌ Potential secret detected. Use environment variables!"
exit 1
fi
# 3. Check for large files (BLOCK)
large_files=$(git diff --cached --name-only | xargs -I{} du -b {} 2>/dev/null | awk '$1 > 1048576 {print $2}')
if [ -n "$large_files" ]; then
echo "❌ Large files detected (>1MB): $large_files"
echo "Use Git LFS for large files."
exit 1
fi
# 4. Auto-format with Prettier (FIX, don't block)
if command -v npx &> /dev/null; then
npx prettier --write $(git diff --cached --name-only -- '*.js' '*.ts' '*.css' 2>/dev/null) 2>/dev/null || true
git add -u # Re-stage formatted files
fi
echo "✅ Pre-commit checks passed!"
exit 0
EOF
chmod +x .git/hooks/pre-commitgit commit --no-verify skips ALL client-side hooks: pre-commit, prepare-commit-msg, commit-msg.
It exists because sometimes you NEED to commit without hooks: WIP commits, emergency fixes, fixing a broken hook.
But overusing --no-verify defeats the purpose of hooks entirely.
If developers frequently use --no-verify, your hooks are TOO SLOW or TOO STRICT.
Fix the root cause: make hooks faster, reduce false positives, allow warnings for non-critical issues.
NEVER make --no-verify part of your normal workflow. It should be the exception, not the rule.
Auto-formatters like Prettier or Black can run inside pre-commit and FIX code automatically.
The pattern: run formatter → re-stage the formatted files → commit proceeds with clean code.
This is MAGIC for teams — no more formatting arguments in code review.
Important: after auto-formatting, you must git add the changed files again so the formatted version is committed.
Use git diff --cached --name-only -- '*.js' to get only staged JS files for formatting.
This pattern is exactly what lint-staged automates (Chapter 8.5).
# Auto-format pattern in pre-commit
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/bash
# Get staged JS/TS files
staged_js_files=$(git diff --cached --name-only --diff-filter=ACM -- '*.js' '*.ts')
if [ -n "$staged_js_files" ]; then
# Run Prettier on staged files
echo "🎨 Formatting code with Prettier..."
npx prettier --write $staged_js_files 2>/dev/null
# Re-stage the formatted files
git add $staged_js_files
fi
exit 0
EOF
chmod +x .git/hooks/pre-commit
# Now when you commit:
echo "const x=1" > messy.js # bad formatting
git add messy.js
git commit -m "add messy code"
# 🎨 Formatting code with Prettier...
# Prettier fixes the formatting automatically
# git add re-stages the fixed version
# Commit contains CLEAN code, not messy code!
# Check the committed version
git show HEAD:messy.js
# const x = 1; ← properly formatted!Two things you should NEVER commit: large binary files and secrets (API keys, passwords).
Large files bloat the repository forever. Use Git LFS instead (covered in Chapter 10.8).
Secrets in Git history are a security incident. Even if deleted, they remain in history.
Pre-commit is your last line of defense — check for these BEFORE they enter history.
Tools: gitleaks or detect-secrets for finding secrets in staged files.
For large files: check file size against a limit (e.g., 1MB or 5MB) before allowing commit.
But remember: this is client-side. A developer can bypass with --no-verify. Server-side checks (GitHub secret scanning) are the real enforcement.
Lo kar liya — Key Points:
- ✅ pre-commit hook runs BEFORE git commit creates the commit — it can block bad code from entering history
- ✅ Common checks: linting, formatting, debug statements, large files, secrets
- ✅ Exit code 0 = allow commit, non-zero = block commit
- ✅ Auto-formatting pattern: run formatter → git add formatted files → commit proceeds with clean code
- ✅ Keep pre-commit under 5 seconds — slow hooks make developers use --no-verify, defeating the purpose
- ✅ git commit --no-verify bypasses ALL hooks — use only for emergencies, not as regular practice
- ✅ Pre-commit is client-side enforcement — always pair with server-side checks (CI) for defense in depth
Want to track your progress?
Log in to save your place and pick up where you left off.
Progress track karna chahte ho?
Login karo apni progress save karne ke liye aur jahan chhoda tha wahan se shuru karo.
Login