Skip to main content

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

diffmatch-0.1.0.tar.gz (11.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

diffmatch-0.1.0-py3-none-any.whl (12.0 kB view details)

Uploaded Python 3

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

Hashes for diffmatch-0.1.0.tar.gz
Algorithm Hash digest
SHA256 1399f87b941cd2ce68d2abe7738ba932e7c726a53571944e5ccece7404a018d0
MD5 3928cf54c10aa43d5f55d666809d3f05
BLAKE2b-256 4d063d9e8a17eb66da347a6494974c391a7b4488796b6520794336c2a672e90d

See more details on using hashes here.

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

Hashes for diffmatch-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 03aa02339c2ade6d5fd8e2217c8b225fad99b4a9dc93884f5e2d336f99d1b7e7
MD5 327837f2070af234ece08990fbbacd0f
BLAKE2b-256 fcd3410933338603255bad6689a7ef7e4d573679306709ad451232add611b798

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page