logicdiff
A whitespace- and reflow-blind diff. A pull request reindents a file and
rewraps a few long lines, and now git diff shows 80 changed lines — but did
anything actually change? logicdiff answers that: it folds away pure
formatting (respacing and line reflow) and shows only the logical changes.
logicdiff old.js new.js
# only formatting differs - no logical change (a line diff would show 80 changed lines)
logicdiff a.js b.js
# --- a.js
# +++ b.js
# -42: const total = price * qty;
# +51: const total = price + qty;
#
# 1 token removed, 1 added across 2 logical lines (78 lines folded as reflow/whitespace)
Exit 0 when the change is formatting-only (or identical), 1 when there's a
real logical change — so CI can ask "is this PR just a reformat?" Zero
dependencies, language-agnostic, also on npm (npx logicdiff) — the two
builds produce byte-for-byte identical output.
Why not git diff -w?
git diff -w (ignore-all-space) folds respacing — but it is still
line-anchored, so it cannot fold reflow. Re-wrap a function signature across
three lines and git diff -w still shows 1 removed + 3 added, even though not a
single token changed. That exact gap is GitHub discussion #20610
("Ignore Format Changes in Diff"), open and unanswered for years.
difftastic solves it beautifully with per-language tree-sitter parsing — but
it's a multi-megabyte binary, needs a grammar for each language (config/log/DSL
files fall back to text), and it's a display tool with no "is this
formatting-only?" exit code.
logicdiff is the lightweight middle ground: zero-config, zero-dependency,
language-agnostic (works on any text — code, YAML, logs, DSLs), folds both
whitespace and reflow, and gives a one-shot CLI answer plus a CI exit code.
How it works
It tokenizes each file into a sequence of tokens — a token is a run of
[A-Za-z0-9_] or a single punctuation character, and whitespace is dropped.
So a+b, a + b, and a +\n b all become the same token stream [a, + , b]:
respacing and line breaks become invisible. It then runs the canonical
Myers diff on the token streams. If the streams are equal, the change is
formatting-only. If not, the changed tokens are mapped back to their line
numbers and shown.
Because it has no language parser, whitespace inside string literals is also
ignored — x = "a b" and x = "a b" are "formatting only", exactly like
git diff -w. That's a deliberate, documented limitation, not a bug.
Usage
logicdiff old new # human diff (or "only formatting differs")
logicdiff old new --stat # just the counts, machine-friendly key=value
logicdiff old new --json # structured output (byte-identical both builds)
logicdiff old new -q # no output, exit code only (the CI gate)
cat new | logicdiff old - # - reads stdin
--color=auto|always|never, --max-tokens N (bail over N tokens, default 2,000,000).
Two wildly dissimilar inputs (a huge edit distance) also bail with exit 2 instead of
risking the heap — logicdiff is for spotting a real change inside a reformat, not for
diffing unrelated files.
Exit codes: 0 identical or formatting-only · 1 logical changes · 2 error.
# CI: warn when a PR is more than a reformat
- run: logicdiff "$BASE" "$HEAD" -q || echo "::warning::real code change, review carefully"
Install
pip install logicdiff # or pipx run logicdiff
npm i -g logicdiff # Node build, identical behaviour
Python ≥ 3.8 or Node ≥ 18. No dependencies.
License
MIT
Metadata
Release files for logicdiff 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| logicdiff-0.1.0.tar.gz | 12.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| logicdiff-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 22.8 kB
Release files / logicdiff-0.1.0.tar.gz
| Download URL | logicdiff-0.1.0.tar.gz |
|---|---|
| Size | 12.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b8b39551202f8ba5fc7da09df2b3e0543c3a41dbdd54f904c4ae8e515a530a35
|
|
BLAKE2b-256 checksum How to use checksums |
caf6cc449c15d63fe5bb4bd68b5a26b46f9c1e08c1b3fffc640d9c62293661e3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.7
|
Release files / logicdiff-0.1.0-py3-none-any.whl
| Download URL | logicdiff-0.1.0-py3-none-any.whl |
|---|---|
| Size | 10.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fdc2081cbfbf388e47392b8616ee05ba52329486a24baf85c31078eb18f89f04
|
|
BLAKE2b-256 checksum How to use checksums |
e91947491e57bf4b02f731ea89aa08b4bfeb4ab2ec3f392adff2b1a16e8d7479
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.7
|