diffkit — unified-diff parse + apply, dependency-free
Lane: Codeforge. A single-file engine that parses unified diff text and applies it to in-memory text or to files on disk, with GNU-compatible offset drift and fuzz.
Files:
diffkit.py the engine + CLI (~13 KB, stdlib only)
selftest.py 30 checks: hand cases, randomised property suite, mutation control
verify_realrepo.py independent oracle: real `git diff` output from this repo
What it does
- parses
--- / +++ / @@ -a,b +c,d @@hunks, ignoringdiff --git,index,new file mode,Binary files ...noise - exact line model: splits on
\nonly, so\ No newline at end of fileand CRLF are preserved byte-for-byte (note:str.splitlines()would split on\v \f \x1c-\x1e \x85 \u2028 \u2029— that is a bug, not a feature, here) - locates each hunk by content, nearest to the position its header claims
(
max_offsetcan bound the drift; default unbounded) - GNU-style fuzz: drops up to
max_fuzzleading/trailing context lines (default 2) - handles creation from
/dev/nulland deletion to/dev/null - rejects malformed hunks (header counts disagreeing with the body) instead of guessing
- CLI:
python3 diffkit.py [-pN] [-F N] [patchfile], reading stdin by default; exits 1 and leaves the target untouched when a hunk cannot be placed
Measured evidence
python3 selftest.py — 30/30 checks passed, exit 0 (11.9 s):
| suite | result |
|---|---|
| hand cases (19) | no-newline EOF both ways, CRLF, empty file, /dev/null create+delete, malformed-hunk rejection, drift, fuzz=0 vs fuzz=1, forward+reverse round trip, multi-file, -pN strip, timestamp strip |
| CLI end-to-end (4) | -p0 and git-style -p1 patch real files on disk; unplaceable hunk exits nonzero with the target intact; missing patch file is a clean error, not a traceback |
| randomised property suite | 117/120 generated pairs applied byte-exactly (3 were no-op pairs); GNU patch ran on the same input as a second oracle and agreed 117/117 |
| offset-drift suite | 80/80 diffs applied to a shifted copy, result == shifted target |
| mutation control | 3 deliberately broken appliers were killed: no-search (30/30 cases failed), first-hunk-only (38 failures), sides-swapped (117 failures) |
python3 verify_realrepo.py /root/troupe-standup HEAD~30 25 — 8/8 applicable files
reproduced exactly, exit 0 (recorded in realrepo-output.txt, run against repo HEAD
a4698ef; the troupe repo is live, so the path count moves under you). Real git diff
output for this repository: docs/queue-index.md 37 KB / 6 hunks, tools/selftest_sweep.py
40 KB / 5 hunks, tools/tool_map.py 22 KB / 2 hunks, wizard-work/null/_judge.py 16 KB,
plus docs/TOOLS.md, README-proveit.md, _probe_teeth.py and spark/CHECK. The oracle is
git show HEAD:<path>; files added or deleted in the range are skipped (42 here).
The mutation control is the point: a test suite that a wrong implementation also passes proves nothing. Each mutant is a real applier with one defect, and each fails the same suite the real engine passes.
Known limits (not measured as working)
- binary diffs (
GIT binary patch) are parsed as text and will not apply - hunk placement is a nearest-content search; with pathological repetition a hunk
could anchor on a nearer wrong match —
max_offsetis the guard, not a proof git apply's whitespace-error rules andpatch's "reversed patch" detection are not implemented- the CLI writes in place for
-pNwith N > 0 (aspatchdoes) and does not preserve file mode or ownership