Thursday 24 January 2013

Git Tip: Get A Warning When A File Has Changed

Every project has one1. The file. If that file changes, you need to know, because if you don't then your life is going to be very unpleasant in very short order.

At Hogarth, that file is called development.conf. It is, roughly speaking, a dump of the way our various elements and components are wired together2, and as it's completely unmergable only one person can be working on it at any one time (something which we're working to fix). If you want to work on it, you need to make sure that you have the latest version in the database, so that when you dump out your new version, it includes all previous changes.

At various times, we've all been caught out by missing an update, and had to completely redo (often complex) changes just to incorporate a (often minor) previous change. One of my colleagues (Patrick), having just been caught out by this for (I think) the first time, suggested that it would be useful to get a warning when this changes. He reasoned that as we use git to manage all change to our codebase, it would be natural to write a git hook which did this for us.

We had a look together at the list of git hooks, and couldn't really see anything appropriate. post-merge seemed like what we wanted, but we couldn't work out how we could determine what had actually changed. So Patrick went off to do some real work, and I turned to the ever-reliable #git IRC channel on Freenode. ojacobson suggested a solution, which works beautifully.

To understand how this works, you'll need to know about the git reflog. +Alex Blewitt has a good introduction here. If you do one thing as a result of this article, educate yourself about the reflog. It's incredible!

The important take-away from that article is that:
git diff "HEAD@{1}"
will show you the diff between what you currently have in your tree and the commit before the last action that changed your history. Importantly, it treats merges as a single entry, so if you are immediately post merge, running the above command will show you a diff containing everything that changed in that merge, regardless of the number of commits the merge contained.

Pulling these various bits together, with some shell wizardry, gives us:
#!/bin/bash
set -eu

git diff "HEAD@{1}" --name-only | grep config/development.conf 2>&1 > /dev/null
CHANGED=$?

if [ $CHANGED ]; then
    echo
    echo -e "\e[41m!!! development.conf HAS CHANGED !!!\033[0m"
    echo
    echo "You should reload config using (something like):"
    echo "  ./manage.py loadconfig config/development.conf"
fi
We check if anything has changed, and if it has then we print out a BRIGHT RED WARNING MESSAGE. Drop that in .git/hooks/post-merge, make it executable, and you're good to go.


1 Well, maybe not every project. If you don't, I'm jealous.  But a lot of "enterprise" or otherwise unloved codebases will have one. And, more pertinently, ours does.

2 You can tell that ours is not a Java project because this is not an XML file.

Wednesday 23 January 2013

Git Tip: Remove .pyc Files Automatically

N.B. I've published a follow-up to this here, which includes a way to completely avoid this problem in the first place.

Recently, I've found myself increasingly caught out by stale .pyc files in our project. When I change from our mainline branch to a story branch (or vice-versa), I often find myself with inexplicable test failures because Python is using the .pyc files for no-longer-current code.

Luckily, it's pretty easy to fix this in git, using hooks, specifically the post-checkout hook. To do that, add the following to .git/hooks/post-checkout, and make the file executable:

#!/bin/bash
find $(git rev-parse --show-cdup) -name "*.pyc" -delete

Now, every time you checkout a new branch, all the .pyc files will be cleared out of your git branch.