Using Git commit hooks to prevent stupid mistakes
I’m pretty prone to making stupid mistakes all the time. I think its a mixture of being busy, and being a natural scatter brain. In any case, mistakes happen, and thankfully over time developers have come up with a number of ways to combat mistakes. These usually take the shape in automated toolds. Unit tests are a great way to automated tool to help prevent stupid mistakes from happening, Git commit hooks are another. Git hooks are shell or other scripts that you install in your repo, and git will run them at certain points in time. One nice advantage of git commit hooks is you can install them locally and you can avoid publishing the stupid mistake you might make.
For me, I quite often commit debugger;
statements in my javascript code. I want to not do that anymore so I created the following hook in .git/hooks/pre-commit
- #!/bin/sh
- if git-rev-parse --verify HEAD >/dev/null 2>&1; then
- against=HEAD
- else
- against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
- fi
- for FILE in `git diff-index --check --name-status $against -- | cut -c3-` ; do
- # Check if the file contains 'debugger'
- if [ "grep 'debugger' $FILE" ]
- then
- echo $FILE ' contains debugger!'
- exit 1
- fi
- done
- exit
After creating the file its important to make it executable with chmod +x .git/hooks/pre-commit
or git won’t run your script. Git hooks are installed on a per-repo basis, which is kind of bad. Its also kind of good, in that you can easily install your own suite of git hooks and no-one will be the wiser. There are a number of hooks available in git, and you can learn all about them in Pro Git
What is the purpose of the conditional at the beginning with the fallback to the magic refname?
Justin on 4/20/11
At Disqus we’re running JSHint as part of our pre-commit hook. You can configure it to reject development-y function calls (like console.log), as well as enforce a number of good practices.
Speaking of which, static comments Mark? For shame …
Ben on 4/21/11
That’s a cool idea, Ben, using it to look for console.log – that’s hands-down one of my most annoying habits as far as commits are concerned.
I do a similar thing to this, but the stupid mistake I’m protecting against is committing configuration patches, which helps me keep apps platform/configuration agnostic (shameless self-plug: ) http://tndy.me/h3GlDE
Jasper on 4/25/11
Justin: The magic refname is the initial tree object present in every git repo, its in the example pre-commit hook, and it would keep your hook working even on a brand new repo.
mark story on 4/30/11
Hey Mark, I think this line
if [ “grep ‘debugger’ $file” ]should read
if [ “grep ‘debugger’ $FILE” ]Kevin van Zonneveld on 8/1/11
Kevin: You’re totally right. Thanks :)
mark story on 8/7/11
Since it greps the file without regard for what has been staged, I believe it will reject debugs that aren’t actually about to be committed, if you git add a file, then add debug statements but don’t git add them, then try to commit. Probably not a big deal.
I made a similar hook in Ruby that only looks at what has been added: https://github.com/henrik/dotfiles/blob/master/githooks/pre-commit
Henrik N on 10/7/11
You CAN setup a pseudo-global set of hooks as you wish — but ONLY on newly created repositories of the local machine.
The git client’s shared files are typically in “/usr/share/git-core”.
You can use the “templates” folder therein to modify the default layout of a repository and its hooks.
Be careful, though, since this is mostly useful for repeatable project types*. (i.e. a repository of only javascript, or a repository of only php).
will on 6/9/12
Just stumbled over this little gem, too:
http://stackoverflow.com/questions/2293498/git-commit-hooks-global-settings
will on 6/9/12
Thanks for this. I get this error with your code though:
fatal: —name-only, —name-status, —check and -s are mutually exclusive
Jordan314 on 6/19/12
Great, thanks for your effort! Used this to keep those console.log from being checked in and embarrassing me publicly ;-)
Couple of things: * The “git-rev-parse” in line 4 has to read “git rev-parse” on my machine, else it always runs against the root node, never against head. * I had to remove “—check” on line 10, because git complains about the two options being mutually exclusive. Must have changed in the meantime.
Thorsten Krüger on 7/5/12
Was unable to run this hook, found that the problem is that
—check —name-status are mutually exclusive, at least in my version of git.
Just remove the —check and it works.
Dmitri Snytkine on 7/8/12
@Jordan314 using
`git diff-index —name-status —cached $against —`
instead of
`git diff-index —check —name-status $against —`
Koen on 7/25/12
@Jordan314 try using
`git diff-index —name-status —cached $against —`
instead of
`git diff-index —check —name-status $against —`
Koen on 7/25/12
I’m using this git hook to ensure my debugging code doesn’t get committed and I’m very happy with it!
https://gist.github.com/3266940
Luis S on 8/5/12
I develop on OS (Mac). My installation of git did not include ‘git-rev-parse’, but ‘git rev-parse’ works just fine.
https://gist.github.com/3777359
David V on 9/24/12
One other way to remove debugger; lines is to user google clouser compiler. In this way, besides other benefits you are able to remove debugger lines or other debugging code from productions scripts.
http://stackoverflow.com/questions/11402218/removing-debug-code-from-inside-a-function-using-closure-compiler-simple-optimis
catalin on 8/6/13
After reading this I’ve been trying to make a global pre-commit hook that should work for ‘all’ languages here https://github.com/kvz/dotfiles/blob/master/.gittemplate/hooks/pre-commit
Very interested in your feedback.
Kevin van Zonneveld on 12/18/13
Modern way of doing this: #!/bin/sh
if git rev-parse —verify HEAD >/dev/null 2>&1; then against=HEAD
else against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi
for FILE in `git diff-index —name-only $against ` ; do # Check if the file contains ‘debugger’ echo $FILE $against if [ “grep ‘debugger’ $FILE” ] then echo $FILE ‘ contains debugger!’ exit 1 fi
done
exit
JJ on 5/3/14
Just a minor edit to a great and helpful post: Apparently, the Pro Git book added a chapter; Git Hooks are now in Chapter 8, Section 3. Your link at the very end of the post should point here: http://git-scm.com/book/ch8-3.html or http://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks
Daniel M on 12/17/14