pre-commit

T Travis H event 2022-02-08 visibility 777 comment 2
more_vert

pre-commit

pre-commit is a framework that uses Git hooks to go through checklists before the commit. This precheck can help to review if your code is meeting the coding standards or if the commiting file is too large. And the commit will only be made when all pre-defined conditions are met. So that one can worry less on the atting and focus more on building the code.

What is Git Hooks?

Git hooks are the s triggered by Git when certain actions take place, such as commit and merge. They contain customised actions to be executed before or after when certian Git actions are used.

There are 2 ways the hooks can be implemented; Client-side and Server-side.
Client-side hooks are called locally by actions such as commit, whereas, server-side hooks are called on the server when receiving a push.
This framework helps to build the client-side hooks to trigger before the commit.

How to Set Up

To implement pre-commit, the following steps are required.

Installation

The framework can be installed with the following lines.

Using pip

$ pip install pre-commit

Using homebrew

$ brew install pre-commit

Using conda

$ conda install -c conda-forge pre-commit

Configuration

Before activating pre-commit, configuration needs to be defined with the sources of the pre-commit hooks/plugins.

They are defined in the configuration file, .pre-commit-config.yaml in the target repository, and follows the at below. Although, the at is written for Python, pre-commit works for any programming language.

repos:
-   repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.1.0
    hooks:
    -   id: trailing-whitespace
-   repo: https://github.com/psf/black
    rev: 22.1.0
    hooks:
    - id: black
      args: [--config=pyproject.toml]
-   repo: https://github.com/PyCQA/flake8
    rev: 4.0.1
    hooks:
    -   id: flake8
        args: [--config=.flake8]
-   repo: https://github.com/asottile/reorder_python_imports
    rev: v2.7.1
    hooks:
    -   id: reorder-python-imports
        args: [--py37-plus, --add-import, 'from __future__ import annotations']
        exclude: ^testing/resources/python3_hooks_repo/
SyntaxDescription
reporepository url to git clone from
revrepository revision/release tag
hookshooks to implement from the repository
ididentification for the hooks
argslist of eters to pass to the hooks
excludefile exclusion pattern

In the example, the following hooks are configured:

  • trailing-whitespace: pre-commit hook to remove trailing whitespaces
  • black: Python atter
  • flake8: PEP 8 compliance checker
  • reorder_python_imports: reordering tool for Python imports
  • Note that black and flake8 hooks contain configuration arguments which will be discussed next

Compatibility between Black & Flake8

In the example, black is used to at the code , followed by 'flake8' to further comply with PEP 8 standards for non-style linting concerns.
Although black is compliant with PEP 8, there are some opiniated atting that may clash with flake8.
Hence, it is important to amend the configuration to prevent spewing errors.

As per black repository, the following configuration can be set for flake8 to avoid lison by creating a configuration file, .flake8 file in the target repository. (source: https://github.com/psf/black/blob/main/.flake8)

[flake8]
ignore = E203, E266, E501, W503
# line length is intentionally set to 80 here because black uses Bugbear
# See https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#line-length for more details
max-line-length = 80
max-complexity = 18
select = B,C,E,F,W,T4,B9

In addition, a configuration file, pyproject.toml can also be set for black to ensure it ignores certain files and the string quote normalisation (converting " to ') is disabled.
The string quote normalisation is upto personal preference.

[tool.black]
line-length = 88
skip-string-normalization = 1
include = '\.pyi?$'
exclude = '''
/(
    \.git
| \.hg
| \.mypy_cache
| \.tox
| \.venv
| _build
| buck-out
| build
| dist
)/
'''

Usage

After the configuration is done, pre-commit needs to be installed into the git hooks of the target repository. Ensure all relelvant configuration files are in the same target repository ectory.

$ pre-commit install

This command will enable pre-commit to be triggered before the commit for the target repository.

To manually execute the hooks, the follow commands can be used. When pre-commit runs, it will download, install and run the hooks specified in the .pre-commit-config.yaml.

$ pre-commit run --all-files
$ pre-commit run <hook_id> # for specific hooks

When configured hooks are not installed, they are installed first.

20220818121426-image.png

Hooks are triggered to check the codes.

20220818121453-image.png

Once the code has passed all the hooks, then the code gets committed (or is ready to be committed if hooks called manually).

Bypass pre-commit

Hooks can be bypassed by adding the --no-verify option to commit command.

$ git commit --no-verify -m "comment"

Environment and Cache

By default, pre-commit stores its hook environment and cache at ~/.cache/pre-commit.
The path can be altered by setting the following environment variables.

  • PRE_COMMIT_HOME: pre-commit uses this variable's path
  • XDG_CACHE_HOME: pre-commit uses this variable's path followed by /pre-commit

Warning

If the repository is located in the network drive using UNC (Universal Naming Convention), Git may not recognise the path and pre-commit may not be installed properly.

More from Kontext
comment Comments
Raymond Raymond

Raymond access_time 3 years ago link more_vert

Good article, Travis. I have not used pre-commit before but now I'm intrigued :)

T Travis H

Travis access_time 3 years ago link more_vert

Thanks Raymond! Yes, I'm finding this tool quite useful. :)

Please log in or register to comment.

account_circle Log in person_add Register

Log in with external accounts