
# Diff

Pitmaster beräknar diffar i gits enhetliga format. Diffmotorn stöder flera algoritmer, formatering av enhetliga diffar, identifiering av binärfiler och rekursiv jämförelse på trädnivå.

För en ändrad textfil med läget `100644` producerar `format()` samma byte som `git diff`. Andra fall formateras annorlunda i dag ([#3](https://github.com/inline0/pitmaster/issues/3)):

- Nya och borttagna filer saknar rad för `new file mode` / `deleted file mode`, rad för `index` och `/dev/null`: båda sidorna etiketteras `a/<path>` och `b/<path>`, och den tomma sidans hunk-intervall skrivs som `-1,0` eller `+1,0` där git skriver `-0,0` eller `+0,0`.
- Raden `index` slutar alltid med `100644`, oavsett filläge.
- Ändringar av enbart läge (till exempel `chmod +x`) ger inget resultat.
- Binärfiler skriver ut `Binary files differ` utan en rad för `index`, medan git skriver ut `Binary files a/<path> and b/<path> differ`.
- Namnbyten visas som en enda `diff --git a/<old> b/<new>`-post med en innehållsdiff, utan rader för `similarity index`, `rename from` eller `rename to`.

## Diffåtgärder

### Ej mellanlagrade ändringar (arbetsträd jämfört med index)

```php
$diffs = $repo->diff();

foreach ($diffs as $diff) {
    echo $diff->format();
}
```

Jämför varje fil i indexet med dess aktuella tillstånd i arbetsträdet. Upptäcker ändringar, borttagningar och binärfiler.

### Mellanlagrade ändringar (index jämfört med HEAD)

```php
$diffs = $repo->diffStaged();

foreach ($diffs as $diff) {
    echo $diff->format();
}
```

Jämför det aktuella indexet med HEAD-incheckningens träd. Visar vad som kommer att ingå i nästa incheckning. Borttagna och tillagda filer med liknande innehåll rapporteras som en post med nytt namn. Före den första incheckningen finns inget HEAD och resultatet är tomt ([#5](https://github.com/inline0/pitmaster/issues/5)).

Parning av namnbyten matchar för närvarande även orelaterade filer med en enda rad, eftersom den avslutande tomma raden räknas som delat innehåll ([#4](https://github.com/inline0/pitmaster/issues/4)).

### Träd-till-träd-diff

```php
$a = $repo->readObject($repo->resolve('HEAD~1')->hex)->tree;
$b = $repo->head()->tree;
$diffs = $repo->diffTree($a, $b);
```

Jämför två träd rekursivt. Skicka med träd-ID:n: inchecknings-ID:n omvandlas inte till sina träd och ger ett tomt resultat. Samma `TreeDiff`-motor används av `diffStaged()`, `show()` och `logPath()`.

### Filtrera efter sökväg

```php
$diffs = $repo->diff('src/specific-file.php');
```

Sökvägsspecifikationen är en exakt filsökväg. `diffStaged()` accepterar samma argument men ignorerar det för tillfället, så den returnerar alltid alla mellanlagrade filer ([#5](https://github.com/inline0/pitmaster/issues/5)).

### Välj en algoritm

```php
$diffs = $repo->diff(null, 'patience');
$diffs = $repo->diffStaged(null, 'histogram');
$diffs = $repo->diffTree($a, $b, 'minimal');
```

Giltiga namn är `myers` (standard), `patience`, `histogram` och `minimal`. Alla andra namn orsakar `InvalidArgumentException`.

## DiffResult-struktur

Varje `DiffResult` representerar diffen för en enskild fil.

```php
use Pitmaster\Diff\DiffResult;

foreach ($diffs as $diff) {
    echo $diff->oldPath;    // Path in the old tree
    echo $diff->newPath;    // Path in the new tree
    echo $diff->binary;     // true for binary files
    echo $diff->oldHash;    // Hex hash of the old blob (null for added files)
    echo $diff->newHash;    // Hex hash of the new blob (null for deleted files)

    foreach ($diff->hunks as $hunk) {
        echo $hunk->header();  // @@ -1,5 +1,7 @@
        foreach ($hunk->lines as $line) {
            echo $line . "\n"; // ' context', '+added', '-removed'
        }
    }
}
```

### Formaterad utdata

```php
echo $diff->format();
```

Producerar utdata med enhetlig diff. För en ändrad textfil matchar den `git diff`:

```
diff --git a/src/File.php b/src/File.php
index abc1234..def5678 100644
--- a/src/File.php
+++ b/src/File.php
@@ -10,6 +10,8 @@ class File
     private string $name;

+    private int $count;
+
     public function __construct()
     {
```

### Kontrollera om det finns ändringar

```php
if ($diff->hasChanges()) {
    // File was modified (or is binary with changes)
}
```

## Hunk-struktur

Varje `Hunk` representerar ett sammanhängande ändringsområde med kontextrader.

```php
use Pitmaster\Diff\Hunk;

$hunk->oldStart;   // Starting line in old file
$hunk->oldCount;   // Number of lines from old file
$hunk->newStart;   // Starting line in new file
$hunk->newCount;   // Number of lines in new file
$hunk->lines;      // Array of diff lines (prefixed with ' ', '+', or '-')
$hunk->header();   // "@@ -10,6 +10,8 @@"
```

## Diff-algoritmer

Pitmaster implementerar fyra diff-algoritmer. Standardalgoritmen är Myers.

### Myers-diff

Standardalgoritmen O(ND), samma som standardalgoritmen i git. Producerar minimala redigeringsskript.

```php
use Pitmaster\Diff\MyersDiff;

$hunks = MyersDiff::diff($oldContent, $newContent);
```

Metoden `diff()` tar två strängar och returnerar en array med `Hunk`-objekt. Den delar upp indata vid radbrytningar, beräknar det kortaste redigeringsskriptet och grupperar ändringarna i hunkar med 3 kontextrader (i enlighet med standardvärdet i git).

### Patience-diff

Bättre strukturella diffar för kod. Matchar först unika rader och fyller sedan i mellanrummen.

```php
use Pitmaster\Diff\PatienceDiff;

$hunks = PatienceDiff::diff($oldContent, $newContent);
```

### Histogram-diff

En variant av patience-diff som hanterar upprepade rader bättre. Används av JGit och Eclipse.

```php
use Pitmaster\Diff\HistogramDiff;

$hunks = HistogramDiff::diff($oldContent, $newContent);
```

### Minimal diff

Producerar minsta möjliga diff (minst antal ändrade rader), till priset av mer beräkning.

```php
use Pitmaster\Diff\MinimalDiff;

$hunks = MinimalDiff::diff($oldContent, $newContent);
```

## Binärdetektering

Pitmaster behandlar innehåll som binärt om det innehåller en NUL-byte bland de första 8192 byten (git kontrollerar de första 8000).

```php
if (MyersDiff::isBinary($content)) {
    // Binary file, skip line-level diff
}
```

Binärfiler ger en `DiffResult` med `$binary = true` och en tom `$hunks`-array. Den formaterade utdata är:

```
diff --git a/image.png b/image.png
Binary files differ
```

## Färgmarkerad utdata

CLI använder `ColorDiff` för terminalutdata med ANSI-färger.

```php
use Pitmaster\Diff\ColorDiff;

$colorized = ColorDiff::colorize($diff->format());
echo $colorized;
```

- Tillagda rader (`+`) visas i grönt
- Borttagna rader (`-`) visas i rött
- Hunk-rubriker (`@@`) visas i cyan
- Filrubriker (`diff --git`, `index`, `---`, `+++`) visas i fetstil

`pitmaster diff` färglägger alltid utdata, även när den skickas vidare. `pitmaster show` färglägger bara när stdout är en terminal.

## Träddiff (internt)

Klassen `TreeDiff` jämför två trädobjekt rekursivt och skapar en `DiffResult` för varje ändrad fil.

```php
use Pitmaster\Diff\TreeDiff;

$treeDiff = new TreeDiff($repo->objectDatabase());
$diffs = $treeDiff->diff($oldTreeId, $newTreeId);
```

Träddiff hanterar:
- Tillagda filer (finns bara i det nya trädet)
- Borttagna filer (finns bara i det gamla trädet)
- Ändrade filer (samma sökväg, annan blob-hash)
- Rekursiv jämförelse av underkataloger
- Parning av namnbyten mellan borttagna och tillagda filer (exakt hashmatchning eller minst 50 % gemensamma rader)

Poster med oförändrad hash hoppas över, så en ändring av enbart läge ger inget resultat.

## Orddiff

För ändringar på ordnivå inom rader.

```php
use Pitmaster\Diff\WordDiff;

$result = WordDiff::diff('the quick fox', 'the slow fox');
// 'the [-quick-]{+slow+} fox'
```
