I was listening to the episode Get inside the .git folder on the podcast Talk Python To Me and learned about pre-commit hooks. Git hooks are commonly used with linters and formatters.

In this post, we’ll create a hook with pre-commit that runs prior to committing using the formatter black and the linter flake8. The goal is to keep code up to standards and as readable as possible. But first:

What are hooks?

You can find the hooks folder within repo/.git/hooks

This directory contains shell scripts that are invoked after the Git commands they are named after. For example, after you run a commit, Git will try to execute the post-commit script, if it has executable permissions.

Here’s a full list of hooks available:

  • applypatch-msg
  • pre-applypatch
  • post-applypatch
  • pre-commit
  • prepare-commit-msg
  • commit-msg
  • post-commit
  • pre-rebase
  • post-checkout
  • post-merge
  • pre-receive
  • update
  • post-receive
  • post-update
  • pre-auto-gc
  • post-rewrite
  • pre-push

How to create a hook without pre-commit

$ cd your_repo/
$ vim repo/.git/hooks/pre-commit

add the following:

#!/bin/bash
echo Luke makes the best pre-commit hooks
echo PWD is $PWD

Now each time you commit, this will run before the commit finishes.

pre-commit creates these scripts automatically

Pre-commit generates these scripts for you from a .yaml file then stores the generated script in the appropriate place within repo/.git/hooks/<hook_type>. Here’s an example of the script that is generated by pre-commit after following the instructions below.

How to configure pre-commit with black and flake8

$ cd your_repo/
$ python3 -m venv env
$ source env/bin/activate
$ pip install pre-commit

optional: $ pip freeze > requirements.txt to update your requirements

Create the pre-commit .yaml file in your repository

$ vim .pre-commit-config.yaml
repos:
  - repo: https://github.com/psf/black
    rev: 20.8b1 # Replace by any tag/version: https://github.com/psf/black/tags
    hooks:
      - id: black
        language_version: python3 # Should be a command that runs python3.6+

  - repo: https://github.com/pycqa/flake8
    rev: 3.9.1 # Replace by any tag/version: https://github.com/PyCQA/flake8/tags
    hooks:
      - id: flake8

Have pre-commit generate the script from your .yaml file and output it into repo/.git/hooks then format and lint all existing files in your repository

$ pre-commit install
$ pre-commit run -all

From now on, when you commit, this hook will run then format and lint all your modified files in this repository.

see also