Commit hooks within Git can be incredibly useful for automatically incorporating third-party tools into the pre and post commit process.
Hooks are written in shell script (BASH) and can therefore execute any program or custom tool that you need to use. Typically, these hooks are great for validating code or helping to enforce a particular coding style.
All hooks are stored in the .git/hooks folder located at the root of a repository. Git comes equipped with lots of example scripts that are already stored within this folder. Each hook receives slightly different parameters based upon the hooks intended function and the samples help with identifying them.
$ ls /path/to/my/repo/.git/hooks applypatch-msg.sample commit-msg.sample post-commit.sample post-receive.sample post-update.sample pre-applypatch.sample pre-commit.sample prepare-commit-msg.sample pre-rebase.sample update.sample
One problem I recently encountered was how to enforce a strict coding style for a project that 7 other developers contribute to. Lots of developers often have slightly varying styles of programming – this problem is further complicated when taking multiple languages into consideration. This project involved a substantial amount of Coffeescript; whilst this language is fairly style-restrictive, enforcing a singular style that all developers adhered to was still important. Whilst Coffeelint is still very much in its infancy, it provides enough configuration to enable a specific style to be enforced effectively.
Lets take a look at creating a pre-commit hook, which enables us to automatically validate all .coffee files within the project when any commit is attempted. First, we need to create our hook script file and ensure that it is executable.
$ touch /path/to/my/repo/.git/hooks/pre-commit $ chmod a+x /path/to/my/repo/.git/hooks/pre-commit
Once the pre-commit file has been created, lets insert a test into the file to ensure that it is working. Open the file and insert the code below, this will make any attempt to commit files fail with the message (in red) ‘Pre-Commit Tests Failed’. Feel free to try it out for yourself at this point.
#!/bin/bash echo -e "\e[00;31mPre-Commit Tests Failed.\e[00m" exit 1
Exiting any hook with a non-zero value aborts the next process within the cycle.
The shell script always exits with a non-zero value causing Git to abort the pre-commit sequence. This script is less than useless at the moment, so lets insert the command and logic for (recursively) determining if all .coffee files within the project are valid and use the correct style.
#!/bin/bash test=`find coffee/ -name "*.coffee" | xargs coffeelint -f coffeelint.json | grep Ok! | awk '{ print $1 }'` if [ "$test" != '✓' ]; then find coffee/ -name "*.coffee" | xargs coffeelint -f coffeelint.json echo -e "\e[00;31mCoffeelint Tests Failed.\e[00m" exit 1 else echo -e "\e[00;32mCoffeelint Tests Passed.\e[00m" fi
Whilst the above example is very specific, it is fairly simple to pipe out the determinate from the output of any process, whether that is a single character or a more complex value. This makes hooks an important asset to any project that has repetitive operations.
Hooking enables the automation of tedious tasks, and as seen in the example above, can help to enforce coding styles. Git hooks in general and their usage are further explained here.
Pro Tip: Remember to ensure that the script is executable! chmod a+x!