# pre-commit-hooks: the default hook set for the pre-commit framework

> A repository of ready-made hooks for the pre-commit framework, covering whitespace, YAML and JSON syntax, private keys, large files and branch protection. It is a curated collection, not a framework, and the README is the only configuration reference you get.

**pre-commit/pre-commit-hooks** — Some out-of-the-box hooks for pre-commit

- Repository: https://github.com/pre-commit/pre-commit-hooks
- Stars: 6,700 · Forks: 800
- Language: Python
- License: MIT
- Published: 2026-09-22 · Updated: 2026-09-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/pre-commit-pre-commit-hooks

## What pre-commit-hooks is, and what it is not

pre-commit-hooks is a repository of prebuilt hooks for the pre-commit framework. The README opens with one line: "Some out-of-the-box hooks for pre-commit." That framing matters. The framework that installs git hooks, manages environments and runs them is a separate project, and the README links to it rather than reproducing its documentation. This repository supplies the hook implementations and the .pre-commit-hooks.yaml manifest that tells the framework which ids exist and what each one runs.

The audience is teams that already decided to use pre-commit and now need the boring checks: trailing whitespace, missing final newlines, invalid YAML, invalid JSON, private keys in the tree, oversized files, merge conflict markers. Every one of those is a rule someone would otherwise write as a shell script and maintain. Here they are packaged, versioned and pinned by rev. The repository is written in Python and licensed MIT, with a setup.py that does nothing but call setup(), so the packaging metadata lives in setup.cfg.

What it is not: a formatter, a linter for your language, or a replacement for the pre-commit framework. There is no black, no flake8, no eslint here. If your goal is code style enforcement for TypeScript or Terraform, this repository is the wrong layer.

## How a hook from this repository actually runs

The mechanism is a three-part handshake. Your .pre-commit-config.yaml names a repo URL and a rev, then lists hook ids. The framework clones that repo at the pinned rev into its own cache, reads the .pre-commit-hooks.yaml manifest at the root of the repository, and matches the ids you asked for against the entries there. Each manifest entry declares a language and an entry point, so the framework can build an environment and invoke the hook as a command, passing the staged file paths as arguments.

That last detail explains most of the behaviour people find surprising. Hooks are file-oriented: the framework hands them the list of files being committed, filtered by the files, types, exclude and exclude_types settings in your config. A hook that is not filtered receives everything staged. The README calls out one exception explicitly for no-commit-to-branch, which is configured by default to always_run, meaning it ignores files, exclude, types and exclude_types entirely. To let that hook respect your file filters you must set always_run: false, and the README notes a caveat in that configuration that is cut off in the available text.

Several hooks are aware of git state rather than just file content. check-added-large-files limits itself to files git reports as staged for addition, skips git-lfs files when git-lfs is installed (requiring git-lfs>=2.2.1), and offers --enforce-all to check all listed files instead. check-merge-conflict can run outside an actual merge with --assume-in-merge. destroyed-symlinks targets a specific Windows failure: a symlink checked out as a regular file containing the path it pointed to.

## Installing pre-commit-hooks and running a first hook

There is no standalone installer for this repository. You install the pre-commit framework, then point it at this repo. The README gives exactly one configuration example, and it is the one to start from: add the repo, pin a rev, list hook ids.

```yaml
-   repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v6.0.0  # Use the ref you want to point at
    hooks:
    -   id: trailing-whitespace
    # -   id: ...
```

With that file saved as .pre-commit-config.yaml at the root of your project, the framework installs the git hook and the hook environments. The README does not spell out those commands; it defers to the framework documentation at https://github.com/pre-commit/pre-commit. What you should see after a commit attempt is the hook name in the output and, for a fixer like trailing-whitespace, the file rewritten and the commit aborted so you can review and re-stage.

To add more checks, extend the hooks list with ids from the README. A configuration that catches malformed data files and stray secrets looks like this:

```yaml
-   repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v6.0.0
    hooks:
    -   id: check-yaml
    -   id: check-json
    -   id: check-toml
    -   id: detect-private-key
    -   id: check-added-large-files
        args: ['--maxkb=123']
```

The args key is how you pass hook options, and the README documents the flag names per hook. check-added-large-files takes --maxkb with a default of 500kB. check-yaml takes --allow-multiple-documents and --unsafe. no-commit-to-branch takes --branch (repeatable, and both main and master are protected by default when no branch argument is set) and --pattern for regex matching, for example --pattern release/.*. Getting a flag name wrong fails at hook invocation, not at config parse time, so read the corresponding README entry before adding args.

## The hooks that change files, and the ones that only complain

The collection splits into fixers and checkers, and the split determines your workflow. trailing-whitespace, end-of-file-fixer, fix-byte-order-marker, mixed-line-ending, double-quote-string-fixer and file-contents-sorter modify files in place. When one of them rewrites a file, the commit fails and you re-stage. That is the intended loop, and it is why these hooks belong in a commit hook rather than a CI-only job: the fix lands in your working tree while you still have context.

file-contents-sorter deserves a warning the README states plainly. It removes blank lines, does not respect comments, and converts all newlines to line feeds. Pointing it at a file where blank lines carry meaning, or at a config file with comments, will destroy structure. The README also requires you to supply the target files, so it does nothing until you configure files for it.

mixed-line-ending has a subtler trap. The --fix=crlf and --fix=lf modes force a line ending, and the README notes this is not compatible with a git setup that checks in LF and checks out CRLF, because git smudges the file after the hook runs. --fix=no checks without modifying. If your team relies on autocrlf, use no or auto and expect churn either way.

The checkers do not touch files: check-ast, check-json, check-toml, check-xml, check-yaml, check-merge-conflict, check-symlinks, check-case-conflict, check-illegal-windows-names, check-executables-have-shebangs, check-shebang-scripts-are-executable, check-vcs-permalinks, detect-aws-credentials, detect-private-key, forbid-submodules, forbid-new-submodules, debug-statements and name-tests-test. Several encode platform-specific knowledge that is easy to get wrong by hand, like check-case-conflict for case-insensitive filesystems such as macOS HFS+ or Windows FAT, and check-illegal-windows-names for filenames Windows refuses to create.

## Where this collection gets in your way

The hooks are opinionated about Python and about naming. name-tests-test verifies test file names against a pattern chosen by flag: --pytest is the default and expects .*_test\.py, --pytest-test-first expects test_.*\.py, and --django or --unittest expect test.*\.py. If your test layout matches none of those, the hook fails on files you consider correctly named, and your only options are changing the layout or dropping the hook.

check-builtin-literals requires literal syntax for empty and zero builtin types, with escape hatches: positional constructor calls like list('abc') are allowed, builtins.list() is allowed, --ignore=type1,type2 disables specific types, and --no-allow-dict-kwargs forbids dict keyword syntax. Every one of those flags exists because the default rule was too strict for somebody. That is a fair signal about the hook's fit outside a conventional Python codebase.

double-quote-string-fixer is the clearest wrong-tool case. It rewrites double-quoted strings to single quotes. In a project whose style guide prefers double quotes, or one with a formatter that normalizes the other way, this hook and the formatter will fight on every commit. The README gives no configuration to reverse the direction.

There is also a documentation boundary worth naming. The README documents each hook and its flags, but it does not document rollback, does not describe how to remove hooks once installed, and does not cover the framework's own commands. For anything about installing the git hook, running hooks manually, or skipping them, the README points at the framework repository instead. Treat this README as a hook reference, not a manual.

## Alternatives and how their approach differs

The closest alternative is writing your own hook repository and referencing it from the same .pre-commit-config.yaml. The difference is control, not capability. A local repo lets you encode project-specific rules, keep the manifest in your own tree, and version it alongside the code, at the cost of implementing and testing each hook yourself. pre-commit-hooks is the opposite trade: no implementation work, but you inherit its defaults and its flag set, and every deviation becomes an args line or a dropped hook.

A second alternative is Husky, which people search for alongside this project. Husky installs git hooks through the Node ecosystem rather than through a Python-managed framework with cached hook environments and a YAML manifest. If your repository is JavaScript or TypeScript and has no Python toolchain, adding pre-commit means adding a Python runtime to every developer machine and to CI, which is a real cost. Husky avoids that and lives where your package manager already is. The trade is that you assemble the checks yourself; there is no equivalent curated collection pinned by rev in the same way.

A third path is CI-only enforcement. Running the same checks in a pipeline catches the same problems, but it catches them after the commit exists, so the fix becomes a follow-up commit rather than an amendment. The hooks here are designed for the pre-commit stage; using them in CI is possible through the framework's own integration, but it changes the feedback loop rather than the rules.

## Maintenance, versioning and the MIT licence

The repository is not archived, and the last push was on 2026-08-17. Releases are infrequent and deliberate: v6.0.0 on 2025-08-09, v5.0.0 on 2024-10-07, v4.6.0 on 2024-04-06. That cadence matters for upgrades. Because you pin a rev in .pre-commit-config.yaml, nothing changes under you until you edit that line, and the CHANGELOG.md at the repository root is where breaking changes between majors are recorded. Upgrading is a rev bump plus a read of the changelog; the cost is the review, not the installation.

The licence is MIT. That permits use, modification and redistribution with the licence and copyright notice preserved. It is a permissive licence with no copyleft obligation, but whether it satisfies your organisation's policy, and how you attribute the project if you vendor or fork it, is a question for your legal review rather than something this article can settle.

One operational cost is easy to overlook: each hook id you add expands the environment the framework builds and the set of files it inspects on every commit. A long hooks list on a large repository slows the commit path, and the fix is scoping with files and types rather than removing checks. Remember that no-commit-to-branch ignores those scoping keys unless you set always_run: false.

## Conclusion

Adopt pre-commit-hooks if you already run the pre-commit framework and want whitespace, syntax and secret checks without writing them yourself. Do not adopt it if you are not using pre-commit, or if you expect a linter or formatter: the repository is a hook collection, and the README points elsewhere for the framework itself. Before rolling it out, verify which hooks actually fire on your file types, since several default to running on every file and ignore your files and exclude settings, and check the pinned rev against the releases list.

## FAQ

### How do I install pre-commit-hooks?

There is no separate install step for this repository. You install the pre-commit framework and then add the repo URL and a rev to your .pre-commit-config.yaml, as the README's example shows with rev v6.0.0. The framework fetches the hooks at that pinned revision.

### How do I run pre-commit-hooks manually?

The README does not document manual invocation; it points to the pre-commit framework repository for anything outside hook configuration. What the README does specify is the config shape: a repo entry, a rev, and a hooks list of ids such as trailing-whitespace.

### How do I skip or ignore pre-commit-hooks?

The README does not describe skipping hooks. It does document per-hook filtering through the framework's files, exclude, types and exclude_types settings, with the explicit exception of no-commit-to-branch, which is set to always_run by default and ignores those keys unless you set always_run: false.

### How does pre-commit-hooks work with .pre-commit-config.yaml?

Your .pre-commit-config.yaml names the repository URL and a rev, then lists hook ids. The framework reads the .pre-commit-hooks.yaml manifest in this repository at that pinned rev and matches the ids you listed against the entries there.

### How do I add pre-commit-hooks to a project?

The README's example adds a repo entry pointing at https://github.com/pre-commit/pre-commit-hooks, a rev such as v6.0.0, and a hooks list containing ids like trailing-whitespace. Additional hooks are added as further ids under the same hooks key, with options passed through args.

## Sources

- [Issues](https://github.com/pre-commit/pre-commit-hooks/issues)
- [License: MIT](https://github.com/pre-commit/pre-commit-hooks/blob/main/LICENSE)
- [pre-commit/pre-commit-hooks on GitHub](https://github.com/pre-commit/pre-commit-hooks)
- [README](https://github.com/pre-commit/pre-commit-hooks/blob/main/README.md)
- [Releases](https://github.com/pre-commit/pre-commit-hooks/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/pre-commit-pre-commit-hooks
