← All 41 libraries

SugarDiff

SugarDiff

Unified-diff engine — hunks, writer, and patch scanner

component-package diff patch

SugarDiff code coverage

A unified-diff engine the whole ecosystem can share: LCS line diffing, numbered context hunks, a GNU diff -u writer, and a scanner that numbers the rows of any patch. Extracted from sugar-crush's BuildsUnifiedDiff trait and the DiffGutter region model, so every surface that produces or displays patches reads the same line numbers out of the same engine. First-party — there is no 1:1 upstream Go library.

Install

composer require sugarcraft/sugar-diff:@dev

Quickstart

use SugarCraft\Diff\Diff;
use SugarCraft\Diff\DiffOptions;
use SugarCraft\Diff\Reader\UnifiedScan;

$diff = Diff::compute("Hello World\n", "Hello PHP\n");

echo $diff->unified('file.txt');
// --- a/file.txt
// +++ b/file.txt
// @@ -1,1 +1,1 @@
// -Hello World
// +Hello PHP

// Immutable, fluent options — defaults reproduce sugar-crush byte-for-byte.
$wide = Diff::compute($before, $after, DiffOptions::new()
    ->withContextLines(10)
    ->withIgnoreWhitespace()
    ->withNoNewlineMarker()
    ->withDevNullOnEmptySide());

// Number the rows of an existing patch (gutter/viewer support):
foreach (UnifiedScan::lines($patchText) as $row) {
    // $row->old, $row->new, $row->fileHeader
}

The model

Diffcompute() entry point; lines, hunks, per-side counts and final-newline facts; isEmpty/addedLines/removedLines/unified/hunkText.
Hunk / Line / LineKindOne @@ block; one numbered row; eq/del/ins. All immutable value objects.
Writer\UnifiedFormatRenders the GNU diff -u shape: headers, @@ ranges, opt-in "\ No newline" markers and /dev/null labels.
Reader\UnifiedScanWalks any rendered unified diff and returns which old/new line each row points at — the gutter model without the gutter.

Use it for

Source

Try the quickstart →

API

ClassMethodDescription
Diffcompute(string|list<string> $before, string|list<string> $after, ?DiffOptions $options = null): selfLine-mode LCS diff (byte-for-byte faithful to sugar-crush defaults)
Diffunified(string $path, ?string $newPath = null): stringFull diff -u text with --- / +++ headers
DiffhunkText(): stringHunks only, no file headers
DiffisEmpty() / addedLines() / removedLines(): int|boolSummary counts
DiffOptionswithContextLines(int) / withMaxLcsCells(int)Context window (default 3) and LCS cell budget (default 250 000)
DiffOptionswithIgnoreWhitespace() / withIgnoreCase()Fold comparisons; emitted rows keep original bytes
DiffOptionswithNoNewlineMarker() / withDevNullOnEmptySide()GNU annotations, opt-in
UnifiedFormatwrite(Diff, string, ?string) / hunksOnly(Diff): stringThe writer behind Diff::unified()/hunkText()
UnifiedScanlines(string|list<string>): list<ScannedLine>Per-row old/new line numbers + fileHeader flag
UnifiedScanfileHeaders(string|list<string>): list<bool>Which rows start a new file