Commit-diff auditor. commitlint checks format. diffmatch checks truth.
Project description
diffmatch
commitlint checks format. diffmatch checks truth.
diffmatch audits git commits to verify that commit messages actually match their diffs. It catches the lies we all tell in commit messages.
$ diffmatch log --limit 5
diffmatch — auditing last 5 commit(s)
────────────────────────────────────────────────────────────
PASS a1b2c3d4 feat: add user authentication module
+342/-12 in 8 files
WARN e5f6g7h8 fix typo in README
+89/-34 in 6 files
! Message says "typo" but diff is 123 lines across 6 file(s). Expected <20 lines.
WARN i9j0k1l2 docs: update API reference
+45/-12 in 4 files
! Commit type is "docs" but changed files include non-documentation: src/api.ts, src/handler.ts...
PASS m3n4o5p6 refactor: simplify error handling
+28/-45 in 3 files
WARN q7r8s9t0 add new payment provider
+2/-180 in 12 files
! Message says "add" but diff is 99% deletions (+2/-180). More was removed than added.
────────────────────────────────────────────────────────────
2 passed 3 warned 0 skipped
Why
commitlint validates that your commit messages follow a format (e.g., feat:, fix:, docs:). It has 413K weekly downloads because developers care about commit quality.
But commitlint never checks whether the message is true.
- "fix typo" on a 200-line refactor? commitlint says OK.
- "add feature X" when the diff only deletes code? commitlint says OK.
- "docs: update README" when you also changed 5 source files? commitlint says OK.
diffmatch catches these. It reads the actual diff and compares it to what the message claims.
Install
pip install diffmatch
Requires Python 3.8+ and git.
Usage
Check a single commit
diffmatch check # Check HEAD
diffmatch check abc1234 # Check specific commit
diffmatch check --json # JSON output for CI
Audit recent history
diffmatch log # Last 10 commits
diffmatch log --limit 50 # Last 50 commits
diffmatch log --verbose # Show skipped merge commits
Git hook
diffmatch hook install # Advisory mode (warns, doesn't block)
diffmatch hook install --strict # Strict mode (blocks commit on mismatch)
diffmatch hook uninstall # Remove the hook
pre-commit
Add to your .pre-commit-config.yaml:
repos:
- repo: https://github.com/diffmatch-dev/diffmatch
rev: v0.1.0
hooks:
- id: diffmatch
What it checks
| Check | What it catches | Example |
|---|---|---|
| Size mismatch | Minimizing words on large diffs | "fix typo" on 200+ line change |
| Direction mismatch | Add/delete claims vs. actual diff direction | "add feature" but 90% deletions |
| Scope mismatch | Narrow claims on broad changes | "docs: update" but source files changed |
| Rename detection | Unreported file renames | 78 renames not mentioned in message |
| Empty diff | Commits with no actual changes | Message exists but diff is empty |
Exit codes
| Code | Meaning |
|---|---|
0 |
All checks passed |
1 |
Warnings found (mismatches detected) |
2 |
Error (git not found, bad arguments) |
JSON output
All commands support --json for CI integration:
diffmatch check --json | jq '.verdict'
{
"ref": "HEAD",
"subject": "fix typo in config",
"type": "fix",
"is_conventional": true,
"diff": {
"files": 6,
"added": 89,
"deleted": 34,
"total": 123,
"renames": 0,
"categories": {"code": 4, "config": 2}
},
"findings": [
{
"severity": "warn",
"check": "size_mismatch",
"message": "Message says \"typo\" but diff is 123 lines across 6 file(s). Expected <20 lines."
}
],
"verdict": "warn"
}
Design principles
- Zero dependencies. Pure Python stdlib + git CLI. No npm, no cargo, no binary.
- Single file. The entire tool is one Python file. Read it, audit it, vendor it.
- Heuristic, not AI. Pattern matching, not vibes. Every check is deterministic and explainable.
- Advisory by default. Warns, doesn't block. Earn trust before enforcing.
- Complements commitlint. Format + truth > format alone.
FAQ
Won't this have false positives?
Yes. Heuristics are imperfect. That's why the default mode is advisory (warn, don't block). Tune thresholds over time, and use --strict only when you trust the calibration.
Why not just use an LLM to check commit messages? You could. But that requires API keys, network access, costs money, and is non-deterministic. diffmatch runs offline, instantly, for free, and gives the same answer every time.
Does it support non-English commit messages? The action word matching is English-only. File type and size checks work regardless of language.
Does it work with merge commits?
Merge commits are skipped by default. Use --verbose to see them in the output.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file diffmatch-0.1.0.tar.gz.
File metadata
- Download URL: diffmatch-0.1.0.tar.gz
- Upload date:
- Size: 11.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1399f87b941cd2ce68d2abe7738ba932e7c726a53571944e5ccece7404a018d0
|
|
| MD5 |
3928cf54c10aa43d5f55d666809d3f05
|
|
| BLAKE2b-256 |
4d063d9e8a17eb66da347a6494974c391a7b4488796b6520794336c2a672e90d
|
File details
Details for the file diffmatch-0.1.0-py3-none-any.whl.
File metadata
- Download URL: diffmatch-0.1.0-py3-none-any.whl
- Upload date:
- Size: 12.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
03aa02339c2ade6d5fd8e2217c8b225fad99b4a9dc93884f5e2d336f99d1b7e7
|
|
| MD5 |
327837f2070af234ece08990fbbacd0f
|
|
| BLAKE2b-256 |
fcd3410933338603255bad6689a7ef7e4d573679306709ad451232add611b798
|