How to set-up pre-commit hooks on a project¶
We add pre-commit hooks in order to run various linting and auto-formatting tools over the source before they are committed and pushed to GitLab. This how-to guide covers how to add pre-commit hooks to an existing project.
Info
If you are using one of our boilerplates, an appropriate set of hooks will already be present.
Projects should usually have a standard set of pre-commit hooks and some additional language-specific hooks
Standard hooks to add to all projects¶
Add the following hooks to all projects, by placing them in .pre-commit-config.yaml
at the root
of the project:
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
args:
- --unsafe
- id: check-json
- id: check-toml
- id: check-xml
- id: check-added-large-files
- id: check-executables-have-shebangs
- id: check-merge-conflict
- id: check-symlinks
- id: detect-private-key
- id: mixed-line-ending
- id: pretty-format-json
args:
- --autofix
- --no-sort-keys
- id: debug-statements
- repo: https://github.com/DavidAnson/markdownlint-cli2
rev: v0.14.0
hooks:
- id: markdownlint-cli2
args: ["--fix"]
language_version: 22.10.0
Warning
As the rev
parameter above is hard-coded, you should also
set-up renovatebot to keep this and your project's other
dependencies up-to-date.
Python-specific hooks¶
Additionally, the following pre-commit plugins should be added to .pre-commit-config.yaml
for
Python projects (replacing the rev parameters
with latest releases of the plugins):
- repo: https://github.com/python-poetry/poetry
rev: 1.5.1
hooks:
- id: poetry-check
- repo: https://github.com/editorconfig-checker/editorconfig-checker.python
rev: 2.7.2
hooks:
- id: editorconfig-checker
args: ["-disable-indent-size"]
- repo: https://github.com/psf/black
rev: 23.3.0
hooks:
- id: black
- repo: https://github.com/timothycrosley/isort
rev: 5.12.0
hooks:
- id: isort
- repo: https://github.com/pycqa/flake8
rev: 6.0.0
hooks:
- id: flake8
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.4.1
hooks:
- id: mypy
additional_dependencies: [types-requests, types-oauthlib]
args: [--explicit-package-bases]
Java-specific hooks¶
Additionally, we use the
google-java-format
hook to ensure that Java source is formatted consistently. Add the following to
pre-commit-config.yaml
for Java projects:
- repo: https://gitlab.developers.cam.ac.uk/uis/devops/continuous-delivery/precommit-google-java-format
rev: v1.2.0
hooks:
- id: google-java-format
files: "\\.java$"
args: ["-i"]
This plugin formats your code to match the
Google Java Style Guide on git commit
.
If your code already meets the required code format the plugin will pass and allow your commit to
continue. If your code requires formatting it will format the code automatically for you but fail
pre-commit. This allows you to review the changes and commit any updates to continue.
To run locally, the current v1.2.0
version of the pre-commit formatter requires Java 11 in your
path.
Warning
Minor differences in Java source formatting have been seen when running the
google-java-format
pre-commit hook locally with Java versions > 11 compared to the results
when the hook runs as part of a GitLab pipeline (which is currently running the formatter with
Java 11). To avoid inconsistencies it is recommended that you must run the formatter with
Java 11 in your path.
If your Java project requires a different version of Java to compile, you can use sdkman to easily switch between different versions at your command line.
Installing pre-commit hooks¶
After adding a .pre-commit-config.yaml
or modifying it, the following should be run in order to
set-up the hooks:
pre-commit-install
The above command will also need to be run after the repository has been newly-cloned in order to set-up the hooks on your local copy.
Using pre-commit¶
Having been set-up, pre-commit will run against staged files whenever you run git commit
in this
repository. If files do not conform to the checks, then where possible the pre-commit hooks will
modify them accordingly. The commit will be aborted.
The modified files can then be re-staged with git add
and the commit re-tried.
Resolving existing compliance failures¶
The behaviour above will likely not be sufficient when adding pre-commit to an existing project. For example:
- you may desire to re-format all existing source code to conform to a relevant style guide
- when a repository is using our
standard CI templates,
the CI pipeline
pre-commit
job executes the checks against all files in the repository. This will cause the pipeline to fail if there is any existing code which does not conform to the pre-commit checks.
You can execute the hooks separately from a git commit against all files in your repository by running:
pre-commit run --all-files
Configuring Git to ignore bulk formatting commits¶¶
Adding pre-commit hooks to an existing project can result in formatting a large number of files. If you use git blame to show changes from previous commits, a single commit with a large number of formatting changes can reduce the usefulness.
This will often produce a large set of formatting changes which do not affect the behaviour or
design of the code. Once committed, these would limit the usefulness of git blame
, since many
lines of code would be caught up in this bulk-formatting-change commit.
To avoid this, having applied the commit its SHA can be added to a .git-blame-ignore-revs
file in
the root of the repository. For example:
# initial pre-commit formatting changes
c998bb1ed4b3285398c9c7797135d3f060243c6a
You can then configure your local copy of the repository by running:
git config blame.ignoreRevsFile .git-blame-ignore-revs
and commit the file for the benefit of your colleagues:
git add .git-blame-ignore-revs
git commit -m "chore: add .git-blame-ignore-revs"
It should be noted that git config
changes do not get pushed or persisted, so these commits will
still show in the blame
view on GitLab and for other users unless/until they set
blame.ignoreRevsFile
in their local copy of the repository.
Summary¶
In this how-to you learned how to set-up a standard set of pre-commit hooks on a project.
Further information on using pre-commit: