Using Git Hooks To Help Enforce Our SOP

This week at work I moved all of our git repositories from a GitHub account owned by one of our employees to one owned and managed by the company. That was easy enough, but reminded me that we've been using git commit hooks to make sure we include our ticket/issue number in the commit message. This lets us link Jira with our commits. Trouble is, I couldn't remember how exactly I did it. I should have written it up last time, so I'm writing it up this time.

Git Hooks

Git can run commands at a number of different events using hooks. The 'hook' files are located in your repository's .git/hooks directory. By default there are sample files for each event. These files make good starting place. To use one, just rename the file to remove the '.sample' from the end of the filename.

The bundled example files are Bash shell scripts. They work on Windows as well as Linux machines because the standard Windows installer for git includes a Bash emulator by default. You can write the scripts in any language you like (such as Python) so long as your system can run them as executable files.

Requiring an Issue Number

Our SOP requires us to include the ticket number we are working on as part of each commit message. You could get creative with this using regular expressions. But I wanted to keep it as simple as possible. I'm just doing simple text matching.

My 'commit-msg' looks like this:

 #!/bin/sh
 # This hook gets run after the commit message is entered, but before the commit
 # is made. If this fails, the commit will not take place. We are using it to:
 #    * Check that the first line of the commit message includes the issue number prefix

 # The path to the file with the commit message is passed in as the first argument
 file_path=$1

 # Get the first line of the file
 first_line=$(head -n 1 $file_path)

 # Check that the first line includes the text we want
 if [[ "$first_line" == *FW-* || "$first_line" == *TD-* ]]
 then
     exit 0
 else
     # The 'echos' will be displayed in the error message. The 'exit 1' tells git this is
     # a failure.
     echo "You must include the Jira issue number in the first line of the commit message."
     echo "The issue number must start with FW- or TD- and must be in all caps."
     exit 1
 fi

If you are familiar with Bash scripts this is pretty straight forward. If not, I'll walk through it below. The key is that this will get called before a commit is accepted. If the file returns a zero the event, in this case a commit, is accepted. If it returns a one it is rejected. You can do whatever you like in these scripts.

The breakdown

file_path=$1

The path to the commit message file is being passed in as the first argument to the script. We can use that to get the contents of the file. In this case I want the ticket number in the first line of the commit message. We get that here:

first_line=$(head -n 1 $file_path)

Next we check to see if the first line contains either "FW-" or "TD-". These are the two valid prefixes for tickets in the Jira instance we use at work. You could look for any string or strings. Or get fancier using some regex.

if [[ "$first_line" == *FW-* || "$first_line" == *TD-* ]]

If we find what we are looking for we return a '0'. This tells git all is well and to keep going with the action.

then
     exit 0

If not, we pop up a message to the user so they know their commit is about to be rejected. In this case the user is me, so I make it very specific and clear. After that we return a zero so git knows that it should reject the commit.

else
     # The 'echos' will be displayed in the error message. The 'exit 1' tells git this is
     # a failure.
     echo "You must include the Jira issue number in the first line of the commit message."
     echo "The issue number must start with FW- or TD- and must be in all caps."
     exit 1
 fi

All the git hooks work in the same way. They get called by the action indicated in the file name. The script does some work and returns a zero (success) or a one (failure). You can run hooks on both the client-side and the server-side. More info on the hooks can be found in the git documentation.

A few things to note

  • You can, and probably should, include your git hooks in your repo. Put a copy of each into another directory and commit them like you would other files. Then you or another developer will get the copy of every hook they'll need to when working with your repo. They'll still need to put them in the correct location, but they'll have what they need.
  • These are client-side only. This means that you aren't forced to use them. This helps remind a developer that they've made a mistake, but doesn't actually enforce the restriction. In this case, if someone doesn't use the hook, they can commit all they want without including a ticket number. That is where server-side hooks come in. These can be used to enforce the rules. It is probably best to use client-side and server-side scripts together to inform the developer of issues and make sure nothing gets committed that shouldn't.
  • The hook files have no file extension. This is weird for Windows users, but fear not, they are just regular text files and can be edited using any text editor.

Comments

Comments powered by Disqus