DEV Community

Cover image for What are Git Hooks?
skaytech
skaytech

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

What are Git Hooks?

Introduction

In this article, we'll look at what Git Hooks are, why using Git Hooks are beneficial, and some practical examples of how to use them for development.

What are Git Hooks?

Git hooks are scripts that run automatically every time a particular event occurs in a Git repository. Through the scripts, you can customize Git’s internal behavior and trigger customizable actions at key points in the development life cycle.

There are two kinds of hooks:

  • Client-side (local) hooks - They are prompted by the events on the local repository. For example, when a developer commits or merges the code.
  • Server-side (remote) hooks - They are prompted by the events on the server hosting the repository. For example, when a developer pushes the code.

Why should you use Git Hooks?

As a part of the learning process, the question of 'Why to use a certain piece of technology?' is very vital to understand it's true intent or purpose of existence.

In my opinion, the adoption of well-defined processes and the use of automation greatly enhances the team's productivity to deliver software in a predictable manner.

A developer in a typical day writes code and commits to the project repository. A senior developer, in general, adopts to writing meaningful commit messages when performing a code commit. However, the same cannot be expected of junior developers who have just come into the project.

As a check-point to this, the project lead could establish a standard commit message format and must ensure that it must be followed by other team members. A good example of that could be to ensure that every commit message should start with a JIRA task Id. This becomes an excellent use-case of a Git hook. All you have to do is customize the 'pre-commit' git hook, that can intercept any commits made by the developer and validate for a JIRA task Id to be present.

The above mentioned is one simple example. In reality, we can do much more with the hooks such as check for code style (run lint or some equivalent), run static code analyzer tools such as Sonarqube or an equivalent, check for documentation of API methods if they are newly introduced and so much more. The possibilities are quite endless.

Implementing a Hook

Git hooks are a built-in feature that come with every Git repository. Upon initializing a new project, Git automatically populates the hooks folder with template files.

Let us take a step-by-step approach:

Step 1 - Create a New Project folder & Initialize Git

  1. Go to any folder and create a new folder 'myproject'.
  2. Cd into your project and initialize git repository.
  3. You'll see a message shown below & a '.git' folder created.
$ mkdir myproject
$ cd myproject
$ git init

//Ouput -> Initialized empty Git repository in <<folder>>/myproject/.git/
Enter fullscreen mode Exit fullscreen mode

Step 2 - Locate your Hooks folder & view the files

  1. Cd into your '.git' folder and you should see the following list of folders.

Alt Text

  1. Cd into the 'hooks' folder, you should see the following list of files:

Alt Text

  1. All the files with the extension '.sample' means that they are present as a sample and they will not be executed by default.

  2. Each file is triggered at a particular event during the git life cycle. A code of '1', signifies an error, and the process is exited at that point and a code of '0' signifies that the process went through fine.

Step 3 - Install/Enable the Git hook you want to execute

To enable the hook, it is a simple as removing the extension of '.sample' from the file name. As soon as we do it, Git immediately will recognize that it should execute the file. In the above example, the file 'pre-commit.sample' is renamed to 'pre-commit'.

  • Rename the file by removing the '.sample' extension.
$ mv pre-commit.sample pre-commit
Enter fullscreen mode Exit fullscreen mode
  • Change the permissions to make it executable
$ chmod +x pre-commit
Enter fullscreen mode Exit fullscreen mode

Step 4 - Choose the language

The default files are written in shell-scripts. You can use any scripting language as long as it can be run as an executable. The supported languages are Bash, Python, Ruby, Perl, Rust, Swift & Go.

To choose the language of your choice, open up the file in your code editor and using the shebang (#!) sign indicate the language, so that, Git knows how to interpret the subsequent scripts.

For example:

To choose 'shell'

#!/bin/sh
Enter fullscreen mode Exit fullscreen mode

To choose 'bash'

#!/bin/bash
Enter fullscreen mode Exit fullscreen mode

Practical Examples

Check for a valid email before committing the code

It is very common for developers to have multiple Github accounts and one account could be linked to a personal email account and another one could be linked to the work email account.

Let us look at creating a 'pre-commit' Git Hook in order to ensure that any commits made by me are from the email Id 'skay@gmail.com'. (assuming this is the work email address)

Pre-Commit Git Hook

The pre-commit Git hook to check if the user committing the file has the user email address set to 'skay@gmail.com'.

#!/bin/sh

# Make sure the email is set properly before committing the file
useremail=$(git config user.email)

# Check if the git config useremail DOES NOT MATCH skay@gmail.com
if [ "$useremail" != "skay@gmail.com" ]
then
        cat <<\EOF

# OUTPUT THE ERROR ON THE TERMINAL
Error: user.email not set to "skay@gmail.com"
EOF

# EXIT WITH 1 INDICATES ERROR
        exit 1
fi
Enter fullscreen mode Exit fullscreen mode

Things to Note:

When the user attempts to commit the file, the script will be run.

The following steps are what happens in the file:

  • The first line '#!' signifies that the script is a shell script.
  • Fetch the 'usermail' from git config user.email.
  • Check if the 'useremail' does not match 'skay@gmail.com', then throw an error on the terminal.

Run the Commit to verify if the Git hook is invoked

Step 1

Go to the 'myproject' folder

$ cd myproject
Enter fullscreen mode Exit fullscreen mode

Step 2

Create a new file

$ nano demo.txt
Enter fullscreen mode Exit fullscreen mode

Step 3

Enter some text & save the file!

Alt Text

Step 4

Check the current user email set in git-config

Alt Text

Step 5

Git add & commit the file to the local repository.

$ git add demo.txt

$ git commit -m "Testing Pre-Commit Git Hook"
Enter fullscreen mode Exit fullscreen mode

Step 6

If the Git 'pre-commit' Hook had fired correctly, then you should see the following error on the terminal, since the git config user email (skaytech30@gmail.com) does not match 'skay@gmail.com'.

Alt Text

Congratulations! You've just created your first Git Hook and run it successfully.

Git Pre-Push Hook

Another great example explained of a Pre-Push hook is explained superbly by my friend Simon in the tweet below.

Bypassing a Hook

A Git Hook can be easily bypassed or over-ridden by the flag '--no-verify'. In the above example, if you would like to exempt running the 'pre-commit' hook, then you would have to include the no-verify flag as shown below.

$ git commit -m "Testing Pre-Commit Git Hook" --no-verify
Enter fullscreen mode Exit fullscreen mode

Version Control - Git Hooks

As you would have observed, Git hooks are local to any repository. A simple workaround for this could be to create a 'scripts' folder in the project repository to enable version control of git hook files, so that, they can be extended to be used by the whole team.

Conclusion

Adding Pre/Post Git Hooks is a simple way to ensure that there are checkpoints along the software development life cycle. In addition, hooks can be extended in any way to aid in repetitive testing that can be triggered upon specific file commits.

While it might take some time to set it up in the beginning, you would reap a lot of benefits, especially as the team grows.

If you would like to read further, here is a very good article by Atlassian on Git hooks.

I hope you enjoyed the article. Do let me know your feedback and comments about the article.

If you enjoyed this article, you may also like:

Top comments (3)

Collapse
 
vlasales profile image
Vlastimil Pospichal • Edited

pre-commit

#!/bin/sh

# Make sure the email is set properly before committing the file
required="skay@gmail.com"
useremail=$(git config user.email)

# Check if the git config useremail DOES NOT MATCH your required
if [ "$useremail" != "$required" ]; then
        echo "Error: user.email not set to \"$required\"" >&2
        exit 1
fi
Collapse
 
skaytech profile image
skaytech

Great suggestion on the error message to be generic :-)

Collapse
 
vlasales profile image
Vlastimil Pospichal

pre-push from the video (modified):

#!/bin/sh

ref=$(git symbolic-ref HEAD)
if [ "${ref#*/*/}" = 'master' ]; then
    echo "ERROR: Not alowed to push to master" >&2
    exit 1
fi