Git hooks with pre-commit
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: flake8Have 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.