Recursive AI
LibraryC#Available

onediff

Text diff and document comparison for .NET, in pure C#. Diff, patch and merge text the way git does; compare PDFs and scans into a checked list of changes, marked on the original pages or as a redline.

Open source, MIT or Apache-2.0, made by Recursive AI. Version 1.0.0, the first stable release, is on NuGet.

dotnet add package RecursiveAI.OneDiff

A quick look

The text core is one line per job. For documents, inputs are recognized by their content and the output form follows the extension of the output path.

Getting started
Program.csC#
// Text: changed words, a unified patch, a three-way merge
string marked = Diff.ToMarkdownInline(Diff.Words(oldText, newText));
string patch  = Diff.Unified(oldText, newText);
TextMerge both = Diff.Merge(baseText, ours, theirs);
 
// PDF: marks on v2’s own pages, or a rendered redline
PdfRedline.Register();   // once at startup
Redline.Create("v1.pdf", "v2.pdf", "markup.pdf");
var cmp = Comparison.Of("v1.pdf", "v2.pdf");
cmp.SaveAs("redline.pdf", PdfRedline.Rendered);
A rendered redline

Edits, a move, and the renumbering it caused

The repository’s own sample: page 2 of blackline-inline.pdf, made by OneDiff 1.0.0 from two short versions of a services agreement. Shown here is the top of the page: the renumbering the move caused, grouped as one cascade, a replaced word, and the moved clause marked at its new place. Further down, the same clause is struck through where it was, and a quote-mark difference the settings hide is counted under “Differences not shown” instead of dropped.

Every redline look
blackline-inline.pdf, page 2 (top)docs/samples
A blackline page headed “Grouped changes”. A group titled “Renumbering and the references it changed (3 changes)” lists 1.5 → 1.2, 1.2 → 1.3 and 1.3 → 1.4. Below, the start of the Services Agreement with change bars: in clause 1.1, “thirty” is struck through in red and “forty-five” inserted in blue; clause 1.2, the governing-law clause moved up from 1.5, is underlined in green with an arrow at its new place.
What it does

What it does

Text diff

Myers, patience and histogram over any sequence; line, word, character and grapheme tokens; semantic and efficiency cleanup; a work cap for untrusted input. No dependency beyond the .NET base class library.

Diff algorithms

Patches and merge

Unified and diff-match-patch patches with fuzzy apply and conflict reporting, and three-way merge in git’s merge, diff3 and zdiff3 conflict styles.

Three-way merge

A checked change list

Every comparison is proved complete: old plus the changes gives new, and new minus the changes gives old. Differences a setting hides, such as quotes or page numbers, are counted, never dropped. Saved as versioned JSON and checked again when read back.

The compare model

Changes as a reader sees them

Alignment that follows moves, splits and merges; typed values (30 days → 60 days); renumbering and renamed defined terms as single changes; a filter for what changed in substance.

Change semantics

PDF redlines

Highlights and notes on the new PDF’s own pages, a rendered and tagged redline PDF, or HTML inline or side by side. A PDF with no text layer is refused with a message naming the OCR package, never compared as empty. From the results page: “one 29.7 MB pair exceeds the renderer’s display-list limit; the rendered redline gives a clear error for it.”

PDF comparison

Scans

Scanned PDFs and images read by a local OCR engine, every word with its confidence. Changes that may be recognition errors are flagged, never hidden. Latin-script only; in the results page’s words, boundary and move scores “are limited by recognition errors and by paragraphs the recognition engine merges”. Scanned PDFs aren’t yet read in the browser: the project’s test source says rasterizing them through OneImage.Pdf “fails to load an internal OneImage type under the browser runtime (TypeLoadException for OneImage.DeterministicMath, still with OneImage 1.10.5)”, so those tests are skipped there.

Scans and images

Reviewers and versions

Combine several reviewers’ copies of one original, with each change attributed and conflicts listed, or compare a chain of versions step by step, as a report in HTML or JSON.

Combine and version chains

Word, spreadsheets, slides

Not in 1.0. In the README’s words: “Coming in later releases: Word documents in and tracked-changes DOCX out (RecursiveAI.OneDiff.Word, in this repository and not in the first release), spreadsheets, slides, and the OneNLP value recognizer (RecursiveAI.OneDiff.Nlp). Until then a Word file is refused with a message that names the package.”

What works at 1.0

The onediff tool

diff, patch, merge, compare, combine and chain from a shell. onediff compare v1.pdf v2.pdf -o markup.pdf does what the library does.

Install the tool
How it measures

Correctness and speed

The project’s own measurements at the release (not an external audit), with their conditions and every losing row on the results page.

  • Against the reference tools. Myers’ edit length equals GNU diffutils 3.12’s minimum on 9,483 of 9,483 code and prose pairs. Three-way merge equals git merge-file byte for byte on 2,599 of 2,599 real triples in all three conflict styles. Unified patches are accepted by GNU patch and git apply on 9,481 pairs, 2 skipped, 0 failed.
  • Text speed against DiffPlex 1.9.0 and DiffMatchPatch 5.0.0. Single-threaded, inputs of about 2 KB, 40 KB and 300–400 KB. Each ratio is OneDiff’s time over the comparator’s, so below 1 is faster. Each mean is the geometric mean of one area’s rows against one comparator, on x64 (AMD Ryzen 9 7950X3D, Linux): lines 0.19 against DiffPlex and 0.52 against diff-match-patch’s line mode; words 0.29 against DiffPlex, which cuts words differently and so does not do equal work; characters 0.01 against DiffPlex and 0.22 against diff-match-patch; patch apply 0.61; patch make 0.85.
  • The slowest rows. Patch make on the medium input, 1.13 on x64 and 1.02 on arm64 (Apple M2, macOS); efficiency cleanup 0.89 and words against DiffPlex 0.81, both on x64. Not covered: word rows against diff-match-patch, large character, cleanup and patch rows, and native tools such as git and GNU diff.
  • Documents. No managed comparator exists for PDF or scan comparison, so these are budgets: a 100-page PDF pair with about 300 changes is read, compared and marked in 841 ms on x64 and 726 ms on arm64 (budget 2 s), and rendered as an inline redline in 1,178 ms and 1,197 ms.
Every result, including the rows that lose

Status and packages

Go further

OneDiff is unaffiliated with any other project named OneDiff, including the Python diffusion-model accelerator of that name.