October 02, 2020

Git is a powerful version control system that we’ve barely scratched the surface on over our last few posts. Today, we’re going to look at the automation power that Git can give you with Git Hooks.

Every repository gets hooks built in when you use the git init command. When a repository is initialized you get a hidden .git directory and inside that is a directory called hooks that will contain all your hooks. Open any git repository you have handy and use ls -a to see the hidden directory, then open it up in your favorite code editor.

To start you’ll see a bunch of files with .sample file extensions. These are exactly what they say, sample scripts that you could use in your projects. The files are named to correspond with the hook they run on. So post-commit.sample runs on the post-commit hook.

You can use pretty much any language to write a hook. The file is parsed according to the shebang notation at the top of the file. If you wanted to use node you’d use #! /usr/bindi/env node and your file will be parsed as a node file.

Before we dive into what you can do with git hooks, let’s take a look at some of the hooks that are available to you.

Types of Git Hooks

Commit Workflow Hooks

pre-commit is run before you even enter your commit message and it can be bypassed with git commit --no-verify.

prepare-commit-msg can be used to edit the default message you see in your commit message. Use it to give instructions to developers about what type of commit message they should be leaving. It can also be used to automate the contents of where the message is automatically generated for you, like merges or to add an issue number to your commit message automatically.

commit-msg can be used to validate the commit message for your project. Maybe you don’t want anyone to be able to put in a commit message that simply says “dealing with white space”. You can use this hook to detect the presence of the words white space and then exit and provide a warning to the user that they need to have a better commit message.

post-commit runs after all the commit hooks above. It’s most useful for a notification that a commit has been made.

Client Hooks

post-checkout runs after you’ve run a successful git checkout command. If you had a set of large files used on the site but didn’t want them in source control, you could use this command to move the files for you.

pre-push runs during a git push command before any objects are transferred to the remote repository.

Server Hooks

pre-receive runs when a client has pushed code to a remote repository. This can be used to check the code that is being pushed to make sure that it meets the criteria of your project before you accept the push.

post-receive runs after your remote repository has received the updates. This could be used to call a web hook which triggers a deployment process or notifying a chat room that a commit has been received and is ready for review.

Many of the hooks above can be set to run only on specific branches. That may mean when you use a post-receive hook only when someone has pushed code to the main branch that’s supposed to be ready to deploy. A list of developers could be notified to review the code and then deploy it. This way you would always have 2 sets of eyes on a deploy which can mean catching mistakes that a single developer can easily miss.

I’ve skipped some of the hooks that are available because I’ve never seen a need to use them. One set of hooks I didn’t talk about is the email workflow hooks. If you’re not accepting patches to your code via email, then you’ll likely never need them. You can find all the available hooks in the documentation.

In practice hooks I’ve used most are:

  • pre-commit
  • pre-push
  • commit-msg
  • pre-receive
  • post-commit
  • post-receive

Now let’s do something with these hooks.

Activating a WordPress Plugin with WP Cli and Git Hooks

For one client project this year I was adding a store, and still doing a few tasks on the main site. That meant the main site did not have any of our WooCommerce plugins installed or activated. I needed to develop the WooCommerce store on one branch and only once I was ready to push it all live, did I want to move WooCommerce over to main.

To start we’ll need a new branch called store. We can get this by using git checkout -b store. This creates a new branch and checks it out for us. Now let’s get the hook ready.

First we need to create the post-checkout hook with this command touch .git/hooks/post-checkout.

Next we need to make it executable. We can do this with the chmod command from terminal chmod +x .git/hooks/post-checkout.

Now open the file in your code editor of choice and copy the code below into your post-checkout file.

#! /bin/bash

wp plugin activate woocommerce

echo "activated WooCommerce"

wp plugin activate automatewoo

echo "activated AutomateWoo"

You can demo this by changing to any branch via terminal. You should see two lines telling you that WooCommerce and AutomateWoo have been activated. We know it’s working, but it’s not quite what we want because it will turn the plugins on every single time we change to any branch.

What we really want is to turn them on when we move to our store branch, and then turn them off when we are on our main branch. To do that we’ll need the hook to detect which branch we are one. Swap the contents of post-checkout with the code below.

#! /bin/bash

oldrev=$1
newrev=$2

branch_name="(git symbolic-ref HEAD 2>/dev/null)"

if [ "refs/head/store" = "$branch_name" ];then
  wp plugin activate woocommerce
 echo "activated Woo"

  wp plugin activate automatewoo
 echo "activated AutomateWoo"
fi

if [ "refs/head/main" = "$branch_name" ];then
 wp plugin deactivate woocommerce
  echo "deactivated Woo"

  wp plugin deactivate automatewoo
  echo "deactivated AutomateWoo"
fi

This code starts by assigning the branch we are checking out to the branch_name variable. Then we have two if statements. The first checks to see if we have moved to the store branch. If we have it uses WP CLI to activate WooCommerce and AutomateWoo.

The next if statement checks to see if we are on the main branch. If we are, it will deactivate the plugins with WP CLI and tell us about it in the terminal.

Controlling Git Workflows with Git Hooks

In a previous post on Git I talked about different Git workflows. One very common use case for hooks is to stop anyone from committing code directly to the main branch. You can use a hook to make sure that all code is merged from a different branch into main.

Start by renaming pre-commit.sample to pre-commit and then make it executable as I’ve described above. Next grab the code below and use it in the pre-commit file.

#! /bin/bash

username=$GIT_AUTHOR_NAME
branch="$(git symbolic-ref HEAD 2>/dev/null)"

if [ "$branch" = "refs/heads/main" ]; then
echo "WHOA that was '"${branch}"' you should not do that. Stop doing silly stuff and create your own branch and merge it."
exit 1 # if you remove this it won't block the commit but it will send the message to slack

fi

This checks to see if we’re on the main branch, and if we are, the commit is stopped. Then it prints a reminder to the user that they shouldn’t be committing directly to the main branch.

Remember many places are changing to main as their branch. Older projects may need master in place here if they haven’t updated.

You could even take this a step further and use cURL to access the API of a chat app and then complain publicly that someone tried to commit to main.

The only limitations of git hooks are your imagination. You could use them to stop someone from committing if a TODO is present in their code or to stop whitespace at the end of a file.

If you have some part of your workflow that is a continual stumbling block, look at hooks to automate it, so that you don’t have to remember.

Curtis McHale
Curtis McHale

Curtis is a husband, father, developer and business coach. He specializes in helping people build a business that lets them spend time with their family instead of working all the time.

We use cookies to understand how you interact with our site, to personalize and streamline your experience, and to tailor advertising. By continuing to use our site, you accept our use of cookies and accept our Privacy Policy.