A pre-commit check is invoked right before a change is committed into the repository. This is a perfect place to run a quick smoke test to ensure that no broken code is ever checked in. Many source control systems, including the powerful Git, support pre-commit check as part of its hooks system. Unfortunately, based on my (limited) observations, leveraging pre-commit feature is not something every single developer practices yet.

For Git, the pre-commit script is a single file located under the hidden subdirectory .git. Creating a new one is as easy as:

touch .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit

from the top-level working directory of your repository. When the script is executed (right before a commit), the exit code will be inspected. If it is zero, the commit will continue. If it is non-zero, the commit is blocked and the working directory is left in the “dirty” state (you can inspect it with git diff).

Here is a simple pre-commit script (Unix only, sorry!) which prevents you to commit something on Sunday.

if [ `date +%w` -eq 6 ]; then
  echo "Enjoy your life. Do not work on Sunday!"
  exit 1
exit 0

Many software projects drive the test via make test. Provided that the exit code is suitably set, the pre-commit script needs only that line:

make test

For Node.js applications which use npm test to run the unit-test, making a pre-commit script is never been easier. The content of the script is as simple as:

npm test

Even style checking (which is usually very fast) can be carried out easily. For JavaScript-based apps, JSLint or JSHint fits perfectly in this situation. Perhaps use my EightPack project to get such command-line tools easily. As an example, here is my pre-commit script for typical day-to-day Esprima development:

jslint esprima.js && jslint test/test.js

If your script needs to be more sophisticated and handles all the files being added, copied, or modified, you can take advantage of Git filtering feature:

git diff --cached --name-status --diff-filter=ACM

By some piping machinery, or even just through xargs magic, the list of the files can be fed and processed appropriately. In most cases, especially large projects, checking only the files affected by the commit will save a lot of time.

What if your project is a web app? Well, hopefully your app has unit tests. In that case, you can use PhantomJS to invoke the tests without the need to launch a web browser. PhantomJS has a nice integration with some popular frameworks such as Jasmine, QUnit, and many others.

This post is just a cursory look at pre-commit hook from Git. You may want to read more about various other hooks. Since it is quite common, you are also recommended to look for and investigate various tools out there (e.g. git-hooks) which let you manage multiple hooks easily. Pre-commit is easy to use and yet quite powerful. Invest a minute to set it up and it would save you from future nightmares!

  • Jean Lauliac

    Just calling `make test` or `npm test` from the pre-commit hook is *not* okay, because the user may have only added a few files to the staging area, or worse, only parts of files. Calling `make test` will execute the tests on the working files, not on the actual committed (staged) files.

    To avoid this, you need to add `git stash –keep-index` before testing, and restore changes with `git stash pop` at the end of the hook (even in the case of error during `make test`, of course).