Git Hooks: Automate Your Workflow Like a Pro
Learn: Git Hooks: Automate Your Workflow Like a Pro
Welcome to TopperBlog! 👋
I'm a tech content creator passionate about helping developers level up their careers and master cutting-edge technologies.
🎯 What I Write About:
• AI/ML Engineering & LLMs
• Web3 & Blockchain Development
• System Design & Architecture
• Interview Preparation (FAANG)
• Freelancing & Remote Work
• Modern Tech Stacks (Next.js, React, Rust, TypeScript)
• Performance Optimization & Best Practices
💼 Mission: Sharing practical, actionable insights that accelerate your tech career and maximize your earning potential.
📚 15+ In-Depth Guides covering everything from earning $10k/month as a freelancer to cracking FAANG interviews.
🌐 Let's connect and grow together in this amazing tech journey!
#TechBlogger #SoftwareEngineering #CareerGrowth #WebDevelopment #AIEngineering
Git Hooks: Automate Your Workflow Like a Pro (Pre-commit Magic Tricks)
You know that sinking feeling when you push code to production and immediately realize you left a console.log("WHY IS THIS BREAKING") in there? Or worse—you've committed your AWS credentials to a public repo and now some crypto miner in Belarus is having a field day with your credit card?
Yeah. We've all been there. And we've all sworn we'll be more careful next time.
Spoiler alert: You won't be. Because humans are terrible at repetitive tasks. We get tired, distracted, and overconfident. We need robots to save us from ourselves.
Enter Git hooks—your personal code bodyguard that stops you from doing stupid things before they become expensive things.
The "Oh Crap" Moment That Started It All
Picture this: It's 2 AM. You're three Red Bulls deep, finally fixing that bug that's been haunting you for days. You commit, you push, you close your laptop with the satisfaction of a job well done.
8 AM: Your Slack is on fire. The build is broken. Tests are failing. Your team lead is using the disappointed parent emoji. You forgot to run the linter. Again.
This was me, circa 2019, before I discovered Git hooks. Now? My Git repository literally won't let me commit garbage code. It's like having a really judgmental friend who checks your teeth for spinach before every meeting.
WTF Are Git Hooks Anyway?
Git hooks are scripts that Git executes automatically at specific points in your workflow. Think of them as event listeners for your repository—little programs that wake up and do stuff when you commit, push, merge, or perform other Git operations.
They live in the .git/hooks directory of your repository, lurking in the shadows, waiting to either save your ass or ruin your day (depending on how you configure them).
The most useful ones for daily development:
- pre-commit: Runs before a commit is created (our star today)
- pre-push: Runs before pushing to remote
- commit-msg: Validates your commit messages
- post-merge: Runs after a successful merge
Today, we're focusing on pre-commit hooks because they're the Swiss Army knife of preventing embarrassment.
Why Pre-commit Hooks Are Your New Best Friend
Here's the thing: Code review is great, but it's expensive. Every time a reviewer has to say "hey, you forgot to format this" or "um, there's a syntax error on line 47," that's time and mental energy wasted on stuff a robot could catch.
Pre-commit hooks are like spell-check for code. They catch the dumb stuff so humans can focus on the smart stuff—architecture decisions, logic errors, and whether your variable name thingy2 is really the best you can do.
Real benefits:
- Consistency: Everyone's code looks the same (no more tabs vs. spaces holy wars)
- Security: Catch secrets before they hit the repo
- Quality: Enforce linting, testing, and formatting standards
- Speed: Catch issues in seconds, not hours later in CI
- Sanity: Sleep better knowing you can't accidentally commit
TODO: FIX THIS HORRIBLE HACK
The Manual Way (AKA The Painful Way)
Let's start with a basic pre-commit hook the old-school way. This will help you understand what's happening under the hood before we get fancy.
Navigate to your repo and create a hook:
cd your-awesome-project
cd .git/hooks
touch pre-commit
chmod +x pre-commit
Now edit that pre-commit file:
#!/bin/bash
echo "🔍 Running pre-commit checks..."
# Check for console.log statements
if git diff --cached --name-only | grep -E '\.(js|jsx|ts|tsx)$' | xargs grep -n "console\.log" 2>/dev/null; then
echo "❌ ERROR: Found console.log statements. Remove them before committing."
exit 1
fi
# Check for TODO comments
if git diff --cached | grep -E "TODO|FIXME" 2>/dev/null; then
echo "⚠️ WARNING: Found TODO/FIXME comments. Are you sure you want to commit?"
read -p "Continue? (y/n) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
fi
echo "✅ All checks passed!"
exit 0
Try to commit something with console.log now. Go ahead. I'll wait.
Boom. Rejected. Your hook just saved you from yourself.
The problem with this approach? This hook lives in .git/hooks, which isn't tracked by Git. So every developer on your team needs to manually set this up. And we both know that's not happening.
The Pro Way: Enter pre-commit Framework
The pre-commit framework (yes, confusingly named the same as the hook type) is a Python tool that makes managing hooks actually pleasant. It's like the difference between writing raw SQL and using an ORM.
Installation
# Using pip
pip install pre-commit
# Or using Homebrew (macOS)
brew install pre-commit
# Or using conda
conda install -c conda-forge pre-commit
Setting Up Your First Config
Create a .pre-commit-config.yaml file in your repo root:
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
args: ['--maxkb=500']
- id: check-json
- id: check-merge-conflict
- id: detect-private-key
Install the hooks:
pre-commit install
That's it. Now every developer who clones your repo just runs pre-commit install once, and they're protected.
Real-World Magic Tricks
Trick #1: Never Commit Secrets Again
repos:
- repo: https://github.com/Yelp/detect-secrets
rev: v1.4.0
hooks:
- id: detect-secrets
args: ['--baseline', '.secrets.baseline']
This hook scans for API keys, passwords, tokens—basically anything that looks like it could cost you money if it leaks.
Pro tip: Generate a baseline file for known false positives:
detect-secrets scan > .secrets.baseline
Trick #2: Automatic Code Formatting
Stop arguing about code style. Let robots handle it.
For Python:
- repo: https://github.com/psf/black
rev: 23.12.1
hooks:
- id: black
language_version: python3.11
For JavaScript/TypeScript:
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.1.0
hooks:
- id: prettier
types_or: [javascript, jsx, ts, tsx, json, css, markdown]
Now your code gets auto-formatted before every commit. No more "can you reformat this?" comments in PRs.
Trick #3: Enforce Commit Message Standards
- repo: https://github.com/commitizen-tools/commitizen
rev: v3.13.0
hooks:
- id: commitizen
stages: [commit-msg]
This enforces conventional commit messages like:
feat: add user authentication
fix: resolve memory leak in data processor
docs: update API documentation
Why? Because good commit messages make git log actually useful, and they enable automatic changelog generation.
Trick #4: Run Your Tests
- repo: local
hooks:
- id: pytest
name: pytest
entry: pytest
language: system
pass_filenames: false
always_run: true
Warning: This can slow down commits if your test suite is large. Consider running only fast unit tests in pre-commit and saving integration tests for CI.
Trick #5: Lint Everything
Python:
- repo: https://github.com/pycqa/flake8
rev: 7.0.0
hooks:
- id: flake8
args: [--max-line-length=100]
JavaScript/TypeScript:
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v8.56.0
hooks:
- id: eslint
files: \.[jt]sx?$
types: [file]
The "Oh Shit" Escape Hatch
Sometimes you need to commit something that breaks the rules. Maybe you're in the middle of a refactor, or you're committing a known issue with a ticket to fix it.
# Skip all hooks for this commit
git commit --no-verify -m "WIP: refactoring in progress"
# Or skip specific hooks
SKIP=flake8,black git commit -m "temporary commit"
Use this power responsibly. With great power comes great "why did you commit that?"
Advanced: Custom Hooks for Your Specific Needs
Let's say you want to prevent commits to main branch (because we've all accidentally done that):
- repo: local
hooks:
- id: prevent-main-commit
name: Prevent commits to main
entry: bash -c 'if [ "$(git rev-parse --abbrev-ref HEAD)" = "main" ]; then echo "❌ Cannot commit directly to main!"; exit 1; fi'
language: system
pass_filenames: false
always_run: true
Or check for debugging statements:
- repo: local
hooks:
- id: no-debug-statements
name: Check for debug statements
entry: bash -c 'git diff --cached | grep -E "(debugger|console\.log|print\(|pdb\.set_trace)" && exit 1 || exit 0'
language: system
Performance: When Hooks Become Annoying
Here's the uncomfortable truth: If your pre-commit hooks take 30 seconds to run, developers will start using --no-verify. I've seen it happen.
Optimization strategies:
- Only check changed files: Most hooks do this by default
- Run expensive checks in CI: Save integration tests and full builds for your CI pipeline
- Use caching: Tools like
blackandeslintcache results - Parallelize: The
pre-commitframework runs hooks in parallel when possible
# Example: Only run on changed files
- repo: https://github.com/psf/black
rev: 23.12.1
hooks:
- id: black
# This is default behavior, but you can be explicit
pass_filenames: true
Team Adoption: Getting Everyone On Board
The technical part is easy. The human part? That's where things get interesting.
The wrong way: "Hey team, I added pre-commit hooks. They'll reject your commits if your code isn't perfect. Deal with it."
The right way:
- Start small: Add non-blocking hooks first (formatters, trailing whitespace)
- Communicate: Explain why these help everyone
- Make it easy: Document installation in your README
- Be flexible: Allow
--no-verifyfor legitimate cases - Iterate: Add stricter hooks gradually as the team adapts
Sample README section:
## Development Setup
After cloning this repo:
1. Install pre-commit: `pip install pre-commit`
2. Install hooks: `pre-commit install`
3. (Optional) Run on all files: `pre-commit run --all-files`
The hooks will automatically format your code and catch common issues.
If you need to bypass them temporarily, use `git commit --no-verify`.
The Complete Starter Config
Here's a battle-tested configuration that works for most projects:
# .pre-commit-config.yaml
repos:
# General checks
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
args: ['--maxkb=1000']
- id: check-json
- id: check-merge-conflict
- id: detect-private-key
- id: mixed-line-ending
# Security
- repo: https://github.com/Yelp/detect-secrets
rev: v1.4.0
hooks:
- id: detect-secrets
args: ['--baseline', '.secrets.baseline']
# Python
- repo: https://github.com/psf/black
rev: 23.12.1
hooks:
- id: black
- repo: https://github.com/pycqa/flake8
rev: 7.0.0
hooks:
- id: flake8
args: [--max-line-length=100, --extend-ignore=E203]
# JavaScript/TypeScript
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.1.0
hooks:
- id: prettier
types_or: [javascript, jsx, ts, tsx, json, css, markdown]
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v8.56.0
hooks:
- id: eslint
files: \.[jt]sx?$
types: [file]
# Commit messages
- repo: https://github.com/commitizen-tools/commitizen
rev: v3.13.0
hooks:
- id: commitizen
stages: [commit-msg]
Troubleshooting: When Hooks Go Wrong
"My hooks aren't running!"
# Check if hooks are installed
ls -la .git/hooks/pre-commit
# Reinstall
pre-commit uninstall
pre-commit install
"Hook X is failing but I don't know why"
# Run hooks manually with verbose output
pre-commit run --all-files --verbose
"I updated the config but nothing changed"
# Update hook versions
pre-commit autoupdate
# Clean and reinstall
pre-commit clean
pre-commit install
"Hooks are too slow!"
# See what's taking time
time pre-commit run --all-files
# Consider moving slow checks to CI
The Philosophical Bit: Automation vs. Autonomy
Here's a hot take: Some developers hate pre-commit hooks. They feel like Big Brother watching over their shoulder, preventing them from committing "work in progress" code.
And you know what? They have a point.
The key is balance. Hooks should catch obvious mistakes, not enforce opinions. They should make life easier, not harder.
Good hook: Prevents committing AWS credentials Bad hook: Rejects commits because a function is 51 lines instead of 50
Good hook: Auto-formats code to team standards Bad hook: Rejects commits for subjective style preferences
The goal is to automate the boring stuff so humans can focus on the interesting stuff. If your hooks are causing more frustration than they're preventing, dial them back.
Your Action Plan
Here's what you should do right now (yes, right now):
Install pre-commit:
pip install pre-commitorbrew install pre-commitAdd a basic config: Start with the starter config above, remove what you don't need
Install hooks:
pre-commit installTest it:
pre-commit run --all-filesCommit the config: Add
.pre-commit-config.yamlto your repoUpdate your README: Tell your team how to set it up
Iterate: Add more hooks as you discover pain points
The Bottom Line
Git hooks won't make you a better developer. But they will make you a more consistent developer. They'll catch your mistakes when you're tired, distracted, or just having an off day.
They're not about being perfect—they're about having a safety net.
And honestly? In a world where a single committed API key can cost you thousands of dollars, or a broken build can block your entire team, that safety net is worth its weight in gold.
So stop relying on your future self to remember to run the linter. Your future self is an idiot. Automate that shit.
Your team will thank you. Your code reviewers will thank you. And most importantly, your 2 AM self will thank you when the hook catches that console.log before it becomes a 8 AM Slack disaster.
Now go forth and hook responsibly. 🪝
Resources:
P.S. - If you found this helpful, the best way to thank me is to actually implement this in your next project. And maybe buy your DevOps team a coffee. They've been dealing with your unformatted commits for too long.