Automating PEP 8 Style Checks with Git Pre-commit Hooks
A Git pre-commit hook is a script that runs automatically before each commit and can block the commit if a check fails. This post sets up pre-commit hooks with Python linters to enforce PEP 8 code style automatically before files are committed.
Coding gets hectic, and style is usually the first thing I drop when I’m racing to finish something. That’s a mistake: PEP 8 is the official Python style guide, and it sets out rules for naming, commenting, indentation, and whitespace. Following it produces code that’s easy to read and reuse — which matters a lot more once someone else has to work in your codebase.
PEP 8 is a standard, not an enforcement mechanism, so you need tooling to check and fix violations automatically. Flake8 is one such tool (alongside others such as Pylint and PyLama) that inspects code for PEP 8 compliance errors — see Flake8: Your Tool For Style Guide Enforcement. Tools like this are called linters. In this post, I use Git hooks and pre-commit for a simple setup that checks Python code before you commit files into the repository.
Python Linters: Flake8 for PEP 8 Compliance
A linter is a static-analysis tool that checks source code for syntax errors and style violations without executing it. Linters ensure code quality in the following aspects:
- ensure correct code formatting and style;
- minimise syntax errors;
- help in code reviews;
- save development time.
Flake8 is a Python linter that is easy to install with pip. Pip is the standard package manager for Python; if you need guidance on installing it, read the article at realpython.com. Since you are reading this post, you most likely already have pip on your system.
# define which Python version you want to install flake8 for
python<version> -m pip install flake8
To use Flake8, run it on a specific file or directory.
# Run Flake8 on repo directory
flake8 path/repo
# Run Flake8 on the file main.py
flake8 path/repo/main.py
Code Formatters: Black for Automatic PEP 8 Formatting
Black is an opinionated Python code formatter that rewrites source files to a deterministic PEP 8-compliant style. You can use Black alone or together with Flake8. Per the official Black installation docs, Black requires Python 3.10+ to run and is installed with pip.
pip install black
The usage is similar to Flake8; just feed in the project directory or file.
black <file_or_directory>
Black reformats Python files following PEP 8 guidelines, while Flake8 checks for syntax and style errors. Using them together is good practice, and they work well in tandem. Refer to the docs “Using Black with other tools” for more details on Black’s compatibility with Flake8 and other linters.
Git Hooks and the pre-commit Framework
pre-commit is a framework for managing and running Git hooks that checks and reformats code before it is committed to the repository. Once installed, it runs your configured checks automatically on every git commit. See the official pre-commit documentation for the full hook catalogue.
Installing pre-commit in a Git Repository
To install pre-commit in your Git repository folder, run the following commands.
# Install pre-commit
pip install pre-commit
# Install git hooks in your .git/ directory
pre-commit install
Configuring .pre-commit-config.yaml for Black and Flake8
To configure the hooks you want to include, define a .pre-commit-config.yaml file in the project repository. Here’s a config for Black and Flake8, following the current hook syntax from Black’s own docs and Flake8’s docs:
repos:
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 26.5.1
hooks:
- id: black
language_version: python3.11
- repo: https://github.com/pycqa/flake8
rev: 7.3.0
hooks:
- id: flake8
Black’s own docs recommend the black-pre-commit-mirror repo rather than psf/black directly, since it lets pre-commit use a faster mypyc-compiled build. Pin rev to whatever the latest release tag is when you set this up — both projects tag new versions regularly, so treat the versions above as a snapshot, not a permanent value.
When you install pre-commit, it creates a Git hook (a shell script) in the hidden .git/hooks directory of your repository. These scripts automate your development process. As you can see below, some Git hook scripts (files with the .sample extension) are already installed in a fresh Git repository.
ls .git/hooks
To read the Git manual about githooks, run:
man githooks
Running pre-commit Hooks on Commit and on All Files
The pre-commit hook automatically runs code style and formatting checks when you commit, and the commit is blocked if a check fails. You can also run pre-commit across every file in the repository on demand, not just the staged ones.
pre-commit run --all-files
Sometimes there’s an issue you can’t fix immediately, but you still need to get a commit through. The --no-verify flag on git commit bypasses the pre-commit hook entirely, so use it sparingly.
git commit -am "commit message" --no-verify
Resolving “command not found: pre-commit”
If pre-commit install returns command not found: pre-commit, the package is not on your PATH. Cause: pre-commit was installed into a different Python environment or user site directory. Fix it by reinstalling into the active environment and verifying the executable:
pip install --user pre-commit
python -m pre_commit --version
Final Thoughts: Git Pre-commit Workflow for PEP 8
A Git pre-commit hook gives you a simple, automated Python development cycle: Flake8 lints for PEP 8 and syntax errors, Black reformats files, and the commit is blocked until checks pass. I’ve shown you how to install pre-commit in a Git repository and configure it to run Flake8 and Black on every commit — set it up once, and you stop thinking about style altogether.
Git Pre-commit FAQ
How do I install pre-commit in a Git repository?
Run pip install pre-commit to install the package, then pre-commit install inside the repository to register the Git hook in .git/hooks/pre-commit. The hook then runs automatically on every git commit.
How do I run pre-commit hooks on all files, not just staged ones?
Run pre-commit run --all-files. By default the hooks only check staged files; this command applies every configured hook (Black, Flake8) across the whole repository.
How do I commit when a pre-commit hook fails?
Use git commit -am "message" --no-verify. The --no-verify flag bypasses the pre-commit hook so the commit goes through, useful when a style issue cannot be fixed immediately. Use it sparingly.
What is the difference between Black and Flake8?
Black is an autoformatter that rewrites code to a consistent PEP 8 style, while Flake8 is a linter that reports PEP 8 violations and syntax errors without changing files. They are commonly used together in one .pre-commit-config.yaml.
References
- PEP 8 – Style Guide for Python Code
- Black (PyPI)
- Black: Getting Started (installation and Python version requirement)
- Black: Version control integration (pre-commit config)
- Using Black with other tools
- Flake8: Your Tool For Style Guide Enforcement
- Flake8: Using Version Control Hooks
- pre-commit: A framework for managing and maintaining multi-language pre-commit hooks
- Git hooks documentation
- git-commit: the
--no-verifyoption - realpython.com: What Is Pip?
Did you like this post? Please let me know if you have any comments or suggestions.
Posts about development tools and Python codingEnjoyed this? Get more like it.
Weekly notes on AI tools, Python, and what I'm actually building — plus two free gifts: the 15-page Fantastic AI: The 2026 Toolkit and a Git Commands & Contribution Workflow Cheatsheet.