Diff Viewers & PR Review Tools
How do diff viewers, structural differs, TUI/GUI git clients, and pull-request review platforms compute, render, align, de-noise, and navigate diffs? This survey is the evidence base for hue's diff & PR view spec (DVM/DVS/DVL/DVN/DVG/DPR), whose motivating pain is a formatter re-aligning unrelated markdown-table rows and drowning the real change in alignment noise.
Last reviewed: August 4, 2026
The survey answers six questions, each a dimension analyzed uniformly in every deep-dive (the fixed spine) and synthesized in the comparison:
- Where does the diff come from — computed in-process, re-parsed from git's output, delegated to a host engine, or server-computed?
- How is it rendered — unified vs side-by-side, how rows align across panes, and how syntax highlighting composes with diff decorations?
- How is noise handled — intra-line refinement, whitespace policy, formatting-only classification, structural equivalence, moved code?
- How does it navigate and scale — hunk/file navigation, unchanged-region folding, large-diff guards?
- How does it integrate with the VCS and review platforms — plumbing, staging, PR comments, revisions, stacks?
- What is reusable — which mechanisms transplant into hue's engine/widget architecture, and which are monolith-bound?
Master catalog
| Subject | Ecosystem | Category | One-line role |
|---|---|---|---|
| delta | Rust | terminal differ | git-output re-styling pager: line state machine + Needleman–Wunsch token alignment |
| git-split-diffs | TypeScript | terminal differ | streaming side-by-side git pager with shiki highlighting and span-preserving wrap |
| Difftastic | Rust | structural diff | tree-sitter CST → atom/list model → Dijkstra minimal edit script; whitespace-immune by construction |
| Mergiraf | Rust | structural merge | tree-sitter 3-way merge driver with per-language commutativity profiles |
| diffoscope | Python | structural diff | recursive archive/binary differ: canonicalize-then-diff with labelled noise disclosure |
| SemanticDiff | proprietary | structural diff | AST diff hiding changes proven irrelevant by curated per-language invariance rules |
| codediff.nvim | Lua/C | editor diff | VSCode diff computer ported to C + FFI, rendered with extmarks and filler alignment |
| diffview.nvim | Lua | editor diff | session orchestrator driving Vim's built-in diff mode through declarative layouts |
| diffview-plus.nvim | Lua | editor diff | maintained fork adding inline unified layout + guarded subword/char refinement |
| diffs.nvim | Lua | editor diff | extmark-only diff layer with a difftastic structural-alignment oracle behind a JSON seam |
| Neovim linematch | C | editor diff | merged core pass (PR #14537) re-pairing xdiff blocks via character-LCS DP across 2–8 buffers |
| Neovim charmatch | C | editor diff | unmerged PR #23569: cross-line char/word DP refinement, superseded by diffopt+=inline: |
| VS Code diff editor | TypeScript | editor diff | worker-computed Myers/DP diff with the richest readability-heuristic pipeline surveyed |
| GitLens | TypeScript | editor diff | renders no diffs: resolves (path, revision) URIs into the host diff editor |
| gitui | Rust | TUI client | async hash-deduplicated libgit2 diff pane doubling as a hunk/line staging surface |
| lazygit | Go | TUI client | outsources diff rendering; owns staging via a pure parse→transform→format patch model |
| Meld | Python/GTK | GUI differ | live-editable 2/3-way panes, regex noise filters, curved link-map connectors |
| WinMerge | C++ | GUI differ | forked diffutils engine with trivial-flagged hunks, substitution-filter re-diff, CSV table mode |
| Araxis Merge | proprietary | GUI differ | commercial ceiling of UI mechanics + the richest regex/positional unimportant-text toolkit |
| Beyond Compare | proprietary | GUI differ | grammar-based unimportant-text classification + key-aligned, tolerance-aware Table Compare |
| Gerrit | Java/TypeScript | web review | server-computed histogram + intraline diffs streamed as classified chunks to gr-diff |
| ReviewStack | TypeScript | web review | serverless SPA computing tree/line/interdiff entirely in the browser from raw git objects |
| Reviewable | proprietary | web review | per-reviewer file×revision matrix + cross-rebase diffing with base-change classification |
| diffy | TypeScript | web review | browser extension replacing GitHub's Files-changed tab; four-stage API diff-fallback cascade |
| av (Aviator) | Go | stacked PR | parent-pointer branch metadata + branching-point SHAs; serializable restack sequencer |
| git-spice | Go | stacked PR | branch-graph state versioned inside a git ref; multi-forge capability discovery |
| git-branchless | Rust | stacked PR | event-sourced smartlog/undo suite; its extracted scm-record TUI hunk selector |
| GitButler | Rust/Svelte | stacked PR | virtual branches partitioning one worktree diff via hunk ownership + line-level locks |
| Graphite | proprietary | stacked PR | stacked-PR platform: version interdiffs, "hide reviewed changes", speculative merge queue |
Taxonomies
By where the diff is computed
| Model | Subjects | Consequence |
|---|---|---|
| Own in-process engine | VS Code, Meld, WinMerge, Difftastic, Mergiraf, ReviewStack, codediff.nvim, Beyond Compare, Araxis Merge, SemanticDiff, Neovim core (linematch, charmatch) | owns the noise policy; can classify, refine, and align freely |
| Re-parses git's text output | delta, git-split-diffs, lazygit, GitButler (UI tier) | must re-infer pairing/alignment; noise policy limited to what survives the text |
| Delegates to a host engine | diffview.nvim (Vim), GitLens (VS Code), diffs.nvim (xdiff + difftastic oracle) | inherits the host's policy; cannot add classification later |
| Server-computed | Gerrit, Reviewable, Graphite, diffy (GitHub-fed) | client renders a classified chunk stream; wire model becomes the seam |
By formatting-noise strategy
The survey's motivating axis — what happens when a formatter re-aligns unrelated rows:
| Strategy | Subjects | Verdict for hue |
|---|---|---|
| None (line red/green only) | gitui, lazygit, git-branchless, av, git-spice, diffy, GitButler | negative evidence: responsiveness alone doesn't solve the table pain |
| Whitespace flags / trim-only | VS Code (ignoreTrimWhitespace), codediff.nvim, diffview-plus.nvim (xdiff flags) | interior re-padding still lights up |
| Lexical rules, filters & re-diff | Meld (regex filters), WinMerge (substitution + re-diff, OP_TRIVIAL), Araxis Merge (line expressions), Beyond Compare (grammar importance), Gerrit (common:true chunks), Reviewable (policy + semantic guard) | demote-don't-hide is the consensus UX contract |
| Structural invariance | Difftastic, Mergiraf, SemanticDiff, diffs.nvim (via difftastic), diffoscope (canonicalize-then-diff) | whitespace-immune by construction — but none covers markdown tables |
| Temporal / workflow | Graphite (interdiffs, mechanical-layer stacking), Reviewable (revision marks), ReviewStack (diff-of-diffs) | orthogonal axis: review the formatter pass once, never again |
By intra-line refinement
| Refinement | Subjects |
|---|---|
| None | gitui, lazygit, git-branchless, diffview.nvim (host's) |
| Naive positional pairing | GitButler, ReviewStack, GitLens (model only) |
| Similarity-paired token alignment | delta (Needleman–Wunsch + grouping bias), VS Code (DP + boundary snapping), Gerrit (char Myers + hygiene passes), Meld (equal-run post-filter), diffview-plus.nvim (guarded two-stage), WinMerge (capped word engine), Neovim charmatch (cross-line char/word DP; mainline chose inline:'s per-block xdiff re-run instead) |
| Structural sub-line | Difftastic (word LCS inside changed regions), SemanticDiff, Beyond Compare (grammar elements) |
Milestones
| When | Milestone |
|---|---|
| mid-1990s | Beyond Compare establishes the commercial compare-suite category (Table Compare, importance grammar) |
| 2000–2002 | WinMerge (2000) and Meld (0.1, 2002-05-18) bring open-source visual diffing with filter stacks |
| 2008 | Gerrit ships server-computed review diffs (Mondrian/Rietveld lineage) — the classified chunk stream is born |
| 2015–2016 | Reviewable (2015) per-reviewer revision matrices; VS Code (2015) and GitLens (2016) editor stack |
| 2018–2020 | TUI wave: lazygit (v0.1, 2018-08), gitui (2020-05) |
| 2019 | delta 0.0.1 (2019-07-16) — syntax-highlighted re-styling of git's own output goes mainstream |
| 2021 | Structural wave: Difftastic 0.2 "first version using Dijkstra" (2021-07-04); diffview.nvim; git-split-diffs; Graphite CLI beta |
| 2022 | ReviewStack (2022-11, with Sapling's open-sourcing); Neovim linematch squash-merged (2022-11-04, ships in 0.9, on by default since 0.11) |
| 2023-05-04 | VS Code 1.78 ships the "advanced" diff computer (moved-code detection, subword refinement, unchanged-region hiding) |
| 2024 | git-spice v0.1.0 (2024-07-21); Mergiraf v0.1.0 (2024-11-01) — structural merging lands |
| 2025–2026 | AI review (Graphite Diamond, 2025-03); Neovim ports Vim's inline:char/word (2025-03-28), closing the charmatch PR; difftastic-as-oracle editor integrations (diffs.nvim v0.1.0, 2026-02-03) |
Suggested reading paths
- "I'm designing hue's diff view" (the spec feed): Difftastic → Mergiraf → Beyond Compare → Gerrit → VS Code → delta → comparison.
- Terminal rendering: delta → git-split-diffs → gitui → lazygit → diffs.nvim.
- The noise problem: SemanticDiff → WinMerge → Araxis Merge → Beyond Compare → Meld → Gerrit.
- PR review & stacks: Gerrit → Reviewable → ReviewStack → Graphite → av → git-spice → GitButler → diffy.
Sources
Each deep-dive carries its own Sources section pinned to the surveyed revision (local checkouts) or the consulted official docs (proprietary products). The synthesis across subjects is comparison.md; the design it feeds is the hue diff & PR view spec.