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.
The code
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
rejected.
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.
Warning: I know many developers that love using this and have had no problems. I get the occasional report of it not working. If it doesn’t work, and it seems like you’ve lost changes, you can find the patch file wherever mktemp creates files on your local machine. I’d still recommend testing it out on a small changeset so if something doesn’t work on your machine you don’t have to both debug why and recreate your changes.
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
touch ~/.git-templates/hooks/pre-commit
- Copy and paste the above code into
~/.git-templates/hooks/pre-commit
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 cd
into
the repo and run git init .
.
Example output
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 git commit
--no-verify
.
I’ve found this pre-commit hook really useful. It has saved me from committing numerous times. I’d recommend adopting it.
Errata
2015/12/23
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
your system.
2017/04/28
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.