pre-commit hooks that pip-install the clang-format and clang-tidy version you pin, on every developer's machine.
Website · Get started · Discussions
Add this configuration to your .pre-commit-config.yaml file:
repos:
- repo: https://github.com/cpp-linter/cpp-linter-hooks
rev: v1.7.0
hooks:
- id: clang-format
args: [--style=file, --version=21]
- id: clang-tidy
args: [--version=21]Run pre-commit install once in each clone. --style=file loads the style from your
.clang-format file, and clang-tidy reads your .clang-tidy file by itself. The clang-tidy hook
needs a compile_commands.json, which it looks for in build/ and a few other directories (see
Compilation database);
leave it out if you only run clang-tidy in CI, for example with
cpp-linter-action.
Tip
The rev tag (e.g. v1.7.0) is the project version, not the clang tool version. Without
--version, each hook installs the newest clang-format or clang-tidy wheel on PyPI the first time
it runs and keeps it until pre-commit rebuilds the hook environment, so two machines can run
different LLVM versions with the same configuration. For production use, always pin the tool
version explicitly with --version.
--version=21installs the newest 21.x wheel, and--version=21.1.8pins an exact release. clang-tidy wheels are released separately from clang-format wheels and skip some releases, so give clang-tidy the major version.- clang-format wheels cover LLVM 6 to 23 and clang-tidy wheels LLVM 13 to 23. For a version without a wheel, the hook fails and lists some of the versions that exist.
- When the hook environment already has a wheel that matches
--version(or any wheel, without--version), the hook uses it and does not contact pypi.org. Otherwise it looks the version up on pypi.org and installs it, so that run needs network access.
To install the clang tool together with the hook, list the exact wheel in
additional_dependencies:
- id: clang-format
args: [--style=file, --version=21]
additional_dependencies: [clang-format==21.1.8]
- id: clang-tidy
args: [--version=21]
additional_dependencies: [clang-tidy==21.1.6]pre-commit installs additional_dependencies when it creates the hook environment (for example
with pre-commit install-hooks), so the hooks need no network access when they run. Use this on
pre-commit.ci, which runs hooks without network access, and behind a
firewall. The pinned wheel must match --version; otherwise the hook installs the --version one
at run time.
To use a predefined coding style instead of your .clang-format file:
- id: clang-format
args: [--style=Google] # Other coding style: LLVM, GNU, Chromium, Microsoft, Mozilla, WebKit.When clang-format changes a file, the hook fails and pre-commit stops the commit:
clang-format.............................................................Failed
- hook id: clang-format
- files were modified by this hookHere’s a sample diff showing the formatting applied with --style=Google:
--- a/testing/main.c
+++ b/testing/main.c
@@ -1,3 +1,6 @@
#include <stdio.h>
-int main() {for (;;) break; printf("Hello world!\n");return 0;}
-
+int main() {
+ for (;;) break;
+ printf("Hello world!\n");
+ return 0;
+}Note
Use --dry-run in args of clang-format to print instead of changing the format.
The hook fails if a file needs formatting and prints the lines to fix:
clang-format.............................................................Failed
- hook id: clang-format
- exit code: 1
main.c:2:13: error: code should be clang-formatted [-Wclang-format-violations]
int main() {for (;;) break; printf("Hello world!\n");return 0;}
^
main.c:2:21: error: code should be clang-formatted [-Wclang-format-violations]
int main() {for (;;) break; printf("Hello world!\n");return 0;}
^
main.c:2:28: error: code should be clang-formatted [-Wclang-format-violations]
int main() {for (;;) break; printf("Hello world!\n");return 0;}
^
main.c:2:54: error: code should be clang-formatted [-Wclang-format-violations]
int main() {for (;;) break; printf("Hello world!\n");return 0;}
^
main.c:2:63: error: code should be clang-formatted [-Wclang-format-violations]
int main() {for (;;) break; printf("Hello world!\n");return 0;}
^To set the checks in args instead of your .clang-tidy file, quote the whole option: inside
[...], YAML splits an unquoted value at each comma. When that happens, the hook joins the pieces
again and adds a note on how to fix the configuration. --checks takes check names, not a file:
clang-tidy reads the nearest .clang-tidy file by itself, so --checks=.clang-tidy does nothing.
- id: clang-tidy
args: ["--checks=boost-*,bugprone-*,performance-*,readability-*,portability-*,modernize-*,clang-analyzer-*,cppcoreguidelines-*"]When clang-tidy reports a warning or an error, the hook fails:
clang-tidy...............................................................Failed
- hook id: clang-tidy
- exit code: 1
/home/runner/work/cpp-linter-hooks/cpp-linter-hooks/testing/main.c:4:13: warning: statement should be inside braces [readability-braces-around-statements]
for (;;)
^
{
The hook passes -quiet to clang-tidy and drops status lines such as 522 warnings generated.,
which count warnings in system headers that clang-tidy does not show. Add --verbose to args to
keep clang-tidy's full output.
Note
Add --fix to args to automatically apply clang-tidy fixes in place (equivalent to
passing -fix to clang-tidy directly). This is opt-in and not the default because
auto-fixing can modify source files in unexpected ways. A valid compile_commands.json is
strongly recommended when using --fix.
For cases where compiler errors exist alongside style issues, pass -fix-errors directly
in args instead (clang-tidy native flag).
repos:
- repo: https://github.com/cpp-linter/cpp-linter-hooks
rev: v1.7.0 # includes --fix support
hooks:
- id: clang-tidy
args: [--fix]For CMake or Meson projects, clang-tidy works best with a compile_commands.json
file that records the exact compiler flags used for each file. Without it, clang-tidy
may report false positives from missing include paths or wrong compiler flags.
The hook auto-detects compile_commands.json in common build directories (build/,
out/, cmake-build-debug/, _build/) and passes -p <dir> to clang-tidy
automatically — no configuration needed for most projects:
repos:
- repo: https://github.com/cpp-linter/cpp-linter-hooks
rev: v1.7.0
hooks:
- id: clang-tidy
# Auto-detects ./build/compile_commands.json if presentTo specify the build directory explicitly:
- id: clang-tidy
args: [--compile-commands=build]To disable auto-detection (e.g. in a monorepo where auto-detect might pick the wrong database):
- id: clang-tidy
args: [--no-compile-commands]Note
Generate compile_commands.json with CMake using cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -Bbuild .
or add set(CMAKE_EXPORT_COMPILE_COMMANDS ON) to your CMakeLists.txt.
--compile-commands takes the directory containing compile_commands.json, not the file path itself.
Three self-contained templates plus quick snippets for other common setups.
- CMake minimal config
- Meson minimal config
- Large project
files:regex — scoping hooks for speed - Quick snippets — Meson, clang-format-only, monorepo, CI,
compile_commands.json
Tip
For large codebases, if your pre-commit runs longer than expected, it is highly recommended to add files in .pre-commit-config.yaml to limit the scope of the hook. This helps improve performance by reducing the number of files being checked and avoids unnecessary processing. Here's an example configuration:
- repo: https://github.com/cpp-linter/cpp-linter-hooks
rev: v1.7.0
hooks:
- id: clang-format
args: [--style=file, --version=21]
files: ^(src|include)/.*\.(cpp|cc|cxx|h|hpp)$ # Limits to specific dirs and file types
- id: clang-tidy
args: [--version=21]
files: ^(src|include)/.*\.(cpp|cc|cxx|h|hpp)$For clang-tidy, you can also process multiple files in parallel by adding --jobs
or -j:
- repo: https://github.com/cpp-linter/cpp-linter-hooks
rev: v1.7.0
hooks:
- id: clang-tidy
args: [--version=21, --jobs=4]Warning
When args include --fix, -fix, -fix-errors or --export-fixes, the hook ignores
--jobs. pre-commit itself still runs the hook on groups of files in parallel, so each group
overwrites a shared --export-fixes file and fixes to the same header can collide. Add
require_serial: true to the hook to run it once for all files.
Alternatively, if you want to run the hooks manually on only the changed files, you can use the following command:
pre-commit run --files $(git diff --name-only)This approach ensures that only modified files are checked, further speeding up the linting process during development.
Note
Use -v or --verbose in args to enable verbose output.
For clang-format, it shows the list of processed files.
For clang-tidy, it prints which compile_commands.json is being used (when auto-detected or explicitly set)
and keeps clang-tidy's full output, without -quiet.
pre-commit shows this output only when the hook fails; add verbose: true to the hook to see it on every run.
repos:
- repo: https://github.com/cpp-linter/cpp-linter-hooks
rev: v1.7.0
hooks:
- id: clang-format
args: [--style=file, --version=21, --verbose] # Shows processed files
- id: clang-tidy
args: [--verbose] # Shows which compile_commands.json is usedmirrors-clang-format is pre-commit's mirror of the clang-format wheel.
| Feature | cpp-linter-hooks |
mirrors-clang-format |
|---|---|---|
Supports clang-format and clang-tidy |
Both | clang-format only |
| Custom configuration files | .clang-format, .clang-tidy |
.clang-format |
| Specify tool version | via --version arg (e.g. --version=21) |
via rev tag (e.g. rev: v21.1.8) |
rev tag meaning |
Project version, not the tool version | Equals the clang-format version directly |
| Default file types | C, C++ | C, C++, C#, CUDA, Java, JavaScript, JSON, Objective-C, proto, textproto, Metal |
| Supports passing format style string | via --style |
via --style |
| Verbose output | via --verbose |
via --verbose |
| Dry-run mode | via --dry-run |
via --dry-run --Werror |
| Auto-fix mode | via --fix (clang-tidy only) |
No |
| Compilation database support | auto-detect or --compile-commands |
No |
These organizations run cpp-linter-hooks on their default branch:
MIT ACL ·
Bazel Contrib ·
CodSpeed ·
doldecomp ·
HKUST Aerial Robotics ·
Kubewarden ·
Computational Geography ·
IMSY ·
CONVINCE-Project
The showcase lists more projects that use cpp-linter tools.
cpp-linter is maintained by two volunteers. Sponsor the project through Open Collective. Silver and Gold sponsors get their logo here.
See the contributing guide and open an issue for bugs and feature requests.
This project is licensed under the MIT License.