
# Diff

Pitmaster beregner forskelle i gits samlede format. Diff-motoren understøtter flere algoritmer, formatering af samlede forskelle, registrering af binære filer og rekursiv sammenligning på træniveau.

For en ændret tekstfil med tilstanden `100644` producerer `format()` de samme bytes som `git diff`. Andre tilfælde formateres i øjeblikket anderledes ([#3](https://github.com/inline0/pitmaster/issues/3)):

- Nye og slettede filer har ingen `new file mode` / `deleted file mode`-linje, ingen `index`-linje og intet `/dev/null`: begge sider mærkes `a/<path>` og `b/<path>`, og den tomme sides hunk-interval skrives som `-1,0` eller `+1,0`, hvor git skriver `-0,0` eller `+0,0`.
- `index`-linjen slutter altid med `100644`, uanset filtilstanden.
- Ændringer kun af tilstanden (f.eks. `chmod +x`) giver intet resultat.
- Binære filer viser `Binary files differ` uden en `index`-linje, hvor git viser `Binary files a/<path> and b/<path> differ`.
- Omdøbninger vises som én `diff --git a/<old> b/<new>`-post med en indholdsdiff uden linjerne `similarity index`, `rename from` eller `rename to`.

## Diff-handlinger

### Ikke-indekserede ændringer (arbejdsmappe vs. indeks)

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

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

Sammenligner hver fil i indekset med dens aktuelle tilstand i arbejdsområdet. Registrerer ændringer, sletninger og binære filer.

### Indekserede ændringer (indeks vs. HEAD)

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

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

Sammenligner det aktuelle indeks med træet i HEAD-committen. Viser, hvad der kommer med i den næste commit. Slettede og tilføjede filer med lignende indhold rapporteres som én post for en omdøbt fil. Før den første commit findes der ingen HEAD, og resultatet er tomt ([#5](https://github.com/inline0/pitmaster/issues/5)).

Parringen af omdøbninger matcher i øjeblikket også ikke-relaterede filer på én linje, fordi den afsluttende tomme linje tæller som fælles indhold ([#4](https://github.com/inline0/pitmaster/issues/4)).

### Træ-til-træ-diff

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

Sammenligner to træer rekursivt. Angiv træ-id'er: commit-id'er foldes ikke ud til deres træer og giver et tomt resultat. Den samme `TreeDiff`-motor ligger til grund for `diffStaged()`, `show()` og `logPath()`.

### Filtrér efter sti

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

Stispecifikationen er én præcis filsti. `diffStaged()` accepterer det samme argument, men ignorerer det i øjeblikket, så den altid returnerer alle indekserede filer ([#5](https://github.com/inline0/pitmaster/issues/5)).

### Vælg en algoritme

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

Gyldige navne er `myers` (standard), `patience`, `histogram` og `minimal`. Ethvert andet navn kaster `InvalidArgumentException`.

## DiffResult-struktur

Hver `DiffResult` repræsenterer forskellen for én enkelt 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'
        }
    }
}
```

### Formateret output

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

Producerer output med forenede forskelle. For en ændret tekstfil svarer det til `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()
     {
```

### Kontrollér for ændringer

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

## Hunk-struktur

Hver `Hunk` repræsenterer et sammenhængende ændringsområde med kontekstlinjer.

```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 implementerer fire diff-algoritmer. Standardalgoritmen er Myers.

### Myers-diff

Standardalgoritmen O(ND), den samme som gits standardalgoritme. Giver minimale redigeringsscripts.

```php
use Pitmaster\Diff\MyersDiff;

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

Metoden `diff()` tager to strenge og returnerer et array af `Hunk`-objekter. Den opdeler inputtet ved linjeskift, beregner det korteste redigeringsscript og grupperer ændringer i hunks med 3 kontekstlinjer (som git gør som standard).

### Tålmodig diff

Bedre strukturelle diff'er for kode. Matcher først unikke linjer og udfylder derefter mellemrummene.

```php
use Pitmaster\Diff\PatienceDiff;

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

### Histogram-diff

En variant af tålmodig diff, der håndterer gentagne linjer bedre. Bruges af JGit og Eclipse.

```php
use Pitmaster\Diff\HistogramDiff;

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

### Minimal diff

Producerer den mindst mulige diff (færrest ændrede linjer) på bekostning af mere beregning.

```php
use Pitmaster\Diff\MinimalDiff;

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

## Registrering af binærfiler

Pitmaster behandler indhold som binært, hvis det indeholder en NUL-byte blandt de første 8192 bytes (git kontrollerer de første 8000).

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

Binære filer giver en `DiffResult` med `$binary = true` og et tomt `$hunks`-array. Det formaterede output er:

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

## Farvelagt output

CLI bruger `ColorDiff` til terminaloutput med ANSI-farver.

```php
use Pitmaster\Diff\ColorDiff;

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

- Tilføjede linjer (`+`) vises med grønt
- Fjernede linjer (`-`) vises med rødt
- Hunk-headere (`@@`) vises med cyan
- Filheadere (`diff --git`, `index`, `---`, `+++`) vises med fed skrift

`pitmaster diff` farvelægger altid output, også når det sendes gennem en pipe. `pitmaster show` farvelægger kun, når stdout er en terminal.

## Tree-diff (intern)

Klassen `TreeDiff` sammenligner to tree-objekter rekursivt og producerer en `DiffResult` for hver ændret fil.

```php
use Pitmaster\Diff\TreeDiff;

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

Tree-diff håndterer:
- Tilføjede filer (findes kun i det nye tree)
- Slettede filer (findes kun i det gamle tree)
- Ændrede filer (samme sti, forskellig blob-hash)
- Rekursiv sammenligning af undermapper
- Parring af omdøbninger mellem slettede og tilføjede filer (præcist hash-match eller mindst 50 % fælles linjer)

Poster med uændret hash springes over, så en ændring af tilstanden alene ikke giver et resultat.

## Orddiff

Til ændringer på ordniveau inde i linjer.

```php
use Pitmaster\Diff\WordDiff;

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