Skip to content

Built-in Fast Hooks

prek includes fast, Rust-native implementations of popular hooks for speed and low overhead. These hooks are bundled directly into the prek binary, eliminating the need for external interpreters like Python for these specific checks.

Built-in hooks come into play in two ways:

  1. Automatic Fast Path: Automatically replacing execution for known remote repositories.
  2. Explicit Builtin Repository: Using repo: builtin for offline, zero-setup hooks.

1. Automatic Fast Path

When you use a standard configuration pointing to a supported repository (like https://github.com/pre-commit/pre-commit-hooks), prek automatically detects this and runs its internal Rust implementation instead of the Python version defined in the repository.

The fast path is activated when the repo URL matches https://github.com/pre-commit/pre-commit-hooks. No need to change anything in your configuration. Note that the rev field is ignored for detection purposes.

This provides a speed boost while keeping your configuration compatible with the original pre-commit tool.

repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks  # Enables fast path
    rev: v4.5.0  # This is ignored for fast path detection
    hooks:
      - id: trailing-whitespace

Note

In this mode, prek will still clone the repository and create the environment (e.g., a Python venv) to ensure full compatibility and fallback capabilities. However, the actual hook execution bypasses the environment and runs the native Rust code.

Supported Hooks

Currently, only part of hooks from https://github.com/pre-commit/pre-commit-hooks is supported. More popular repositories may be added over time.

https://github.com/pre-commit/pre-commit-hooks

Notes

  • check-yaml fast path does not yet support the --unsafe flag; for those cases, the automatic fast path is skipped.
  • pretty-format-json is currently available only via repo: builtin while parity coverage against upstream Python behavior is still being expanded.
  • Other hooks from the repository which have no fast path implementation will run via the standard method.

Disabling the fast path

If you need to compare with the original behavior or encounter differences:

PREK_NO_FAST_PATH=1 prek run

This forces prek to fall back to the standard execution path.

2. Explicit Builtin Repository

You can explicitly tell prek to use its internal hooks by setting repo: builtin.

This mode has significant benefits:

  • No network required: Does not clone any repository.
  • No environment setup: Does not create Python environments or install dependencies.
  • Maximum speed: Instant startup and execution.

Note: Configurations using repo: builtin are not compatible with the standard pre-commit tool.

repos:
  - repo: builtin
    hooks:
      - id: trailing-whitespace
      - id: check-added-large-files

Supported Hooks

For repo: builtin, the following hooks are supported:

Hook Reference

This section documents the built-in (Rust) implementations used by repo: builtin.

Configuration notes

  • Configure arguments via args: [...] just like pre-commit.
  • For repo: builtin, entry is not allowed and language must be system (it is fine to omit language).
  • Some hooks are fixers (they modify files). Like pre-commit-hooks, they typically exit non-zero after making changes so you can re-run the commit.

Example:

repos:
  - repo: builtin
    hooks:
      - id: trailing-whitespace
        args: [--markdown-linebreak-ext=md]
      - id: check-added-large-files
        args: [--maxkb=1024]

trailing-whitespace

Trims trailing whitespace from each line.

Supported arguments (compatible with pre-commit-hooks):

  • --markdown-linebreak-ext=<ext> (repeatable / comma-separated)
    • Preserves Markdown hard line breaks (two trailing spaces) for files with the given extension(s).
    • Use --markdown-linebreak-ext=* to treat all files as Markdown.
  • --chars=<chars>
    • Trim only the specified set of characters instead of “all trailing whitespace”.
    • Example: args: [--chars, " \t"] (space + tab).

Caveats

  • --markdown-linebreak-ext values must be extensions only (no path separators).

check-added-large-files

Prevents giant files from being committed.

Supported arguments (compatible with pre-commit-hooks):

  • --maxkb=<N> (default: 500)
    • Maximum allowed file size, in kibibytes.
  • --enforce-all
    • Check all matched files, not just those staged for addition.

Caveats

  • By default, only files staged for addition are checked.
  • Files configured with filter=lfs (via git attributes) are skipped.

check-case-conflict

Checks for paths that would conflict on a case-insensitive filesystem (for example macOS / Windows).

Supported arguments

  • None.

Caveats

  • The check includes parent directories as well as file paths, to catch directory-level case conflicts.

check-illegal-windows-names

Checks for filenames that cannot be created on Windows.

Supported arguments

  • None.

Behavior / caveats

  • Reports filenames containing Windows-reserved device names such as CON, PRN, AUX, NUL, COM1, and LPT1.
  • Reports filenames containing characters forbidden by Windows, including <, >, :, ", \, |, ?, *, and control characters.
  • Reports path segments ending with a trailing . or space.

end-of-file-fixer

Ensures files end in a newline and only a newline.

Supported arguments

  • None.

Behavior / caveats

  • Empty files are left unchanged.
  • Files containing only newlines are truncated to empty.
  • If a file has no trailing newline, a single \n is appended (even if the file otherwise uses CRLF).
  • If a file has trailing newlines, they are reduced to exactly one trailing line ending.

file-contents-sorter

Sorts the non-empty lines in each matched file and rewrites the file when the normalized order changes.

Supported arguments (compatible with pre-commit-hooks):

  • --ignore-case
    • Sort using ASCII case-folded ordering.
    • Mutually exclusive with --unique.
  • --unique
    • Sort and deduplicate lines.
    • Mutually exclusive with --ignore-case.

Behavior / caveats

  • Blank lines and whitespace-only lines are removed before sorting.
  • Line endings are normalized to \n in the rewritten file.
  • Like upstream, the builtin hook defaults to files: '^$', so you must configure files: explicitly to target specific files.

Example:

repos:
  - repo: builtin
    hooks:
      - id: file-contents-sorter
        files: ^requirements(-dev)?\.txt$

fix-byte-order-marker

Removes a UTF-8 byte order marker (BOM) from the beginning of a file.

Supported arguments

  • None.

Caveats

  • Only removes the UTF-8 BOM (EF BB BF).

check-json

Attempts to load all JSON files to verify syntax.

Supported arguments

  • None.

Caveats / differences

  • This implementation rejects duplicate object keys (errors with duplicate key ...).
  • The parser disables the default recursion limit and uses a stack-friendly drop strategy for deeply nested JSON.

check-json5

Attempts to load all JSON5 files to verify syntax.

Supported arguments

  • None.

Caveats / differences

  • This implementation rejects duplicate object keys (errors with duplicate key ...).

pretty-format-json

Checks that JSON files are pretty-formatted and can optionally rewrite them in place.

Supported arguments (compatible with pre-commit-hooks):

  • --autofix
    • Rewrite files in place when formatting changes are needed.
  • --indent=<indent> (default: 2)
    • Use <indent> for each indentation level.
    • Numeric values mean that many spaces.
    • Non-numeric values are used literally, so --indent=\t uses tabs.
  • --no-ensure-ascii
    • Keep non-ASCII characters as UTF-8 instead of escaping them as \uXXXX.
  • --no-sort-keys
    • Preserve the original key order instead of sorting object keys.
  • --top-keys=<k1,k2,...>
    • In every JSON object, move matching keys to the front in the given order.
    • Duplicate names after the first one are ignored.
    • Remaining keys come after that prefix and are sorted unless --no-sort-keys is set.
    • This applies recursively to nested objects too, not just the root object.

Caveats

  • This hook is currently available only via repo: builtin; automatic fast-path replacement of the upstream Python hook remains disabled until parity coverage is broader.
  • Rewritten files always use LF (\n) line endings and end with exactly one trailing newline.

check-toml

Attempts to load all TOML files to verify syntax.

Supported arguments

  • None.

Caveats

  • Files must be valid UTF-8; invalid UTF-8 is reported as an error.
  • May report multiple parse errors for a single file.

Ensures that links to VCS websites are permalinks.

Supported arguments (compatible with pre-commit-hooks):

  • --additional-github-domain=<domain> (repeatable)
    • Adds extra GitHub-style domains to check in addition to the default github.com.

Behavior / caveats

  • Flags links of the form https://<domain>/<owner>/<repo>/blob/<branch>/...#L....
  • Does not flag commit-hash permalinks where <branch> is already a 4-64 character hexadecimal revision.
  • The builtin and fast-path implementations currently follow the upstream hook's GitHub-family matching behavior.

check-yaml

Attempts to load all YAML files to verify syntax.

Supported arguments (partially compatible with pre-commit-hooks):

  • -m, --allow-multiple-documents (alias: --multi)
    • Allow YAML multi-document syntax (---).

Caveats / differences

  • --unsafe is not supported.
    • With repo: builtin, passing --unsafe is treated as an unknown argument.

check-xml

Attempts to load all XML files to verify syntax.

Supported arguments

  • None.

Caveats

  • Empty files are treated as invalid XML.
  • Fails if there is “junk after the document element” (multiple top-level roots).

mixed-line-ending

Replaces or checks mixed line endings.

Supported arguments (compatible with pre-commit-hooks, plus one extra mode):

  • --fix=<mode> (default: auto)
    • auto: replace with the most frequent line ending in the file.
    • no: check only (do not modify files).
    • lf: convert to LF (\n).
    • crlf: convert to CRLF (\r\n).
    • cr: convert to CR (\r) (extra mode in prek).

Caveats

  • Empty and binary files (containing NUL) are skipped.
  • Upstream note: forcing lf / crlf may not behave as expected with git CRLF conversion settings (for example core.autocrlf).

Checks for symlinks which do not point to anything.

Supported arguments

  • None.

Caveats

  • Relies on filesystem symlink support. On Windows, symlink creation and detection can be permission-dependent.

Detects files staged as regular files whose HEAD version is a symlink, which usually happens when a repository is checked out in an environment without symlink support.

Supported arguments

  • None.

Caveats

  • This matches upstream pre-commit-hooks behavior: it only checks tracked entries reported by git status --porcelain=v2.
  • It intentionally ignores differences consisting only of trailing ASCII whitespace (including spaces, tabs, and newline/CRLF conversions) when comparing the staged file against the original symlink target path, because those differences are commonly introduced by formatting hooks.

check-merge-conflict

Checks for merge conflict markers.

Supported arguments (compatible with pre-commit-hooks):

  • --assume-in-merge
    • Allow running the hook even when there is no merge/rebase state detected.

Caveats

  • By default, this hook exits successfully when not in a merge/rebase state.
  • Detects conflict markers only when they appear at the start of a line.
  • Detects standard conflict blocks (<<<<<<<, =======, >>>>>>>) and diff3 ancestor markers (|||||||).
  • ======= is only reported after a preceding <<<<<<<, which avoids false positives for content such as reStructuredText headings.

detect-private-key

Detects the presence of private keys.

Supported arguments

  • None.

Caveats

  • This is a heuristic substring scan for common PEM/key headers (e.g. BEGIN RSA PRIVATE KEY, BEGIN OPENSSH PRIVATE KEY, BEGIN PGP PRIVATE KEY BLOCK, etc.). It can produce false positives/negatives.

no-commit-to-branch

Protects specific branches from direct commits.

Supported arguments (compatible with pre-commit-hooks):

  • -b, --branch <branch> (repeatable, default: main, master)
  • -p, --pattern <regex> (repeatable)

Caveats

  • This hook is configured as always_run: true by default, and does not take filenames. As a result, files, exclude, types, etc. are ignored unless you explicitly set always_run: false.
  • If HEAD is detached (no current branch), the hook does nothing.

check-executables-have-shebangs

Checks that non-binary executables have a proper shebang.

Supported arguments

  • None.

Caveats

  • The check is intentionally lightweight: it only verifies that the file starts with #!.
  • On systems where the executable bit is not tracked by the filesystem, prek consults git’s staged mode bits.

check-shebang-scripts-are-executable

Checks that non-binary files with a shebang are marked executable.

Supported arguments

  • None.

Caveats

  • The check is intentionally lightweight: it only verifies that the file starts with #!.
  • To work on filesystems which do not track the executable bit, prek consults git’s staged mode bits.