Library / SDK
mmanela/diffplex avatar
mmanela/diffplex

DiffPlex: a C# diff library where the chunker is the extension point

DiffPlex is Netstandard 2.0+ C# library to generate textual diffs.

1,285 stars202 forksC#Apache-2.0

At a glance

What is it?
DiffPlex is a .NET Standard C# library for generating textual diffs, with a low level IDiffer, a side by side model, a three way merger and a unified diff renderer, and its most interesting design choice is that diffing granularity is a pluggable IChunker rather than a fixed set of modes. The last NuGet release is 1.7.0 from 2021, and the source has moved on since.
Who is it for?
DiffPlex fits a.NET team that needs to show textual differences in a UI, produce a patch, or merge three versions, and that wants the diff granularity to be a policy decision rather than a hardcoded mode.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 43 days ago.
What is it written in?
Mainly C#, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Four entry points, and what each one is for

DiffPlex is a C# library to generate textual diffs, and it exposes four things you can build on.

IDiffer, implemented by the Differ class, is the core diffing class. It exposes the low level functions to generate differences between texts.

ISidebySideDiffer, implemented by the SideBySideDiffer class, is a higher level interface. It consumes the IDiffer interface and generates a SideBySideDiffModel, which the documentation describes as a model suited for displaying the differences of two pieces of text in a side by side view. So the layering is clean: the low level produces a diff result, and the side by side layer reshapes it into rows and cells for a table or a two column display.

IThreeWayDiffer, implemented by the ThreeWayDiffer class, provides three-way diff and merge functionality, enabling intelligent merging by comparing a base text with two modified versions to detect changes and conflicts. That is the one most diff libraries do not have, and it is the one that turns a diff tool into a merge tool.

UnidiffRenderer is a renderer class that generates unified diff, or unidiff, format output compatible with Git, patch utilities, and other standard diff tools. It is a renderer rather than a differ, so it works on the output of a differ and turns it into the patch format the rest of your tooling understands.

That division is worth internalising, because it decides where you plug in. If you want to draw a diff, you use the side by side layer. If you want to emit a patch, you use the renderer. If you want to merge two branches of a document, you use the three way differ. If you want to compute the difference yourself in a custom way, you use the core IDiffer.

The library targets netstandard1.0+ according to the README body, while the package description says Netstandard 2.0+ C# library, so the target has moved over the life of the project. The licence is Apache-2.0, the primary language is C#, and the default branch is master.

The chunker is the extension point, and that is the interesting design

The most unusual thing about DiffPlex, and the part most worth understanding before you use it, is that the unit of comparison is a pluggable strategy rather than a set of flags.

The core interface has four named methods on IDiffer, and they differ only in how the text is broken up. CreateLineDiffs compares text line by line. CreateCharacterDiffs compares character by character. CreateWordDiffs compares word by word and takes a char[] of separators defining what counts as a word boundary. And CreateCustomDiffs compares text in chunks determined by a supplied chunker function, taking a Func<string, string[]> that will break the text into chunks.

The fifth method, CreateDiffs, is the general one, and it takes the whitespace flag, a case sensitivity flag, and an IChunker. The interface itself is one method:

csharp
public interface IChunker
{
    /// <summary>
    /// Dive text into sub-parts
    /// </summary>
    string[] Chunk(string text);
}

Six implementations are provided: CharacterChunker, CustomFunctionChunker, DelimiterChunker, LineChunker, LineEndingsPreservingChunker and WordChunker.

The names explain the design space. LineChunker is the obvious one. CharacterChunker goes finer. WordChunker goes at word granularity, which is what you want for a prose diff where a changed word should be highlighted rather than a whole paragraph. DelimiterChunker takes a delimiter, which is the way to diff CSV or log files where the field separator is meaningful. CustomFunctionChunker is the escape hatch for anything else, and it is the one that makes the library extensible without subclassing DiffPlex itself.

LineEndingsPreservingChunker is the one that shows the care in the library. The problem it solves is real: if you split text on lines and the two versions differ in whether they use CRLF or LF, a naive chunker will report every line as changed, because the line terminator is part of the chunk. A chunker that keeps the line endings as part of the text and preserves them in the output avoids a diff full of noise. That is the kind of detail that only shows up after someone has been burned by it.

So the practical upshot is that the diff granularity in DiffPlex is a first class decision, and for most structured data you can write a chunker in a few lines rather than looking for a mode that already does what you want.

The diff result, and rendering it in a console

The result object is a DiffResult, and the sample code in the README shows how the pieces fit together in practice, which is a better guide to the API than the interface listing.

The sample builds a diff with InlineDiffBuilder.Diff(before, after) and then walks the resulting lines, switching on the line type to colour the console output. Inserted lines are green with a plus prefix, deleted lines are red with a minus, and unchanged lines are gray with two spaces. The gray is annotated in the source as a compromise for dark or light background, which is a small detail that tells you the library has been used in a real terminal.

The switch on ChangeType with Inserted, Deleted and a default is the shape of the whole API. Everything downstream of the differ is a stream of lines each carrying a type and the text, and whatever you build on top is a consumer of that stream. The console renderer, the side by side model, the unified diff renderer and the three way merge are all built on the same underlying representation, which is why they behave consistently.

The C# is worth reading for the language rather than the algorithm:

csharp
var diff = InlineDiffBuilder.Diff(before, after);

var savedColor = Console.ForegroundColor;
foreach (var line in diff.Lines)
{
    switch (line.Type)
    {
        case ChangeType.Inserted:
            Console.ForegroundColor = ConsoleColor.Green;
            Console.Write("+ ");
            break;
        case ChangeType.Deleted:
            Console.ForegroundColor = ConsoleColor.Red;
            Console.Write("- ");
            break;
        default:
            Console.ForegroundColor = ConsoleColor.Gray; // compromise for dark or light background
            Console.Write("  ");
            break;
    }

    Console.WriteLine(line.Text);
}
Console.ForegroundColor = savedColor;

Two things to note. The saving and restoring of the foreground colour is the kind of thing a console application has to do and a library consumer of the diff result might not think about, and it is a good model for how to treat Console.ForegroundColor in your own rendering. And the diff result exposes a Lines collection directly, so you are not forced to use any of the provided renderers at all.

The README also points at the example projects in the solution rather than only at the code above. For the IDiffer interface, that is SidebySideDiffer.cs in the DiffPlex project and UnidiffFormater.cs in the DiffPlex.ConsoleRunner project. For the side by side interface, it is DiffController.cs and its associated MVC views in the WebDiffer project, and TextBoxDiffRenderer.cs in the SilverlightDiffer project. For the unified diff renderer, it is Program.cs in the DiffPlex.ConsoleRunner project.

The SilverlightDiffer reference is a historical artefact worth noting, since it tells you the library predates the modern .NET wave and that the sample solutions carry old platform projects alongside new ones. The repository listing confirms the spread: DiffPlex.Blazor, DiffPlex.Windows, DiffPlex.WindowsForms, DiffPlex.Wpf and their Demo directories, plus DiffPlex.App, Facts.DiffPlex for the test facts project, and Perf.DiffPlex for benchmarks.

Three way merging, and the solution full of platform samples

ThreeWayDiffer is the interface that separates DiffPlex from a display library, and it deserves its own look because three way merging is where diff implementations usually get subtle.

The interface provides three-way diff and merge functionality, enabling intelligent merging by comparing a base text with two modified versions to detect changes and conflicts. So the inputs are three documents rather than two. The base is what both sides started from, and the two modified versions are the two things you want to combine. What comes out is a merge, and the conflicts are the places where both sides changed the same region in ways that cannot be reconciled automatically.

That model is the one a version control system uses, and it is the model you want if you are merging two people's edits to a configuration file or a document. The advantage over a two way diff is that the base tells the differ which changes are genuinely competing. A two way diff of the two modified versions would show you every region that differs, including regions where only one side changed and the other left it alone. With the base, those are recognised as non-conflicting and merged silently, and only the genuinely overlapping changes are flagged.

The caveat is the one that applies to every three way merge implementation, and it is worth being explicit about before you rely on it. The conflict detection semantics are a design decision, and different implementations make different choices about what counts as a conflict, how close two changes have to be to interfere, and whether whitespace only differences count. The documentation does not spell out those rules, so if you are using the merge output in a workflow where a missed conflict is costly, test it against the specific cases your users will hit rather than assuming the behaviour matches the tool you are used to.

The solution file gives you a map of what has been built on top of the library, and it is broader than you would expect from a diff library. Alongside the core DiffPlex project there is DiffPlex.ConsoleRunner, which is the command line tool with both the unified diff example and the side by side example, and WebDiffer, which is an MVC application with a DiffController and views demonstrating the side by side model in a web UI. Then there is the full set of Windows presentation stacks: DiffPlex.Windows, DiffPlex.WindowsForms, DiffPlex.Wpf, and a Demo directory for each of WindowsForms and Wpf, plus DiffPlex.App. DiffPlex.Blazor covers the modern web framework, and the SilverlightDiffer project is the historical one mentioned earlier.

Two supporting projects sit alongside: Facts.DiffPlex, the test project, and Perf.DiffPlex, the benchmark project. A benchmark project in a diff library is a good sign, because diff performance is a real concern on large documents and the algorithm choice interacts with the granularity you choose.

Producing a real patch, and the repository's own test fixtures

The unified diff renderer is the piece that makes DiffPlex useful in a build pipeline rather than only in a UI, and the README gives both a static and an instance form:

csharp
// Static method for simple usage
string unidiff = UnidiffRenderer.GenerateUnidiff(
    oldText: "old content", 
    newText: "new content",
    oldFileName: "file1.txt",
    newFileName: "file2.txt"
);

// Instance usage with custom settings
var renderer = new UnidiffRenderer(contextLines: 5);
string unidiff = renderer.Generate(oldText, newText, "before.txt", "after.txt");

The static method is the convenience path and the instance path is where the settings live. The contextLines parameter is the number of context lines around changes, which is the one knob that matters most in practice, because a wider setting makes a one line change much easier to read in a review. The key features are listed as generating standard unified diff format compatible with Git and patch tools, a configurable number of context lines around changes, support for custom file names in diff headers, and options to ignore whitespace and case differences.

The example output shows the format:

code
--- before.txt
+++ after.txt
@@ -1,4 +1,4 @@

Note that the file names in the header are the ones you pass in, and that they are not prefixed with a timestamp, which is the git style. And note the hunk header, which is the part that has to be exactly right for patch to apply it, which is a good thing to check against the real tool rather than assume.

The repository has a script for verifying this, unidiffTest.sh at the top level, which is the kind of fixture you want to see in a diff library. A unified diff that looks right but does not apply is a failure that only shows up when someone tries to use it, so a test that runs the output through a patch tool is worth more than unit tests on the string.

The rest of the repository shows a project with real engineering discipline around it. There is a Directory.Build.props and a Directory.Build.targets for shared MSBuild configuration, a global.json pinning the SDK, a NuGet.props and a NuGet.config, a package.ps1 for the packaging step, an opensource.snk which is a strong name key, an appveyor.yml and an azure-pipelines.yml for the two CI systems, a.idea/ and.vscode/ directory, and a.github/ directory with the workflow the build badge points at.

Editorial conclusion

DiffPlex fits a.NET team that needs to show textual differences in a UI, produce a patch, or merge three versions, and that wants the diff granularity to be a policy decision rather than a hardcoded mode. It is the wrong choice if you only take the published package, since the newest NuGet package is 1.7.0 from June 2021 while the source has moved on since then, and it is the wrong choice if you need diffs of structured data such as JSON with semantic awareness, because the chunker works on text. Before adopting, decide whether you will take the NuGet package or build from source, check the current target framework in the package metadata against your project, and read the three way merge behaviour carefully, because conflict detection semantics differ between implementations.

Frequently asked questions

What is DiffPlex and what can it generate?

It is a .NET Standard C# library to generate textual diffs. It exposes IDiffer for the low level diff functions, ISidebySideDiffer for a side by side display model, IThreeWayDiffer for three way diff and merge, and the UnidiffRenderer class for unified diff output compatible with Git and patch utilities.

How do I control the granularity of a DiffPlex diff?

Through the IChunker interface, which is a single method that breaks text into sub-parts. Six implementations are provided, CharacterChunker, CustomFunctionChunker, DelimiterChunker, LineChunker, LineEndingsPreservingChunker and WordChunker, and the CreateCustomDiffs method takes a chunker function directly if you need your own.

Does DiffPlex produce patches I can apply with git?

Yes. The UnidiffRenderer generates standard unified diff format compatible with Git and patch tools, with a configurable number of context lines around changes, custom file names in the diff headers, and options to ignore whitespace and case differences. The repository includes a unidiffTest.sh script for verifying the output.

How current is the published DiffPlex package?

The newest NuGet release is 1.7.0 from 2021-06-06, and the earlier listed tags are 1.4.4 from 2019. The repository itself is not archived, with the last push on 2026-09-10, so the source has moved on since the last published package and you should check whether to take the package or build from source.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. mmanela/diffplex on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/mmanela-diffplex.svg)](https://hysenlabs.com/projects/mmanela-diffplex)

Was this useful?