--- id: BB-RESEARCH-_H2TE28K type: research title: Luigit diff engines and concepts --- # Luigit diff engines and concepts ## Scope Separate Git change detection, textual diffing, structural matching, syntax highlighting, and browser rendering. Assess Mergiraf, Difftastic, Pierre, and embeddable alternatives for a read-only Git UI delivered as one executable. ## Conceptual layers Git-aware change detection establishes changed paths, modes, renames, copies, binary state, submodules, and hunks. A textual diff computes edits between supplied contents. A structural diff parses both contents, matches syntax nodes, and derives structural edits. Syntax highlighting assigns visual tokens. A renderer turns those facts into unified, split, or interactive presentation. These layers have different correctness, performance, dependency, and fallback requirements. No surveyed library owns all layers well. “Semantic diff” is an imprecise name. Tree-sitter supplies concrete syntax trees, not symbols, types, behavior, or semantic equivalence. Any claimed semantics must name its exact normalization or language rule. ## Difftastic Difftastic is an MIT-licensed Rust CLI for two-way structural diff. Version 0.70.0 declares only a binary target and no library target. Its internal Rust surface is not a supported stable embedding API. It parses supported languages with Tree-sitter, reduces syntax into matching units, and treats matching as lowest-cost route finding over a lazily explored directed acyclic graph. It uses Dijkstra's algorithm rather than constructing the exponential graph eagerly. Difftastic falls back to line-oriented diff when a language is unsupported, parsing exceeds tolerance, inputs exceed limits, or structural search exceeds its graph budget. Its useful concepts are: - Syntax-aware matching without claiming behavioral equivalence. - Formatting-insensitive comparison. - Real old and new line numbers. - Explicit byte, graph, and parse-error limits. - Textual fallback as normal behavior rather than failure. Its JSON output is presentation-oriented and is not documented as a stable protocol. Known expensive cases and absence of a stable library boundary make direct embedding unsuitable without owning a fork. Subprocess integration conflicts with the one-executable boundary. Sources: [Difftastic introduction](https://difftastic.wilfred.me.uk/introduction), [diffing internals](https://difftastic.wilfred.me.uk/diffing.html), [supported languages](https://difftastic.wilfred.me.uk/languages_supported.html), [0.70.0 manifest](https://raw.githubusercontent.com/Wilfred/difftastic/0.70.0/Cargo.toml). ## Mergiraf Mergiraf is a GPL-3.0-only Rust CLI and Git merge driver. It is a three-way merge engine, not a two-way diff renderer. It consumes base, left, and right versions and produces merged text, possibly with conflicts. Its fast path first runs line-based diff3. Clean textual results avoid structured work. When needed, it parses all three revisions with Tree-sitter, matches each tree pair with GumTree Classic, creates cross-revision node identities, merges Parent-Child-Successor triples, and reconstructs text. Parsing failure falls back to line-based merge. Its useful concepts are: - Cheap ordinary path before expensive structure. - Structure attempted only for eligible content. - Local fallback to line-based merge inside unresolved structures. - Explicit delete/modify and duplicate-signature conflict checks. - Time-bounded structured attempts returning an ordinary result. Upstream states that Mergiraf is not designed as a library and its Rust API is not stable. Its timeout waits for a worker result and is not evidence of cancellation or a memory bound. Its CLI-shaped API, unstable boundary, merge-specific role, and GPL obligations make it a concept source rather than a Luigit dependency. Sources: [Mergiraf architecture](https://mergiraf.org/architecture.html), [library status](https://docs.rs/mergiraf/latest/mergiraf/), [supported languages](https://mergiraf.org/languages.html), [conflicts](https://mergiraf.org/conflicts.html). ## Pierre Diffs `@pierre/diffs` 1.3.6 is an Apache-2.0 TypeScript browser library. It renders supplied patches or old and new file contents. It does not inspect Git repositories or establish Git change facts. It provides vanilla JavaScript components and separate React bindings. Its surface includes stacked and split layouts, inline highlights, line selection, annotations, custom fonts, light and dark themes, Shiki syntax highlighting, optional workers, and virtualized file or diff views. The npm package reports about 6.9 MB unpacked, which is not a transfer-size or runtime-memory measurement. Pierre can be embedded as browser assets inside one server executable. It cannot provide the required no-JavaScript path by itself. An independent server-rendered textual diff remains necessary. Using Pierre for server-side rendering outside a JavaScript server would require an embedded runtime or another process. Pierre is therefore a plausible progressive renderer, not a Git or diff engine. Its value depends on measured browser payload, startup cost, memory, virtualization quality, theme control, and accessibility. No formal API-stability policy or independent performance study was found. Sources: [Pierre Diffs README](https://raw.githubusercontent.com/pierrecomputer/pierre/diffs-v1.3.6/packages/diffs/README.md), [package manifest](https://raw.githubusercontent.com/pierrecomputer/pierre/diffs-v1.3.6/packages/diffs/package.json), [public exports](https://raw.githubusercontent.com/pierrecomputer/pierre/diffs-v1.3.6/packages/diffs/src/index.ts), [rendering article](https://pierre.computer/writing/on-rendering-diffs), [documentation](https://diffs.com/docs). ## Git-aware library candidates ### libgit2 libgit2 exposes tree, index, worktree, blob, and buffer diffs. It models file deltas, hunks, lines, binary data, statistics, rename and copy detection, and formatted patch output. It is written in C and can be embedded directly or through language bindings such as `git2-rs`. It is the strongest surveyed cross-language candidate for C-Git-like diff facts, but introduces native build, linking, platform, and license-notice work. Rich intraline rendering remains a separate concern. Sources: [libgit2 diff API](https://libgit2.org/docs/reference/main/diff/index.html), [rename and copy options](https://libgit2.org/docs/reference/main/diff/git_diff_find_options.html), [git2-rs](https://github.com/rust-lang/git2-rs). ### gix-diff `gix-diff` provides pure-Rust Git tree, index, blob, and rewrite tracking. Its documentation explicitly states that rename and copy detection differs from Git and is less sophisticated. That difference requires fixture-based acceptance rather than assumption. Source: [gix rewrite tracker](https://docs.rs/gix-diff/latest/gix_diff/rewrites/tracker/). ### go-git Go-git exposes per-file patches, ordered chunks, binary classification, and a unified encoder. Binary file chunks are empty. Its unified encoder does not support rename similarity indexes. It remains a plausible pure-Go candidate only after Git-fidelity tests. Sources: [go-git diff format](https://pkg.go.dev/github.com/go-git/go-git/v5/plumbing/format/diff), [go-git](https://github.com/go-git/go-git). ### JGit JGit provides a mature pure-Java Git implementation with diff formatting, rename detection, and binary handling. Its natural deployment is a JVM application. Whether it can satisfy the one-executable boundary requires separate packaging research. Source: [JGit](https://github.com/eclipse-jgit/jgit). ## Textual diff candidates The Git-aware layer and textual edit layer may be separate. A text library must never invent modes, renames, copies, binary classification, or submodule state. Rust's `similar` supports line, word, character, grapheme, and byte comparison; Myers, Patience, Histogram, Hunt, and LCS algorithms; intraline output; unified output; and explicit deadlines. It is a strong bounded intraline candidate, not a Git engine. Rust's `imara-diff` exposes Myers and Histogram algorithms, structured hunks, indentation-aware post-processing, and optional unified output. It emphasizes pathological-case performance and reports benchmarks against real repositories. Its claims require reproduction on Luigit's corpus. Rust's `diffy` parses and applies unified patches and optionally Git binary patches. It matters only if Luigit consumes patch documents rather than computing directly from repository objects. Java's `java-diff-utils` and JavaScript's `jsdiff` cover textual and intraline work within their runtime boundaries. Neither supplies Git repository semantics. Sources: [`similar`](https://docs.rs/similar/latest/similar/), [`imara-diff`](https://docs.rs/imara-diff/latest/imara_diff/), [`diffy`](https://docs.rs/diffy/latest/diffy/), [java-diff-utils](https://github.com/java-diff-utils/java-diff-utils), [jsdiff](https://github.com/kpdecker/jsdiff), [Git diff algorithms](https://git-scm.com/docs/git-diff). ## Structural candidates Tree-sitter is an active MIT parser with broad grammar coverage and source positions. It provides no node matcher or diff algorithm. Grammar quality, build requirements, and licenses vary independently. GumTree is an active LGPL-3.0 Java library for AST node mapping and insert, delete, update, and move edit scripts. It is the strongest surveyed structural library boundary, but brings JVM and LGPL constraints; its current v4 line is beta. Diffsitter exposes a Rust library target, but upstream describes that library as CLI-supporting and not cohesive. Difftastic has no library target. No mature, stable, general native library was found that combines Tree-sitter parsing, structural matching, source-range mapping, and bounded web-oriented output. Sources: [Tree-sitter](https://github.com/tree-sitter/tree-sitter), [GumTree](https://github.com/GumTreeDiff/gumtree), [diffsitter](https://docs.rs/diffsitter/latest/libdiffsitter/). ## Browser renderer alternatives Diff2Html parses unified, Git, and combined patches and renders stacked or split HTML. It is framework-neutral and configurable, but has no documented virtualization or accessibility API. Server-side use still requires a JavaScript runtime. CodeMirror Merge offers bounded interactive two-document comparison and collapsed unchanged regions. It consumes documents rather than Git patches and has no ordinary server-rendered path. Monaco is an editor platform rather than a minimal diff renderer. Its scope and distribution size are disproportionate for Luigit. Shiki is a syntax highlighter, not a diff engine or renderer. Its themes and grammars can be selected narrowly, but server-side use requires JavaScript and browser-side use adds payload. Sources: [Diff2Html](https://diff2html.xyz/), [CodeMirror Merge](https://registry.npmjs.org/@codemirror%2fmerge/latest), [Monaco diff options](https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor_editor_api.editor.IDiffEditorBaseOptions.html), [Shiki](https://shiki.style/guide/install). ## Conclusions Luigit needs an internal diff model independent of presentation. Git-aware facts must remain distinct from line edits, structural annotations, syntax tokens, and browser state. The canonical path should remain a complete escaped textual diff with headers, hunk coordinates, context, visible prefixes, and binary notices. Unified and split views may share the same facts. Optional intraline, structural, highlighting, and virtualization layers may disappear under budget or failure without removing canonical content. Difftastic and Mergiraf are concept sources, not current library candidates. Pierre is a credible optional browser renderer only if measurement justifies its JavaScript surface and no-JavaScript fallback remains independent. A realistic implementation will compose: - One Git-aware library for repository facts. - One bounded textual or intraline engine where the Git library is insufficient. - Luigit-owned safe server rendering. - Optional structural analysis behind explicit eligibility and resource gates. - Optional browser enhancement after the textual document is complete. A structural eligibility gate needs a known grammar, accepted text encoding, parser-quality policy, file-size cap, node-count cap, time budget, and memory strategy. Failure always returns to ordinary text. Public-request limits are correctness requirements. Budgets must cover blob and patch bytes, files, hunks, lines, line length, output size, rename candidates, structural nodes, CPU time, and memory. A timeout alone is neither cancellation nor a memory bound. Implementation language cannot be selected from diff quality alone. The surviving library families differ enough that Git fidelity, one-executable packaging, license fit, syntax highlighting, signatures, notes, search, and operational behavior must be evaluated together. ## Conclusions: non-candidates now - Difftastic direct embedding, because no stable library target exists. - Mergiraf direct embedding, because it is a merge engine with unstable API and GPL-3.0-only obligations. - Difftastic or Mergiraf subprocesses, because runtime helpers violate the one-executable boundary. - Diffsitter as a foundation, because its library is explicitly immature. - Monaco, because editor-platform scope overwhelms the read-only requirement. - Google diff-match-patch, because upstream is archived and maintained alternatives exist. ## Conclusions: evidence needed - Build a Git-fidelity corpus covering add, delete, modify, mode change, rename, copy, binary, submodule, quoted path, missing final newline, merge, and combined diff cases. - Compare each Git-aware candidate's normalized facts with an agreed Git baseline. - Blind-review Myers and Histogram output on representative, repetitive, generated, formatting-heavy, and highly dissimilar files while measuring latency and peak memory. - Force intraline deadlines on huge lines and adversarial Unicode; complete line content must remain. - Test structural matching on formatting, moves, reorderings, comments, delimiters, malformed syntax, preprocessors, and large strings. - Increase bytes and syntax-node counts independently; eligibility caps must prevent unbounded structural work. - Build Pierre with the exact required features, Nugu themes, and intended grammar set; measure compressed payload, parse time, first render, scroll behavior, and memory. - Disable JavaScript; the same diff facts, navigation, and visible change states must remain readable. - Compare plain server HTML, Pierre, Diff2Html, and a minimal custom enhancement on one fixed large diff. - Verify keyboard navigation, line identity, focus order, and insertion or deletion meaning without color. - Produce the real one-executable container artifact for each surviving native stack and audit runtime files plus licenses. ## Unresolved questions - Which implementation language survives the complete Luigit capability research? - Must rename and copy detection match C Git exactly? - Does Luigit compute only from repository objects, or also parse external patch documents? - Which Git patch forms are mandatory beyond ordinary two-parent text diffs? - Which source languages justify compiled grammar and syntax-theme cost? - Should structural output annotate ordinary hunks, regroup them, or become a separate view? - Should syntax-error trees always fall back or permit labeled best effort? - What concrete latency, memory, blob-size, and browser-payload budgets govern adoption? - Does Pierre's virtualization produce enough benefit over a small custom view to justify its dependency surface? - Can one selected stack support Git facts, notes, signatures, search, syntax highlighting, and diffing without fragmented runtimes?