Using Git Hooks

Git Hooks can be quite useful for a variety of things. Here is a simple example to make sure you don't forget to include an issue # in your commit message.

Using Git Hooks

I've written a fair amount in the past about Git. I'm pretty sure I have mentioned Git hooks before, but I'm not sure if I've given a good example. I just had a reason to use one recently, so I thought I would share.

What are Git hooks?

Hooks are just scripts that execute on various events that occur when using Git, like creating a new commit, or pushing changes to a remote server. These hooks allow you, the Git user, to inject various bits of scripting code to customize the way Git works or enforce some standards. As an example, you could check every commit message to make sure it includes an issue number and reject the commit if the message does not contain an issue number.

What hooks are available?

If you look on Google you can find lists of all hooks and exactly when they execute and what parameters are passed to them. An easier starting point is to just look in the .git/hooks directory. There are a bunch of sample files in there. They all end in .sample They are just bash scripts. You can open them in a text editor. They have a few comments and a simple example. To use one of them just remove the .sample from the file name.

Why did I need Git hook?

The problem I was running into is that I like to include issue numbers in my git commits ie. [#123] However I am sometimes forgetful and forget to put them in. Not a problem if the commits are local, I can just do an interactive rebase and edit the commit messages. However, sometimes I get too eager to push my commits (because I want reassurance that all my CI tasks pass). Once I've pushed my changes, it's too late to do an interactive rebase. I immediately thought well, I can check for that with a Git hook and reject the commit if it doesn't have a issue number.

How does it work?

Here is my commit-msg hook. It's a pretty simple Python script. The sample hooks are all in bash, but you can use any scripting language. You could also use bash to call G-CLI and do things in LabVIEW if you want. I chose Python because I was having trouble getting the pattern matching to work in bash. Escaping special characters in bash strings can be a pain.

#! /usr/bin/python
#checks for [#xx] where xx is an issue number

import sys
import re
 
with open(sys.argv[1]) as f:
    first_line=f.readline()

if not re.search(r'^\[\#[0-9]+\]\s',first_line):
    print('Commit Rejected. No issue number found. Add [#nn] to front of commit message. To bypass use --no-verify') 
    sys.exit(1) # reject commit
else:
    sys.exit(0)

The first line just tells Git that this a python script. Then we import the system and regex modules. sys.argv[1] is the second argument passed to the hook - this is the temp file that holds the commit message. We read the first line of the commit message. We check that line against a regex which searches for a line that starts with [#dd] where dd is an issue number. I also check for a space after the ]. If we don't find the issue number, then we print a message and reject the commit by exiting with a code of 1. If we find the number then we exit with a code of 0. The exit code is what tells Git to abort the commit or allow it to continue. Zero means to continue. Anything else means to abort.

Caveats

There are several caveats with using Git hooks.

In SVN there was only one server and all the hooks ran on the server and everything had to go through the server.  So SVN was very good for centralized control. Not so with Git. There is no central server with Git. It is decentralized. Sure you may have a repository hosted on GitLab or GitHub, but as far Git itself is concerned it is just another remote repository (of potentially many) and there is nothing special about it.

Hooks live in the .git/hooks directory which is created locally when you clone a repository. They aren't stored in your repository and aren't versioned. You can fix this by putting them in a folder in your repository and writing a simple bash script to install them by simply copying them into the correct folder. You do have to rely on the user running the script after they clone the repository. If they forget or don't run the script, then the hooks are never in effect.

Another caveat is that hooks can be bypassed. git commit --no-verify will bypass many of the hooks (you'll have to google which ones). This can be useful sometimes, but it also means if you want to use hooks to enforce some convention on developers, well they can get around it. They can also get around any hook by simply deleting the hook from the .git/hooks directory.

So overall Git hooks are not great for enforcing things, particularly not locally on developer's computers. I don't view that as a major drawback, because well if you don't trust your developers, you have much bigger problems than making sure they add issue numbers to commits.

However if by convention you declare a central repo like your GitHub or GitLab repo as your single source of truth (remember Git itself doesn't care), then you have some options. There is specifically a pre-receive hook that executes when someone else tries to push commits into a server. You can use that to reject the push. That is how GitLab and GitHub set up protected branches and a variety of other features. Unfortunately, if you are using the web versions, you don't have direct access to the file system, so you can't create your own there. Apparently, if you are self-hosted then you do have access to the hooks on the server.

Taking this further

You could take this example further by using web requests and communicating with your issue tracker to make the issue exists, pertains to this project, and is still open before accepting the commit. Maybe you decide to use something like Arlo's Commit Notation. You could use Git hooks to enforce that. You can also use Git hooks to determine which branches are allowed to be merged into which and reject merges that aren't allowed. You could use Git hooks to run unit tests or VI Analyzer before committing or pushing. Or even to build your code. There are a lot of options here.

Looking To Get Better At Using Git

If you are looking to get better at using Git, we do have an online video course. It's about 7 hours of video broken into 10-15 minute chunks perfect for your lunch break. It starts from zero but you can jump around if you already know a bit and just want to focus on one aspect.