Custom git hooks — Automate your development workflow
Version control has become a central requirement for modern software development. Like many other Version Control Systems, Git has a way of firing off custom scripts that perform certain operations. These scripts are referred to as hooks. There are two groups of hooks: client side
and server side
. Client-side hooks perform client operations such as committing and merging, while Server-side hooks perform Git server operations such as receiving pushed commits.
What is covered in this article
In this article, we are going to create our custom scripts to ensure that:
- The staged codebase doesn’t contain certain strings e.g.
pdb
usage in python files orconsole.log
in javascript(pre-commit
). - The commit message is properly formatted and is conformant to a desired pattern(
commit-msg
). - You don’t rebase any commits that have already been pushed(
pre-rebase
).
Note: You can use any scripting languages of your choice to create hooks and here we are going cover the following topic inruby
and shell
Prerequisites
Before we start, ensure that you have git
installed on your system.
Secondly, you should be familiar with git
basic usage. If you need a head start, here is a good place to start.
Create sample project on github
and clone it locally. You’ll need it for a smooth read-work-along.
Getting started
The hooks are all stored in the hooks
subdirectory of the Git directory that’s .git/hooks
. If you list files in this directory, you will notice there are various hooks. You can implement any of them by removing the .sample
extension.
Open your terminal or powershell for windows;
Navigate to $ cd /path-to-your-git-repo/.git/hooks
Creating a Pre-commit hook
Create your custom pre-commit hook
$ touch pre-commit
Copy contents of the sample pre-commit to the custom pre-commit
$ cp pre-commit.sample pre-commit
Make the pre-commit script executable
$ chmod +x pre-commit
Open the pre-commit script and start the real scripting with your text editor
$ open -e pre-commit # mac os x
$ gedit pre-commit # linux
Edit the sample script to meet our expectations
Add the following line of code to ensure that there are no whitespaces at the end of a file
# If there are whitespace errors, print the offending file names and fail.git diff-index --check --cached $against --
Next we grep the staged files with .py
extensions and check for the existence of pdb
usage and print statements. Existence of these strings results in exit 1 thus aborting the commit
That’s it for the pre-commit
hooks. Save the file and exit the editor.
Save the changes. At this point you can test by adding print
statements or import pdb
to a python script.Stage the files changed and try committing them, the hook fires the following message and aborts committing.
changedfile.py:2:import pdb
Error commiting changes: Please remove pdb and its usage
Bravo!! You realize that the commit will be aborted with relevant error messages.
Creating a commit-msg hook
The commit-msg
hook takes one parameter, which again is the path to a temporary file that contains the current commit message. If this script exits non-zero, Git aborts the commit process, so you can use it to validate your project state or commit message before allowing a commit to go through. In this section of this chapter, I’ll demonstrate using this hook to check that your commit message conforms to a required pattern.
In the /path-to-your-git-repo/.git/hooks
directory of your sample project, create a new file named commit-msg
and make it executable as in the above.
We shall be using ruby
in this section to check for commit message and validate it to suite our standards. Note that you can define your own standards of a commit message for the team to adhere to.
In the above script, we use regular expressions to check and validate the format of a message to be like chore(module): message of the commit
Short of this format will result in the commit being aborted:
$ git commit -am 'test'
[POLICY] Your message is not formatted correctly
[STANDARD] Your message should be in the format: ‘feat(module): commit message’$ git commit -m "feat(test): testing tests"
[ch-git-hooks-989231] feat(test): testing tests
1 file changed, 1 deletion(-)
Creating a pre-rebase hook
The pre-rebase
hook runs before you rebase anything. You can use this hook to disallow rebasing any commits that have already been pushed. The example pre-rebase
hook that Git installs does this, although it assumes that next is the name of the branch you publish. You’ll likely need to change that to whatever your stable, published branch is.
/yourgitrepo/.git/hooks
We have covered three git hooks, all of which are are client-side
hooks. There are many other hooks you can customize in your project to control certain actions.
Maintaining hooks for a team of developers can be a little tricky because the .git/hooks
directory isn’t cloned with the rest of your project, nor is it under version control.
A simple solution to both of these problems is to store your hooks in the actual project directory (above the .git
directory). This lets you edit them like any other version-controlled file. To install the hook, you can create a symlink to it in .git/hooks
, or you can simply copy and paste it into the .git/hooks
directory whenever the hook is updated.
Happy coding!