feat(diff): context/normal output formats, Myers algorithm as new default - #216
Merged
Conversation
Fab-Cat
approved these changes
Jul 3, 2026
helly25
force-pushed
the
diff-formats-myers
branch
from
July 3, 2026 22:59
135c848 to
e8d6ebb
Compare
…ault
Formats (DiffOptions::output_format, --format, diff_test format attr):
- unified (default), context (diff -c) and normal (plain diff) output,
byte-identical to GNU diff and verified to apply cleanly with patch(1).
- Rendering extracted from internal/chunk.cc into internal/output.{h,cc};
Chunk now only accumulates and filters, file headers are format aware.
- Fixed unified empty-range chunk headers (pure insertions/deletions with
--context=0) to reference the line preceding the gap like GNU diff;
previously patch applied such hunks one line too late.
Myers algorithm (impl/diff_myers.*, now the default):
- Linear-space middle-snake divide and conquer over interned line tokens
(all ignore/strip/replace options apply); minimal diffs; git-style
furthest-reaching split past a cost cap of max(64, sqrt(L+R)).
- Replays through the ChunkedDiff push interface, so chunking, filters and
all output formats are shared.
- >2x faster on scattered edits, disjoint 2k-line inputs drop from ~16s to
<1ms (//mbo/diff:diff_benchmark).
Algorithm naming:
- The old default engine is renamed unified -> naive (what it is: greedy
closest-match resynchronization, not minimal): DiffNaive, kNaive,
impl/diff_naive.*.
- The flag value 'unified' stays as a deprecated alias that now selects
myers (matching its "like diff -u" promise) and enforces unified format
(CLI check and bzl macro fail()).
Feature/algorithm matrix:
- diff_test_formats_test bzl macro crosses output formats (each with its
own golden) with algorithms; golden coverage for
{myers,naive}x{unified,context,normal} and direct x all formats.
- AlgorithmFeatureMatrix unit test: all comparison options behave
identically under naive, myers and direct.
- Documented corner case: ignore_case with a case-sensitive
ignore_matching_lines expression diverges between naive and myers;
write such expressions as (?i)....
- max_diff_chunk_length only applies to naive (myers uses its cost cap).
helly25
force-pushed
the
diff-formats-myers
branch
from
July 3, 2026 23:03
e8d6ebb to
00561c2
Compare
pre-commit and trunk cover disjoint formatters (pre-commit: clang-format, shfmt; trunk: prettier, buildifier, ...), so markdown/yaml/bzl edits could pass the local hooks and then fail the trunk CI job. Add a local trunk-fmt hook that invokes the trunk CLI on the staged files, keeping .trunk/trunk.yaml the single source of truth for formatter versions. pre-commit remains the sole owner of the git hooks (trunk's git-hook actions stay disabled); the hook is skipped in CI (SKIP env plus ci.skip) where the trunk job runs the full check. Requires the trunk launcher locally (brew install trunk-io).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three related changes to
mbo/diff, developed and verified together:1. Output formats:
unified|context|normalDiffOptions::output_format/--formatflag /formatattr on the bazeldiff_testrule.diff -c) and normal format (plaindiff) reproduce GNU diff byte for byte (verified against the system diff on 18 case families incl. empty files, no-newline markers, multi-hunk inputs) and apply cleanly withpatch(1).internal/chunk.ccintointernal/output.{h,cc}(one emitter per format);Chunknow only accumulates and filters, and file headers are format aware (***/---for context, none for normal).--context=0) now reference the line preceding the gap (@@ -2,0 +3 @@instead of@@ -3,0 +3 @@), matching GNU diff. Previouslypatchapplied such hunks one line too late. Found by round-tripping every format throughpatch.2. Myers diff algorithm — the new default
impl/diff_myers.*: linear-space middle-snake divide and conquer ("An O(ND) Difference Algorithm and Its Variations", the algorithm behind GNU diff and git) over interned line tokens, so every ignore/strip/replace option applies unchanged. Produces minimal diffs; past a cost cap of max(64, √(L+R)) it takes a git-style furthest-reaching split to bound pathological inputs.Replays through the existing
ChunkedDiffpush interface: chunking, context handling, filters and all three output formats are shared.Benchmark (
bazel run -c opt //mbo/diff:diff_benchmark, 10k-line inputs):The old engine is renamed to what it is:
naive(DiffNaive, greedy closest-match resync, not minimal). The flag/attr valueunifiedremains a deprecated alias that now selectsmyers— matching its historic "likediff -u" promise — and enforces--format=unified(CLI check + bzlfail()).3. Feature/algorithm support matrix
diff_test_formats_testmacro crosses output formats (each format checked against its own golden) with algorithms; golden coverage now spans{myers,naive} × {unified,context,normal}plusdirect × all formats.AlgorithmFeatureMatrixunit test proves all comparison options (ignore_case,ignore_*_space,ignore_blank_lines,ignore_matching_lines,strip_comments,regex_replace_*) behave identically undernaive,myersanddirect.ignore_case+ a case-sensitiveignore_matching_linesexpression diverges betweennaiveandmyers; write such expressions as(?i)....max_diff_chunk_lengthonly applies tonaive(documented;myersuses its internal cost cap).Test plan
bazel test //...— 91 tests pass (40 in//mbo/diff/..., incl. 15 new golden targets and the new unit tests).MyersRoundTripproperty test:apply(lhs, diff) == rhsover a deterministic corpus incl. a disjoint pair that exercises the cost-cap fallback.diff/diff -c/diff -U0andpatchround-trips for all three formats × {naive, myers} on 11 input families; hang-regression bisect for the fallback path (fixed grid-edge clamping in the furthest-reaching recurrence).