Skip to content

Logo

Pre-commit


One of the cornerstones of working with git is remembering to commit your work often. Often committing makes sure that it is easier to identify and revert unwanted changes that you have introduced, because the code changes becomes smaller per commit.

However, as you hopefully already seen in the course there are a lot of mental task to do before you actually write git commit in the terminal. The most basic thing is of course making sure that you have saved all your changes, and you are not committing a not up-to-date file. However, this also includes tasks such as styling, formatting, making sure all tests succeeds etc. All these mental to-do notes does not mix well with the principal of remembering to commit often, because you in principal have to do them every time.

The obvious solution to this problem is to automate all or some of our mental task every time that we do a commit. This is where pre-commit hooks comes into play, as they can help us attach additional tasks that should be run every time that we do a git commit.

Configuration

Pre-commit simply works by inserting whatever workflow we want to automate in between whenever we do a git commit and afterwards would do a git push.

Image

Image credit

The system works by looking for a file called .pre-commit-config.yaml that we can configure. If we execute

pre-commit sample-config | out-file .pre-commit-config.yaml -encoding utf8

you should get a sample file that looks like

# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
-   repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v3.2.0
    hooks:
    -   id: trailing-whitespace
    -   id: end-of-file-fixer
    -   id: check-yaml
    -   id: check-added-large-files

the file structure is very simple:

When we are done defining our .pre-commit-config.yaml we just need to install it

pre-commit install

this will make sure that the file is automatically executed whenever we run git commit

❔ Exercises

  1. Install pre-commit

    pip install pre-commit
    

    Consider adding pre-commit to a requirements_dev.txt file, as it is a development tool.

  2. Next create the sample file

    pre-commit sample-config > .pre-commit-config.yaml
    
  3. The sample file already contains 4 hooks. Make sure you understand what each do and if you need them at all.

  4. pre-commit works by hooking into the git commit command, running whenever that command is run. For this to work, we need to install the hooks into git commit. Run

    pre-commit install
    

    to do this.

  5. Try to commit your recently created .pre-commit-config.yaml file. You will likely not do anything, because pre-commit only check files that are being committed. Instead try to run

    pre-commit run --all-files
    

    that will check every file in your repository.

  6. Try adding at least another check from the base repository to your .pre-commit-config.yaml file.

    Solution

    In this case we have added the check-json hook to our .pre-commit-config.yaml file, which will automatically check that all JSON files are valid.

    repos:
    -   repo:
        rev: v3.2.0
        hooks:
        -   id: trailing-whitespace
        -   id: end-of-file-fixer
        -   id: check-yaml
        -   id: check-added-large-files
        -   id: check-json
    
  7. If you have completed the optional module M7 on good coding practice you will have learned about the linter ruff. ruff comes with its own pre-commit hook. Try adding that to your .pre-commit-config.yaml file and see what happens when you try to commit files.

    Solution

    This is one way to add the ruff pre-commit hook. We run both the ruff and ruff-format hooks, and we also add the --fix argument to the ruff hook to try to fix what is possible.

    ```yaml repos: - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.4.7 hooks: # try to fix what is possible - id: ruff args: ["--fix"] # perform formatting updates - id: ruff-format # validate if all is fine with preview mode - id: ruff

  8. (Optional) Add more hooks to your .pre-commit-config.yaml.

  9. Sometimes you are in a hurry, so make sure that you also can do commits without running pre-commit e.g.

    git commit -m <message> --no-verify
    
  10. Finally, figure out how to disable pre-commit again (if you get tired of it).

  11. Assuming you have completed the module on GitHub Actions, lets try to add a pre-commit workflow that automatically runs your pre-commit checks every time you push to your repository and then automatically commits those changes to your repository. We recommend that you make use of

    As an alternative you configure the CI tool provided by the creators of pre-commit.

    Solution

    The workflow first uses the pre-commit action to install and run the pre-commit checks. Importantly we run it with continue-on-error: true to make sure that the workflow does not fail if the checks fail. Next, we use git diff to list the changes that pre-commit has made and then we use the git-auto-commit-action to commit those changes.

    .github/workflows/pre_commit.yaml
    name: Pre-commit CI
    
    on:
      pull_request:
      push:
        branches: [main]
    
    jobs:
      pre-commit:
        name: Check pre-commit
        runs-on: ubuntu-latest
    
        permissions:
          contents: write
    
        steps:
        - name: Checkout code
          uses: actions/checkout@v3
    
        - name: Set up Python
          uses: actions/setup-python@v4
          with:
            python-version: 3.11
    
        - name: Install pre-commit
          uses: pre-commit/action@v3.0.1
          continue-on-error: true
    
        - name: List modified files
          run: |
            git diff --name-only
    
        - name: Commit changes
          uses: stefanzweifel/git-auto-commit-action@v5
          with:
            commit_message: Pre-commit fixes
            commit_options: '--no-verify'
    

That was all about how pre-commit can be used to automate tasks. If you want to deep dive more into the topic you can checkout this page on how to define your own pre-commit hooks.