Even though the debugger is the way to go when figuring out issues in your code sometimes the quickest way is to add a cheeky console.log('test') , alert('test') or var_dump('test'), exit() or print_r(data) in PHP. This is the small hook I wrote to stop myself from doing that.
Clean commits matter. Debug leftovers in version control aren't just embarrassing but they ship to staging, confuse the next person reading the diff, and waste time tracking down "why is this page blank?" when someone left an exit() in production code.
The problem
For quick edits, it's often faster to dump output on the page than to step through a debugger. That's fine during development. The trap is committing that code anyway.
I kept doing it. Not because I didn't care - because I was human, tired, and moving fast. After the third time I had to explain why alert('something') was in a pull request, I stopped relying on willpower alone.
Two ways to fix it
I saw two realistic options:
- Be extra careful run
git diffbefore every commit and read every changed line (which is something that you should do in any case, but sometimes you can still miss it). - Automate the check write a script that blocks the commit if it finds any word from a restricted set.
Option 1 works until it doesn't. A properly written hook does the same job with more consistency and less mental overhead every time you commit.
Why not just use a linter?
Adding linters (opens in new tab) with special rules to your build process will help you avoid shipping test code to production
Linters run in CI or at build time. A pre-commit hook runs before the commit lands in history. Different layer, different job. I wanted both: clean commits locally, and a safety net before anything reaches a server.
The hook
Drop this in .git/hooks/pre-commit and make it executable (chmod +x .git/hooks/pre-commit):
#!/bin/sh
# Diff against HEAD so staged files are included
diff="git diff HEAD"
# Flag any forbidden debug patterns in the diff
result=$($diff | egrep -i 'alert|test|console|exit|var_dump|print_r')
# And add some color variables so our error message could be more noticeable
RED='\033[0;31m'
NC='\033[0m' # No Color
if [ -z "$result" ]; then
echo "\nAll hail to omnipotent master of universe!!!\n"
exit 0
else
echo "\n${RED}************"
echo "${RED}*${NC} STUPIDO! ${RED}*${NC}"
echo "${RED}************${NC}\n"
$result
exit 1
fi
The logic is straightforward: pipe git diff HEAD through egrep, and exit with code 1 if anything matches. Git treats a non-zero exit as a failed hook and aborts the commit. The coloured output and dramatic error message are optional but they made me actually notice when the hook fired.
Gotchas
The test false positive. If you're writing actual tests, you'll probably use the word test somewhere in your diff and this hook will block you. That's the trade-off of a naive pattern match. Tighten the regex to match your real mistakes (e.g. alert\( instead of alert) if test trips you up too often.
The escape hatch. When you genuinely need to commit code that matches a forbidden pattern, pass --no-verify:
git commit --no-verify
Use it sparingly. The hook only works if you can't trivially bypass it every time.
Did it work?
It helped me on the first day I wrote it. Being careful is a good thing, but we are still flesh and blood, and can make mistakes. The hook doesn't replace judgment, it catches the ones you make when judgment is offline.
The full gist is on GitHub (opens in new tab).