DEV Community

Cover image for Gated Commits with Git
David Ojeda
David Ojeda

Posted on • Edited on • Originally published at blog.davidojeda.dev

Gated Commits with Git

A gated commit, also called a pre-tested commit, is an integration pattern in which a commit is not approved until a set of tests are ran against the code being commited. In other words, the commit does not go through if the test suite fails.

Why do you want this? It makes your application more resilient to change since now you are running a set or sub-set of your tests even before that code is available to anyone else.

I am going to show you how to implement a gated commit pattern with Git. In this example, our unit test suite will be the gate to allow our commits to make it to the codebase.


What do you need?

  • Git
  • Your application's Git repo
  • A test suite

Starting off: Git Hooks

We want our tests to run before a commit goes through. Git allows us to run custom commands just before that event happens thanks to Git hooks. I am not going to go into the details on how they work, but conveniently for us, there is a hook called pre-commit. This hook is executed just before the commit happens. Perfect spot for our test suite to run.

Setting up a pre-commit hook

In your Git repo, there is a folder named .git in which the hooks are stored. If you have never modified any hook, your .git directory structure will look like this:

.git directory structure

To create our hook, we need to have a file called pre-commit (no extension required) inside our hooks directory. Let's create it. The only thing the file needs to have is the command you use to run your tests. Also, don't forget to make the file executable (chmod +x).

If your application is, let's say, a Ruby application, you probably run your tests using rake. If that's the case, your pre-commit file will look like this:

rake test:units

Or if you are into the JS hype, you can probably have this in your file:

npm tests

Independently of the language/framework you are using, your pre-commit hook needs to have the command to run your unit test suite. And, as long as the code inside the hook returns a zero exit code, the hook will allow the code to be commited. Otherwise, the commit will be rejected.

Testing

At this point you can go ahead and make a commit and see how our tests are run (and hopefully pass), thus opening the gate and letting the commit pass uncontested.

In the following example I am using a Grails application, and the pre-commit hook contains the following code:

grails test-app -unit

Successful gated commit

Success! 🥳

In the case that the test suite fails:

Unsuccessful gated commit

Sad face 🙁

Wrapping up

We have just created a Git pre-commit hook that contains specific commands to execute our app's unit test suite. Whenever a commit is issued, our tests run. If tests pass, we have a successful commit, if not, commit is rejected.

You can extend your tests of the pre-commit hook and build something as complex as you need. You can, for example, run a linter tool to make sure style guidelines are being followed. Or take it to the next level and integrate it with your Continuous Integration flow using additional hooks.

Hope this helps you build more resilience into your codebase and, ultimately, deliver more value to your customers in a safe and rapid fashion!

Top comments (1)

Collapse
 
david_ojeda profile image
David Ojeda

For your first question there is a simple solution that can help you. Use the command git commit without specifying anything else. The hook will run and, if tests passes, your default editor will open for you to write your commit message details.

For your bonus question, it is indeed possible. There is a Git hook called commit-msg that allows you to check your commit message format. This is an example from the Git documentation (this would go into a 'commit-msg' file in your .git/hooks folder as well):

#!/usr/bin/env ruby
message_file = ARGV[0]
message = File.read(message_file)

$regex = /\[ref: (\d+)\]/

if !$regex.match(message)
  puts "[POLICY] Your message is not formatted correctly"
  exit 1
end

The commit will fail if the message does not match the defined regex. This hook is written in Ruby, but you can also use a Bash script if you want to.

Here is another tip: it can happen that your tests passes, but then your commit message format is incorrect. So, you need to change the format and then commit again, which means that you also wait for tests to finish, yet again. You can skip the tests with the option --no-verify. Just make sure to use this option once you are sure all tests have passed 🙃