Git Pre-commit Hooks – The easy way to improve code quality and speed up continuous integration testing
Testing your code often is a good idea and tools like Jenkins help developers deliver Continuous Integration with automated tests.
But it can be made much better. For example what happens when you commit a bug? Jenkins checks it out and tests it, finds the bug emails the team / individual saying it had a broken build.
What’s bad about this?
- It’s slow – it takes time for CI servers to checkout & test it. It’s a remote server so changes have to be transferred
- It’s delayed – you’ve moved on, so it’s a disproportionally annoying to go back and fix the bug.
- You can commit bugs – bugs which will fail your CI tests can be committed.
- You can break the build – you can commit broken builds which affects other developers.
This is because a centralised continuous integration setup is essentially reactive in nature. Developers commit something and it is only validated when the CI server gets round to testing it.
Introducing Pre-commit Hooks, which validated your changes before they are committed and distributed.
- Easier for developers – developers are forced to fix issues before considering the work finished, and moving on to the next task.
- Fast – changes don’t have to be uploaded to centralised server.
- You cannot commit bugs – the branch is always valid and tested
- You cannot break the build – the branch is always valid and tested
- Enforce coding standards forbid commits containing keywords or broken syntax.
- can be inefficient – re-running all unit tests is time consuming
Our git repo contains our iOS, Android and Web components, which means a naive implementation would re-run all tests over the code base for every commit. Obviously this is very time consuming.
The trick is to only test files which have changed. Your testing framework may have support for this, but we use a git diff to see if we need to test a component, here we test the web component if I’m committing a change to it:
WEB_CHANGES=$(git diff --cached --name-only web | wc -l)
if [[ ! $WEB_CHANGES -eq 0 ]]; then
And our script in full glory, the first check is to scan for any TODO’s and forbid committing and then we test our iOS, Android and Web components if any changes are being committed. The script returns error code 1 if there’s a bug and commit should fail.