Install, configure and use git hooks to automate your workflow with distributed team

My team uses Git as the center of our development process, leveraging all those cool features git provides us - git workflow, feature branching, switching branches for code reviews, Pull Requests and all such cool stuff. Using git is an integral part of our workflow, which means all the development happens over git branches, creating a Pull Request when you need a review, merging them into staging branch so that our CI servers can pick them up and get it deployed and so on.

Tl;DR: I am not here to explain the benefits of git driven workflow(you already are aware of that). This article explains some use cases of git hooks to automate some of those redundant tasks that you just hate doing or completely forget about.

So where does git hooks comes in ?

On any typical day, we work on our branch, switch to another branch for code review when someone in the team says - Hey can you hop in and see if this implementation can be optimized ?

By using git, it helps us to do this branch switching. But switching git branches in between your work also brings in some downsides.

This means we switch branches a lot, either to see someone’s code and review it or to kick off a new branch for a new feature. Over time this scenario seemed to happen a quite a few times:

Use case 1: Installing newly added dependencies, Running migrations, Generating new assets.

Switch to master and pull in updates

Start working on a new feature in a new branch

Write something and run tests

OMG!! the tests are failing..!!

Turn to the team in slack: "Guys, I’m getting an error on the stuff you added, did you get it?"

The team says: "No. Did you install the Gems and ran the migration ?"

Me: "Ah my bad! you got me there, did not run bundle install" or "Ah dammit, I forgot to migrate the Db"

Developers often need to perform a set of tasks or operations when they pull a branch or checkout to a new branch, and if by any case these steps are missed you might end up having unexpected behavior. For the experienced pals, it is easy to debug, but for the inexperienced guys, it might take some time trying to figure out that they missed some basic steps that should have been done.

Use case 2: Prevent the developers from accidentally committing code which were used for testing or debugging.

Developers often have the habit of leaving behind some of the debug statements that they wrote while developing or testing the code in the final branch, not intentionally!. Some of the debug statements just puts/emits some information like console.log(), Rails.logger.info(), IO.puts etc while some others can expose confidential information regarding the stack itself puts caller, and in worse case it might even have a full REPL code like debugger, binding.pry, IEx.pry .

And imagine the worse case when one of your developers leaves behind a binding.pry REPL, just like the one below to the final code and your CI is trying to run those tests.!!

# ...
it 'should return status 422' do
  binding.pry
  expect(response.status).to eq(422)
end
# ...

BOOM!! - Your CI servers end up in a limbo waiting for someone to get itself out of that debug statement.

Git hooks to the rescue.!

While there are many approaches preventing the above scenarios from happening, I chose to use a bunch of git hooks to achieve the objective. The benefit of using git hooks is that it can distribute to your teammates and can be reused.

What is a git hook?

A git hook is basically a bash script that runs on certain occasions in the execution process when you are working with git. They will take out some of those redundant tasks that you just hate doing or completely forget about.
They can do a bunch of tasks like check code style guide violations, security audits, check if debugger statements are present and a whole lot of useful stuff.

Different types of Git hooks.

Git supports custom script triggers through hooks. These hooks give you a chance to inject functionality at particular points in the standard pipeline:

Here are the basic available git hooks:

  • Before committing ("pre-commit")
  • Before writing a commit message ("prepare-commit-msg")
  • After writing a commit message ("commit-msg")
  • After committing ("post-commit")
  • Before a rebase ("pre-rebase")
  • After a checkout ("post-checkout")
  • After a merge ("post-merge")
  • Before receiving a push ("pre-receive")
  • After receiving a push ("post-receive")
  • Before receiving a push, run once per branch ("update")
Creating a Git Hook

Git comes with a lot of pre-available hooks. These default available hooks are included in every repo that you create by default and these can be found inside your .git/hooks dir.

To create a new hook, all we need to do is to create a file in the .git/hooks folder with the name of the hook you want to attach to, in my case pre-commit, since I want this to happen every time I am about to commit something.

So here is what one of my pre-commit hooks looks like for a typical Rails project

#!/bin/bash

# Grep through modified files for forbidden words and reject commit if found

# Separate more file types with pipes here
FILE_PATTERN='\.(js|html|rb|yml)(\..+)?$'

# Separate more forbidden strings with spaces here
FORBIDDEN=( console.log puts logger debugger binding.pry )

for i in "${FORBIDDEN[@]}"
do
  git diff --cached --name-only | \
        grep -E $FILE_PATTERN | \
        GREP_COLOR='4;5;37;41' xargs grep --color --with-filename -n $i && \
        echo 'Debugger code found:'  $i 'Please remove them before commiting' && exit 1
done

exit 0
Gotchas

Note: Permissions of Hooks to Executable
When you’ve written a git hook, don’t forget to make it executable. Git will not tell you why it’s skipped and go ahead and skip the hook altogether.

Solution:

chmod ug+x .git/hooks/*
Wrap up

Git Hooks are pretty cool if your workflow is built around git like lots of teams do nowadays the hooks are a cool place to tie in your day to day stuff that tends to be forgotten and generate unneeded debugging and such. Take a look at available hooks and think about your routine, I’m pretty sure you will find something you can delegate out to git and stop worrying about it every day.

As they say:

Anything that can be automated should be automated.!!

Further Reading:
  1. Git-scm Book
  2. Git-scm docs
  3. Atlassian Tutorial