
# Diff

Pitmaster berekent verschillen in het uniforme formaat van git. De diff-engine ondersteunt meerdere algoritmen, uniforme diff-opmaak, detectie van binaire bestanden en recursieve vergelijking op boomniveau.

Voor een gewijzigd tekstbestand met modus `100644` produceert `format()` dezelfde bytes als `git diff`. Andere gevallen worden momenteel anders opgemaakt ([#3](https://github.com/inline0/pitmaster/issues/3)):

- Nieuwe en verwijderde bestanden hebben geen regel `new file mode` / `deleted file mode`, geen regel `index` en geen `/dev/null`: beide zijden krijgen de aanduiding `a/<path>` en `b/<path>`, en het hunk-bereik van de lege zijde wordt geschreven als `-1,0` of `+1,0`, terwijl git `-0,0` of `+0,0` schrijft.
- De regel `index` eindigt altijd op `100644`, ongeacht de bestandsmodus.
- Wijzigingen in alleen de modus (bijvoorbeeld `chmod +x`) leveren geen resultaat op.
- Binaire bestanden tonen `Binary files differ` zonder een regel `index`, terwijl git `Binary files a/<path> and b/<path> differ` toont.
- Hernoemingen worden weergegeven als één `diff --git a/<old> b/<new>`-item met een inhoudsverschil, zonder regels `similarity index`, `rename from` of `rename to`.

## Diff-bewerkingen

### Niet-gefaseerde wijzigingen (werkboom versus index)

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

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

Vergelijkt elk bestand in de index met de huidige staat ervan in de werkboom. Detecteert wijzigingen, verwijderingen en binaire bestanden.

### Gefaseerde wijzigingen (index versus HEAD)

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

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

Vergelijkt de huidige index met de tree van de HEAD-commit. Toont wat in de volgende commit wordt opgenomen. Verwijderde en toegevoegde bestanden met vergelijkbare inhoud worden als één hernoemd item gerapporteerd. Vóór de eerste commit bestaat er geen HEAD en is het resultaat leeg ([#5](https://github.com/inline0/pitmaster/issues/5)).

Koppeling van hernoemde bestanden matcht momenteel ook niet-gerelateerde bestanden van één regel, omdat de afsluitende lege regel als gedeelde inhoud telt ([#4](https://github.com/inline0/pitmaster/issues/4)).

### Diff tussen trees

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

Vergelijkt twee trees recursief. Geef tree-ID's door: commit-ID's worden niet herleid tot hun trees en leveren een leeg resultaat op. Dezelfde engine `TreeDiff` ligt ten grondslag aan `diffStaged()`, `show()` en `logPath()`.

### Filteren op pad

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

De pathspec is één exact bestandspad. `diffStaged()` accepteert hetzelfde argument, maar negeert het momenteel en retourneert daarom altijd elk gefaseerd bestand ([#5](https://github.com/inline0/pitmaster/issues/5)).

### Een algoritme kiezen

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

Geldige namen zijn `myers` (standaard), `patience`, `histogram` en `minimal`. Elke andere naam gooit `InvalidArgumentException`.

## Structuur van DiffResult

Elk `DiffResult` vertegenwoordigt de diff voor één bestand.

```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'
        }
    }
}
```

### Opgemaakte uitvoer

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

Produceert unified diff-uitvoer. Voor een gewijzigd tekstbestand komt deze overeen met `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()
     {
```

### Controleren op wijzigingen

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

## Hunk-structuur

Elk `Hunk` vertegenwoordigt een aaneengesloten gebied met wijzigingen en contextregels.

```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-algoritmen

Pitmaster implementeert vier diff-algoritmen. Myers is de standaard.

### Myers-diff

Het standaardalgoritme O(ND), hetzelfde als de standaardinstelling van git. Levert minimale bewerkingsscripts op.

```php
use Pitmaster\Diff\MyersDiff;

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

De methode `diff()` neemt twee tekenreeksen en retourneert een array met `Hunk`-objecten. De invoer wordt opgesplitst bij regeleinden, het kortste bewerkingsscript wordt berekend en wijzigingen worden gegroepeerd in hunks met 3 contextregels (zoals de standaardinstelling van git).

### Patience-diff

Zorgt voor betere structurele diffs van code. Matcht eerst unieke regels en vult daarna de tussenliggende stukken op.

```php
use Pitmaster\Diff\PatienceDiff;

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

### Histogram-diff

Een variant van de patience-diff die beter omgaat met herhaalde regels. Wordt gebruikt door JGit en Eclipse.

```php
use Pitmaster\Diff\HistogramDiff;

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

### Minimale diff

Produceert de kleinst mogelijke diff (met zo min mogelijk gewijzigde regels), ten koste van meer rekenwerk.

```php
use Pitmaster\Diff\MinimalDiff;

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

## Binaire detectie

Pitmaster behandelt inhoud als binair wanneer deze een NUL-byte bevat in de eerste 8192 bytes (git controleert de eerste 8000).

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

Binaire bestanden produceren een `DiffResult` met `$binary = true` en een lege array `$hunks`. De opgemaakte uitvoer is:

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

## Uitvoer met kleuren

De CLI gebruikt `ColorDiff` voor terminaluitvoer met ANSI-kleuren.

```php
use Pitmaster\Diff\ColorDiff;

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

- Toegevoegde regels (`+`) worden groen weergegeven
- Verwijderde regels (`-`) worden rood weergegeven
- Hunk-headers (`@@`) worden cyaan weergegeven
- Bestandsheaders (`diff --git`, `index`, `---`, `+++`) worden vet weergegeven

`pitmaster diff` kleurt altijd, ook wanneer de uitvoer wordt doorgevoerd. `pitmaster show` kleurt alleen wanneer stdout een terminal is.

## Tree-diff (intern)

De klasse `TreeDiff` vergelijkt twee tree-objecten recursief en produceert een `DiffResult` voor elk gewijzigd bestand.

```php
use Pitmaster\Diff\TreeDiff;

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

Tree-diff behandelt:
- Toegevoegde bestanden (alleen aanwezig in de nieuwe tree)
- Verwijderde bestanden (alleen aanwezig in de oude tree)
- Gewijzigde bestanden (zelfde pad, andere blob-hash)
- Recursieve vergelijking van submappen
- Koppeling van hernoemingen tussen verwijderde en toegevoegde bestanden (exacte hash-match of minstens 50% gedeelde regels)

Items met een ongewijzigde hash worden overgeslagen, dus een wijziging van alleen de modus levert geen resultaat op.

## Word-diff

Voor wijzigingen op woordniveau binnen regels.

```php
use Pitmaster\Diff\WordDiff;

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