Configuring Git Hooks with Pre-Commit

September 26, 2021

Overview

Git hooks are scripts that trigger commands at various intervals in git's execution. Git hooks are useful for running checks against local code changes (ie: linting and formatting) before submitting these changes for code review. Pre-commit is a python library for configuring and running langauge-agnostic git hooks at various intervals (pre-commit, pre-push, pre-merge etc).

Installation

To install pre-commit:

pip install pre-commit

Supported Hooks

Git hooks are versatile in that they allow you to execute third-party libraries, thereby supporting a variety of use cases to match your workflow requirements (here is a list of supported hooks). A few python libraries that are useful for linting and formatting include:

  • pre-commit-hooks - default hooks to check the format of yaml files, ensure files end in a whitespace, remove trailing whitespaces within files and check the validity of files (Documentation)
  • isort - a tool to sort import statements alphabetically, and separate packages into sections by type (Documentation)
  • black - a tool to format your code, compliant with PEP 8 (Documentation)
  • flake8 - a tool to enforce consistent style and linting checks (Documentation)
  • pydocstyle - a tool to check compliance with Python docstring conventions (Documentation)
  • mypy - a tool for static type checking built on top of Python type annotations (Documentation)

Configuration

Git hooks are defined in a pre-commit-config.yaml file in the root directory of a project. For example, using the libraries specified in Supported Hooks, the following configuration may be used:

repos:
-   repo: [https://github.com/pre-commit/pre-commit-hooks](https://github.com/pre-commit/pre-commit-hooks)
    rev: v4.0.1
    hooks:
    -   id: check-yaml
    -   id: end-of-file-fixer
    -   id: trailing-whitespace
    -   id: check-ast
-   repo: [https://github.com/pycqa/isort](https://github.com/pycqa/isort)
    rev: 5.10.0
    hooks:
    -   id: isort
-   repo: [https://github.com/psf/black](https://github.com/psf/black)
    rev: 22.0.0
    hooks:
    -   id: black
-   repo: [https://github.com/pycqa/flake8](https://github.com/pycqa/flake8)
    rev: 4.0.0
    hooks:
    -   id: flake8
-   repo: [https://github.com/python/mypy](https://github.com/python/mypy)
    rev: v0.950
    hooks:
    -   id: mypy
-   repo: [https://github.com/PyCQA/pydocstyle](https://github.com/PyCQA/pydocstyle)
    rev: 6.1.1
    hooks:
    -   id: pydocstyle

You can specifiy the stage you want a hook to run by choosing and installing a relevant hook type (here is a list of hook stages).

To install the environments needed to run git hooks:

pre-commit install --hook-type pre-commit --install-hooks

A seperate environment is created for each hook type and is located in ~/.git/hooks/.

To update git hooks to their latest versions:

pre-commit autoupdate

Usage

Run Automatically

Once you have configured a pre-commit-config.yaml file and installed the relvant hook environments, pre-commit will automatically run on every new commit. Each hook runs as an individual test for that commit and will either succeed or fail. If any hook fails, then the commit will not be made and pre-commit will try modify the conflicting files.

pre-commit will try make modifications to your working directory, but your staging table won't be modified. You will need to restage any modifications made by the hooks.

For example, if you add a new feature and commit those changes:

git commit -m "Add feature"

The pre-commit hooks will be run and the outcome of each test will be described:

Check Yaml....................................(no files to check)Skipped
Fix End of Files..................................................Passed
Trim Trailing Whitespace..........................................Passed
Check AST.....................................(no files to check)Skipped
isort.............................................................Passed
black.............................................................Passed
flake8............................................................Passed
mypy..............................................................Passed
pydocstyle........................................................Passed
[main 146c6c2c] Add feature
1 file changed, 1 insertion(+)

Run Manually

If you want to run the hooks against all files without making a commit, you can run the hooks from the command line. This is useful when adding new hooks or in continuous integration workflows.

pre-commit run --all-files