Sometimes you’ll make a change to some code and not want to commit it. You probably add a comment to the code and hope you’ll either see the comment in the diff before committing or just remember not to check in the change. If you’ve ever done this you’ve probably also committed something you didn’t mean to commit. I know I have.
Luckily we can do better. Using git pre-commit hooks we can make git stop us from committing. Below is a git pre-commit hook that searches for the text nocommit and if found rejects the commit. With it you can stick nocommit in a comment next to the change you don’t want committed and know that it won’t be committed.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
Lines 3-10 figure out what revision to diff against. They can pretty much be ignored.
Lines 11-30 are all about handling unstaged changes. They create a
patch with these changes and revert these changes from the repository.
Then, in the function
quit, the unstaged changes are reapplied to
the repository. All of this is done so that nocommit in a
un-committed piece of text doesn’t cause the committed changes to be
Some online guides suggest using
git stash to achieve what is
described above. I started out using
git stash but ran into problems
where I’d end up in weird states. Unfortunately I didn’t take good
notes and I’m unable to describe the various bad things that happened.
Trust me when I say bad things did happen and that this way (create
patch, revert, apply patch) is much more successful.
Line 36 figures out what files contain nocommit. Lines 38-44 report
what files contain nocommit and then rejects the commit by exiting
with a non-zero exit code. The first
tput changes the output of the
echo commands to colored red and the second
tput changes output
back to default.
Using with a single repository
To enable in a single repository you need to add the above code to a
.git/hooks/pre-commit file in your local repository and make that
file executable. Once you’ve done that try adding nocommit to a file
and then try to commit it. The commit will be rejected if the
pre-commit hook is setup properly.
Using with multiple repositories
I want this pre-commit hook enabled in all of my repositories. I use
git init templates to do this.
git help init or a
Google search can help fill in the gaps with setting this up but below
are the steps I ended up taking.
git config --global init.templatedir ~/.git-templates
mkdir -p ~/.git-templates/hooks
- Copy and paste the above code into
chmod +x ~/.git-templates/hooks/pre-commit
After following those steps any repository created by
git init will
contain the pre-commit hook. To add to an existing repository
the repo and run
git init ..
If you try to commit some text with nocommit in it you’ll see something similar to the image below and the commit will be rejected.
If you ever need to commit and want to ignore pre-commit hooks
(example: If you are writing a blog post that is full of the text
nocommit) then you can ignore pre-commit hooks by using
I’ve found this pre-commit hook really useful. It has saved me from committing numerous times. I’d recommend adopting it.
I’m updated the code to be more portable. It was brought to my
attention by a comment that the original code took advantage of some
bash extensions and specific
mktemp behavior found in OS X. The
pre-commit code has now been tested works in OS X and Ubuntu 14.04.
There may be minor changes you need to perform to get it to work on
Updated code to handle if
mktemp fails and if whitespace changes
between creating a patch and applying it. Also adds in a change that
better handles whitespace in paths.