The project on which I’m working has acquired some young people recently.
For a couple of them it’s even their first working experience so I’m really trying to transmit them the importance of conventions and an uniform coding style.
It’s usual (and perfectly normal) to find some coding style mistakes in their commits. So, to help us happily code all together, I created a small pre-commit hook to put in our repositories.
Right now it’s just a simple no-tabs check, but with some small changes it could check for use of deprecated functions or some forgotten
import pdb; pdb.set_trace() :)
# Files extensions in hooks.notabs are separated by "|" # Eg: # git config hooks.notabs py|js exec 1>&2 notabs=$(git config hooks.notabs) toplevel=$(echo "`git rev-parse --show-toplevel`/" | sed -e 's/[\/&]/\\&/g') if [ -n "$notabs" ] && git diff --cached -- not_a_file `git diff --cached --name-only | egrep '\.('$notabs')$' | sed 's/^/'$toplevel'/g' | xargs` | egrep -qn $'^\+\.*\t' then echo "Error: Attempt to add TABS" echo echo "Do you really want to break our beautiful coding conventions? :(" echo echo "Remove all tabs characters and stage again your changes." echo echo "To override this hook use:" echo echo " git commit --no-verify" echo exit 1 fi
To have it working:
copy this script into
repository_dir/.git/hooks/pre-commit. Add some file extensions to hooks.notabs variable (these must be separated by |). Done!
Let’s look at each parts in rigorous order of execution
notabs=$(git config hooks.notabs): retrieves from local config the files extensions to check
git diff --cached --name-only: lists names of files that have staged changes
egrep '\.('$notabs')$': removes files having different extensions from the ones in notabs
sed 's/^/'$toplevel'/g': prepend the absoulute path to each filename
xargs: creates a list of arguments using previous file names list
git diff --cached -- not_a_file: returns staged changes for files coming from “xargs”. Note that “not_a_file” is an unexistant file, specified in case that the output of xargs is empty. In fact if no files are specified the “git diff –cached” command would return staged changes for every file.
egrep -qn '^\+\.*\t': searches for all the lines starting with “+” and containing a tab
Note that this pre-commit hook can be easily bypassed using.
git commit --no-verify
This reflects exactly the same thought I want communicate to my collegues. Everyone should struggle to pass the pre-commit hook checks, but no one should be forced to.