Git Pre Commit
Git hooks to enforce uniform style and prevent endless discussions
When you have multiple git repository’s with code one thing that teams often like is a uniform style. This can prevent discussions about style in merge requests and keep the team focussed on the things that really matter (functionality).
In this post we look at how to create a reusable githooks configuration for multiple projects with the help of pre-commit. Pre-commit is a python based tool that hooks into the git hooks system.
- Install pre-commit
- Create a hooks repository
- Make the hooks repository available for other projects
- Testing the hooks
- Running hooks in different stages
Install pre-commit
First install the python package pre-commit so that we are able to use it.
pip install pre-commit
Create a hooks repository
To get started create a new folder and make it a git repository. This way you can use the config over multiple machines. Push it to an origin of you choice (Github, Gitlab, CodeCommit, etc.).
mkdir ~/my_githooks && cd ~/my_githooks && git init
Create the .pre-commit-config.yaml in the root of the project. In here we will define the hooks we are going to use.
touch .pre-commit-config.yaml
Add the following lines to the config file to have validate json and have it nicely formatted. A list of a lot of available hooks can be found here. There are hooks for all types of linting, preventing the commit of private keys, scanning for security vulnerabilities and if you miss something you can always create your own.
|
|
Make the hooks repository available for other projects
When we initialize a git repository quite a few files and folders are created. Default git creates a few example hook
files under .git/hooks/*.sample . You can add a pre-commit file here manually to use it in 1 project. For this post
however we are going to change the configuration variable init.templatedir based on a path to say that the hooks from
our repository we just created should be used.
Create ~/.gitconfig_hooks to add the configuration changes.
touch ~/.gitconfig_hooks
In there add the following lines to tell where the hooks can be found.
[init]
templatedir = ~/Documents/my_githooks
In the .gitconfig file add the following lines to include the just created templatedir for all git projects
under ~/Documents/projects (after you do git init on a repository):
[includeIf "gitdir:~/Documents/projects/"]
path = ~/.gitconfig_testhooks
This configuration will look for an executable file called hooks/pre-commit. Let’s create that file.
touch ~/my_githooks/hooks/pre-commit && sudo chmod +x ~/my_githooks/hooks/pre-commit
In this file we need to say that it should run our githooks from the yaml file. Add the following lines:
#!/usr/bin/env bash
pre-commit run --config "$(git config init.templatedir)/.pre-commit-config.yaml"
Testing the hooks
Create a new repository to test the hooks under ~/Documents/projects/. For example:
mkdir ~/Documents/projects/test_project && cd ~/Documents/projects/test_project && git init
If you now look at the .git/hooks location you will see a pre-commit file. It contains the same data as the file in ~/my_githooks/hooks/pre-commit.
To use the hooks add a json file and add some content to it:
echo "{\"key_one\":1}" > example.json && git add example.json && git commit -m"testing"
As you can see it will run all the hooks we added to the .pre-commit-config.yaml and change the code to the changes required by the hooks. If you add and commit again the code will now be committed because it’s valid.
git add example.json && git commit -m"testing"
Running hooks in different stages
Right now all the hooks we ran where in the stage called commit. We can make this explicit by changing the .pre-commit-config.yaml to the following
|
|
Let’s say we have the requirement to always add our ticketnumber before our commit message in a commit. For this we can hook into a different stage called commit-msg. To use this inside our hooks we can add the following to our .pre-commit-config.yaml so that when our branch name starts with JIRA-number it includes JIRA-number: in front of our message.
|
|
To have it work in the global way we use so far we need to create the corresponding file.
touch ~/my_githooks/hooks/commit-msg && sudo chmod +x ~/my_githooks/hooks/commit-msg
The latest commit message is saved inside the .git/COMMIT_EDITMSG" file. This file needs to be added via the script variable if you use this global hook. If you only use pre-commit on 1 repo it’s automatically added. We also need to tell that we want to run only the commit-msg hook, because otherwise every hook will be run. In ~/my_githooks/hooks/commit-msg include the following script:
#!/usr/bin/env bash
pre-commit run --hook-stage commit-msg --commit-msg-filename "$(pwd)/.git/COMMIT_EDITMSG" --config "$(git config init.templatedir)/.pre-commit-config.yaml"
Testing the new hook
Create a branch called JIRA-1234 inside ~/Documents/projects/test_project.
git checkout -b JIRA-1234
Make a change to the example.json file and create a commit. I the following image you can see the outcode if we commit the message four.

Conclusion
In this post you have seen how to use reusable githooks over multiple repositories. You have used them for different stages, validating code and modifying commit messages. Have fun and success applying them in your use-cases.