Git has a robust system of hooks. Hooks are some scripts that fire off when specific actions occur. For example, you may want to make sure your code builds successfully before pushing it to the upstream. You can use Git hooks to define a script that builds your code when you issue git push
command. Then, Git pushes the code only when the script runs successfully. If there is a build error, the push will not happen.
Most Git hooks are local to your repository, called client-side hooks.
It’s important to note that client-side hooks are not copied when you clone a repository.
It means if you add a hook to your repository, your coworker working on a fork of the repository, does not have access to your hooks. It makes it challenging to share your hooks with others. Even if you email the script to your coworker, how do you update his copy of the script when you make a change in your copy?
Pre-commit is a beautiful tool that makes syncing and updating the hooks a breeze. You can see its documentation to see how to set it up.
Here, I explain the process of creating a Git hook that can be used with pre-commit.
Create a test repository
We will use this repository to test the hook.
$ mkdir "test-my-hook"
$ cd "test-my-hook/"
$ git init
Initialized empty Git repository in /Users/talha/Repos/temp/test-my-hook/.git/```
{% endraw %}
## Enable pre-commit in test repository[](#enable-pre-commit-in-test-repository "Permanent link")
Create a file {% raw %}`.pre-commit-config.yaml`{% endraw %} in the root of the repository.
{% raw %}
```shell
$ pre-commit sample-config > .pre-commit-config.yaml
$ git add .pre-commit-config.yaml
$ git commit -m "add pre-commit configuration"
Add a test file in test repository
Add a file on which we want pre-commit to run the hook.
$ echo "some random text" > example.text
$ git add example.text
Notice, I have staged the file example.text
but I have not committed it.
Now that we have a repository ready to run our test hook, let’s move to the next step: creating a pre-commit hook.
Create a hook repository
Create a new repository, outside “test-my-hook” directory, where you will define your pre-commit hook.
$ cd ..
$ mkdir pre-commit-test-hook
$ cd pre-commit-test-hook/
$ git init
Initialized empty Git repository in /Users/talha/Repos/temp/pre-commit-test-hook/.git/
Create pre-commit hook configuration
From the documentation, we know a hook repository must contain a .pre-commit-hooks.yaml
file. Let’s create it.
$ touch .pre-commit-hooks.yaml
Now edit the content of this file,
- id: "test-hook"
name: "Test Hook"
entry: run-test-hook.sh
types: [text]
exclude: ".yaml$|.yml$"
language: "script"
description: "This is an example hook to demonstrate how to create a pre-commit hook"
args: [--example1, --example2]
id
is the id of the hook. Users of your hook refer to your hook using this ID.entry
is the name of the executable file that pre-commit runs. Think of it as themain()
of a C program.types
are the types of file on which this hook runs. You can also usefiles
.exclude
is a Python regular expression to exclude specific files. In this case, it tells pre-commit to not run the hook on files that have.yaml
or.yml
extension.language
tells pre-commit how to install the hook. If you use'script'
, it means your hook is a Bash script.args
this is optional. It is used to pass arguments to your script. You can define it or leave it for your users to define it in their configuration. Of course, if your script does not accept any argument, thenargs
is redundant. Here we pass two arguments,--example
, andexample2
.
You can use other languages like Python, to create your hook. But in that case, you must provide installation instructions so that pre-commit can install the hook. For example, in the case of Python, you need setup.py
.
A Bash script does not require any installation or extra tools. Bash is ubiquitously present wherever Git is, which is why Bash is preferred to write the hooks.
Create the Hook Script
Now we are going to add some code to the run-test-hook.sh
file.
#!/usr/bin/env bash
echo "The hook is running"
echo "$@"
Commit your additions.
$ git add .pre-commit-hooks.yaml run-test-hook.sh
$ git commit -m "add an example hook"
Test your hook
Luckily, you do not have to publish your hook first to test it. You can use pre-commit to run this hook locally.
Go to the test repository we created to test the hook.
$ cd "test-my-hook"
Run the following command in it,
pre-commit try-repo ../pre-commit-test-hook/ test-hook --all-files --verbose
pre-commit try-repo
is the command and the switch.../pre-commit-test-hook
is the path to yourpre-commit-test-hook
repository on your file system. This path depends on where you have created these repositories.test-hook
is the ID of the hook. Remember it?--all-files
runs the hook on all the supported files.--verbose
is important for debugging. It displays the output of theecho
statements in the Bash script.
The output of the hook
When you run the hook, with the verbose
option, you should see the following output besides a bunch of other pre-commit logs.
The hook is running--example1 --example2 example.text
Remember the --example1
and --example2
arguments that we mentioned earlier?
Pre-commit passes those arguments first and then the list of files on which the hook could run to our script.
Notice, it does not pass .pre-commit-config.yaml
file although it is a text file and present in the repository. It is because we have excluded all files ending in .yaml
or .yml
from the hook.
What’s next?
Now that you have the hook running, you can modify your copy of run-test-hook.sh
. You can use a Bash script to parse the switches and arguments, using getopts
for example.
Then you can use Bash and other command-line utilities to modify the files.
When your hook is ready, you can push it Github so that others can use it.
TekWizely/pre-commit-golang has some brilliants scripts that you can use to learn and improve your hooks. I have created a simple hook for my Go projects, which you can see here, talha131/pre-commit-golang.
Top comments (0)