pre-commit: Various Ways to Run Hooks

Around and around the commit cycle goes…

This post is an adapted extract from my book Boost Your Django DX, available now.

pre-commit’s main mode of operation is to run hooks against changed files when you commit. But you can also run hooks without committing, using pre-commit run.

This post covers an overwhelming number of ways to invoke hooks. These can come in useful for particular situations. You don’t need to remember all these invocations to use pre-commit day-to-day, but I hope this post is a useful reference for when you need it.

Commit Whilst Skipping All Hooks

Sometimes you want to commit broken code that’s failing hooks, for example to save work on an incomplete feature branch. You can tell Git to skip pre-commit entirely with --no-verify:

$ git commit --no-verify -m "Add partial code"
[main 9554542] Add partial code

Given that this is possible, it’s best to configure your CI system to run all hooks against all files. More on that in a bit.

Commit Whilst Skipping A Few Hooks

If you only want to skip only a few hooks, you cannot do this with a Git flag. Instead set the SKIP environment variable to the comma-separated list of hook IDs:

$ SKIP=trailing-whitespace,check-yaml git commit -m "Add broken YAML"
Trim Trailing Whitespace................................................Skipped
Fix End of Files.........................................................Passed
Check Yaml..............................................................Skipped
Check for added large files..............................................Passed
[main 2f6b06d] Add broken YAML
 1 file changed, 1 insertion(+)
 create mode 100644 broken.yaml

Run All Hooks Against Staged Files

When you commit, pre-commit runs all hooks against all files staged for commit, temporarily stashing other changes. To do the same thing without committing, run:

$ pre-commit run
[WARNING] Unstaged files detected.
[INFO] Stashing unstaged files to /.../.cache/pre-commit/patch1635860279-10168.
Trim Trailing Whitespace.................................................Passed
Fix End of Files.........................................................Passed
Check Yaml...........................................(no files to check)Skipped
Check for added large files..............................................Passed
[INFO] Restored changes from /.../.cache/pre-commit/patch1635860279-10168.

Note the [WARNING] and [INFO] messages about stashing and restoring unstaged files.

Note that if a stashed change conflicts with a fix from a hook, the fix will be discarded. pre-commit assumes hand-authored changes are more valuable than any automated edit.

Run One Hook Against Staged Files

You can run a single hook by specifying its ID:

$ pre-commit run trailing-whitespace
[WARNING] Unstaged files detected.
[INFO] Stashing unstaged files to /.../.cache/pre-commit/patch1635860716-10721.
Trim Trailing Whitespace.................................................Passed
[INFO] Restored changes from /.../.cache/pre-commit/patch1635860716-10721.

Run Hooks Against All Files

You can run all hooks on every file in the repository like so:

$ pre-commit run --all-files

This is the command you’d want your CI system to run.

When running locally, you can type the short option -a:

$ pre-commit run -a

You can also run just one hook by specifying its ID:

$ pre-commit run trailing-whitespace -a

Run Hooks Against Particular Files

You can run hooks against specific files, even if they aren’t tracked by Git, with --files:

$ pre-commit run --files example.txt example2.txt

Again, you can run only one hook by specifying its ID.

It’s possible to use your shell’s globbing function to select many files:

$ pre-commit run --files *.txt

This will work for small numbers of files. But with a large number of files, this approach will hit your operating system’s argument limit. Instead, you can combine git ls-files and xargs to find matching files and run pre-commit run against them in batches:

$ git ls-files -- '*.py' | xargs pre-commit run --files

That’s a nice little bit of shell-fu. See previous post for more explanation of how this works.

Run Hooks Against Changes in the Current Branch

To check that all files changed between the current branch and the main branch, you can use:

$ pre-commit run --from main --to HEAD
Trim Trailing Whitespace.................................................Passed
Fix End of Files.........................................................Passed
Check Yaml...........................................(no files to check)Skipped
Check for added large files..............................................Passed

This comes in useful after you’ve skipped hooks for some commits on the current branch.

Fin

May pre-commit serve you well,

—Adam


Learn more about pre-commit in my Git DX book.


Subscribe via RSS, Twitter, Mastodon, or email:

One summary email a week, no spam, I pinky promise.

Related posts:

Tags: