---
title: "onediff"
url: https://recursiveai.org/onediff/
description: "Text diff and document comparison for .NET, in pure C#: Myers, patience and histogram diff, unified patches, three-way merge in git’s conflict styles, and PDF and scan comparison with a verified change list, marks on the original pages and a rendered redline. Open source, MIT or Apache-2.0. 1.0.1."
---

# 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.1 is on NuGet.

```sh
dotnet add package RecursiveAI.OneDiff
```

- [Getting started](https://recursiveai.org/onediff/docs/getting-started/)
- [Docs](https://recursiveai.org/onediff/docs/)
- [GitHub](https://github.com/recursive-ai-oss/onediff-csharp)
- [NuGet](https://www.nuget.org/packages/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](https://recursiveai.org/onediff/docs/getting-started/)

*Program.cs · C#*

```
// 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);
```

## Edits, a move, and the renumbering it caused

The repository’s own sample: page 2 of `blackline-inline.pdf`, made by OneDiff 1.0.1 from two short versions of a services agreement. The moved clause is marked at both ends, the renumbering it caused is grouped as one cascade, and further down the page a quote-mark difference the settings hide is counted under “Differences not shown” instead of dropped.

[Every redline look](https://recursiveai.org/onediff/docs/samples/)

*blackline-inline.pdf, page 2 · docs/samples*

```

```

### 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](https://recursiveai.org/onediff/docs/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](https://recursiveai.org/onediff/docs/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](https://recursiveai.org/onediff/docs/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](https://recursiveai.org/onediff/docs/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](https://recursiveai.org/onediff/docs/pdf/)

### 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”. A known issue, in the README’s words: “In browser WebAssembly, reading scans with `RecursiveAI.OneDiff.Ocr` fails with a `TypeLoadException` from the image library it uses (OneImage). Scans compare normally on every native platform; text, document and digital-PDF comparison work in the browser.”

[Scans and images](https://recursiveai.org/onediff/docs/ocr/)

### 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](https://recursiveai.org/onediff/docs/combine/)

### 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 an Office file is refused; the 1.0.1 message ends: “Support for Office documents (DOCX, XLSX and PPTX) is coming in a 1.x release. Meanwhile, export the documents to PDF and compare the PDFs (with RecursiveAI.OneDiff.Pdf).”

[What works at 1.0](https://recursiveai.org/onediff/docs/migrating-from-wmlcomparer/)

### 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](https://recursiveai.org/onediff/docs/getting-started/)

## 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](https://recursiveai.org/onediff/docs/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](https://recursiveai.org/onediff/docs/results/)

## Status and packages

- **Status**: 1.0.1, which fixes the Office-file messages, the labels of attribute changes in redlines and the wording of the cover page. 1.0.0, from the changelog: “The first stable release of `RecursiveAI.OneDiff`, `RecursiveAI.OneDiff.Documents`, `RecursiveAI.OneDiff.Pdf`, `RecursiveAI.OneDiff.Ocr` and the `onediff` tool (`RecursiveAI.OneDiff.Tool`).”
- **Text core**: `RecursiveAI.OneDiff`: diff, patches and three-way merge, with no dependency beyond the base class library. 1.0.1.
- **Documents**: `RecursiveAI.OneDiff.Documents`: the comparison engine, combine and chains, HTML redlines and the JSON change list, on OneFont. `.Pdf`: PDF in, marked and rendered PDF out, on OnePDF and OneRender. `.Ocr`: scans, on OneOCR. 1.0.1.
- **Tool**: `RecursiveAI.OneDiff.Tool`, the `onediff` command: `dotnet tool install --global RecursiveAI.OneDiff.Tool`. 1.0.1.
- **.NET**: .NET 10, C#, no native code, NativeAOT and trimming. The same inputs and settings give byte-identical output, and nothing touches the network. The test suites of the core, `.Documents`, `.Pdf` and `.Ocr` also run as browser WebAssembly apps (x64 only); tests that need files, threads, processes or AES are skipped there, 10 of 18 for `.Ocr`. MIT or Apache-2.0.

## Go further

- [GitHub](https://github.com/recursive-ai-oss/onediff-csharp)
- [NuGet](https://www.nuget.org/packages/RecursiveAI.OneDiff)
- [Getting started](https://recursiveai.org/onediff/docs/getting-started/)
- [All docs](https://recursiveai.org/onediff/docs/)
- [From DiffPlex](https://recursiveai.org/onediff/docs/migrating-from-diffplex/)
- [Results](https://recursiveai.org/onediff/docs/results/)
- [Changelog](https://recursiveai.org/onediff/docs/changelog/)

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