Intro to Client Side Git Hooks

Automation is everything when it comes to the process of deploying code. Even the smallest improvements over time can have a massive impact on the amount of manual processes you have to endure in building and shipping your software.

There are a lot of places to inject automation in the process of writing, building, and deploying code. When writing code on a team and on increasingly large projects those challenges and opportunities only grow. Things like linting, automated tests, and GitHub checks help you reduce toil when pushing, reviewing, merging, and delivering code.

Screen Shot 2019-08-12 at 10 20 40 AM

There are many solutions to these problems, and most focus on the most clear gatekeeper when it comes to deploying code - the build step. Your build should be able to detect problems automatically without human intervention. Everything from code style to performance to server health can be tested and certified in a build before you ever push code to production. The build enforces your rules and keeps developers honest in a way manual intervention never will be able to.

However, there are checks that can save even more time when pushed forward into the pre-build process.

A good example is developers committing and pushing bad code. Not merging bad code, but just simply writing bad code and commiting it to source control in their own feature branches. This might not seem like a huge problem - after all protected branches mean you can enforce correctness with build checks before approving merges - but it can introduce manual effort and cause frustration for developers, both of which are things that could easily be avoided with client-side git protection.

Introducing Git Hooks

Git Hooks are a way to run scripts when certain Git commands are run. There are hooks on both client-side Git processes and server-side processes where you can react to changes in the Git workflow. We’ll focus on the client-side hooks, where we can protect ourselves before we’re even done with a commit.

Client-side Git Hooks

The client-side hooks include hooks for multiple Git processes including committing, email workflows, and other processes like checkout and merge. For now, let’s focus on the commit workflow (but check out the rest of the list, there’s a lot you can do!).

These client-side commit hooks really fall into two categories:

  1. The code in your commit
  2. Formatting/enforcing a proper commit message

We’ll start with the first category, using pre-commit to analyze the code we’re about to commit before we even consider completing the commit with a message.

Writing a Git pre-commit hook

First, you need to designate a location for your hooks in your repository. For our example, we’re going to create a directory called .githooks in the root of our repository using these commands:

mkdir .githooks
cd .githooks
touch areYouSure.sh

Open up areYouSure.sh in your favorite editor. Writing a hook is both extremely simple and overwhelmingly open-ended. You just need a script that returns an exit code, 0 for success and non-0 for failure. Anything that isn’t 0 aborts the commit altogether.

Let’s write a very simple script that just asks our developer if they’re sure they want to commit.

#!/usr/bin/env bash

# Check that we want to commit.

read -p "Are you sure you want to commit this (y/n)? " answer
case ${answer:0:1} in
    y|Y )
        exit 0 # If yes, success!
    ;;
    * )
        exit 1 # If no, sorry yo.
    ;;
esac

Finally, make sure you make this new file executable by running

chmod +x .githooks/areYouSure.sh

Now let’s figure out how we can have Git actually run this script automatically as part of the pre-commit hook.

Add a custom pre-commit step

In our .githooks directory we’ll create a new script to handle our custom pre-commit actions with these commands:

cd .githooks
touch pre-commit

Open the new pre-commit file in your favorite editor. Let’s update it to do the basic things we need.

#!/bin/sh

# Specify the directory for the hooks.
# We'll use the current one (.githooks)
hookDir=$(dirname $0)

# Specify the hooks you want to run during
# the pre-commit process:
"$hookDir/areYouSure.sh"
# && "hookDir/add-your-own-scripts-here"

This script will specify the directory where we’ll store our hooks (the current .githooks directory) and then it will specify the scripts that should run as part of pre-commit. In our example we just have the single areYouSure.sh script.

You could code your script code directly into pre-commit. We are instead abstracting this code into its own script to enhance readability, reusability, and to enable us to easily add more scripts to our pre-commit step in the future.

Finally, make sure you make this new file executable by running

chmod +x .githooks/pre-commit

Tell Git to use our custom hooks

After you’ve completed the steps above, you should have a new .githooks directory containing the pre-commit script and our new custom script areYouSure.sh. We can go ahead and commit these files to source control. If you do this now, you’ll notice that you don’t get our ‘are you sure’ popup in the command line.

The reason our hooks aren’t run, is we still need to configure Git to let it know we have a new location for pre-commit for this repository. To do that, run this command in the root of your repo:

git config core.hooksPath .githooks

This tells Git to configure the hooks path for this repo to our custom .githooks directory. Now, whenever git runs the pre-commit step it will defer to our custom actions in our own pre-commit script.

Now, when you start a new git commit, you should see our areYouSure.sh prompt. A Yes answer should continue your commit (moving on to commit message steps) while a No answer should abort your commit.

Next Steps

Congrats! 🎉 You have successfully written and configured a custom pre-commit hook in your repository!

Now, you can continue to improve your pre-commit workflow by simply adding new scripts to .githooks and specifying which hooks you want to run in your custom pre-commit script.

A few resources to help continue your Git Hooks journey:

Thanks for reading, if you have questions or thoughts on pre-commit hooks or want to share the awesome stuff you’ve done with git automation - say hi to me on Twitter @ajkueterman!