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 comes 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
- Go to any folder and create a new folder 'myproject'.
- Cd into your project and initialize the git repository.
- 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/
Step 2 - Locate your Hooks folder & view the files
- Cd into your '.git' folder and you should see the following list of folders.
- Cd into the 'hooks' folder, you should see the following list of files:
All the files with the extension '.sample' mean that they are present as a sample and they will not be executed by default.
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
- Change the permissions to make it executable
$ chmod +x pre-commit
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.
To choose 'shell'
To choose 'bash'
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 'firstname.lastname@example.org'. (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 'email@example.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 firstname.lastname@example.org if [ "$useremail" != "email@example.com" ] then cat <<\EOF # OUTPUT THE ERROR ON THE TERMINAL Error: user.email not set to "firstname.lastname@example.org" EOF # EXIT WITH 1 INDICATES ERROR exit 1 fi
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 'email@example.com', then throw an error on the terminal.
Run the Commit to verify if the Git hook is invoked
Go to the 'myproject' folder
$ cd myproject
Create a new file
$ nano demo.txt
Enter some text & save the file!
Check the current user email set in git-config
Git add & commit the file to the local repository.
$ git add demo.txt $ git commit -m "Testing Pre-Commit Git Hook"
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
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.
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.