
# Rozdíl

Pitmaster počítá diffy v jednotném formátu git. Nástroj pro výpočet diffů podporuje více algoritmů, formátování jednotného diffu, detekci binárních souborů a rekurzivní porovnávání na úrovni stromu.

U upraveného textového souboru s režimem `100644` vytváří `format()` stejné bajty jako `git diff`. Ostatní případy jsou nyní formátovány odlišně ([#3](https://github.com/inline0/pitmaster/issues/3)):

- Nové a smazané soubory nemají řádek `new file mode` / `deleted file mode`, řádek `index` ani `/dev/null`: obě strany jsou označeny jako `a/<path>` a `b/<path>` a rozsah bloku změn prázdné strany se zapisuje jako `-1,0` nebo `+1,0`, kde git zapisuje `-0,0` nebo `+0,0`.
- Řádek `index` vždy končí znakem `100644`, bez ohledu na režim souboru.
- Změny pouze režimu (například `chmod +x`) nevytvářejí žádný výsledek.
- Binární soubory vypisují `Binary files differ` bez řádku `index`, zatímco git vypisuje `Binary files a/<path> and b/<path> differ`.
- Přejmenování se zobrazují jako jedna položka `diff --git a/<old> b/<new>` s diffem obsahu, bez řádků `similarity index`, `rename from` ani `rename to`.

## Operace s rozdíly

### Nepřipravené změny (pracovní strom oproti indexu)

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

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

Porovnává každý soubor v indexu s jeho aktuálním stavem v pracovním stromu. Zjišťuje úpravy, odstranění a binární soubory.

### Připravené změny (index oproti HEAD)

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

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

Porovnává aktuální index se stromem commitu HEAD. Zobrazuje, co bude zahrnuto do příštího commitu. Odstraněné a přidané soubory s podobným obsahem se zobrazí jako jedna přejmenovaná položka. Před prvním commitem HEAD neexistuje a výsledek je prázdný ([#5](https://github.com/inline0/pitmaster/issues/5)).

Párování přejmenovaných souborů v současnosti odpovídá také nesouvisejícím jednořádkovým souborům, protože koncový prázdný řádek se započítává jako společný obsah ([#4](https://github.com/inline0/pitmaster/issues/4)).

### Rozdíl mezi stromy

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

Rekurzivně porovnává dva stromy. Předávejte ID stromů: ID commitů se na jejich stromy nepřevádějí a vrátí prázdný výsledek. Stejný modul `TreeDiff` využívají `diffStaged()`, `show()` a `logPath()`.

### Filtrování podle cesty

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

Specifikace cesty je přesná cesta k jednomu souboru. `diffStaged()` přijímá stejný argument, ale momentálně ho ignoruje, takže vždy vrací všechny připravené soubory ([#5](https://github.com/inline0/pitmaster/issues/5)).

### Výběr algoritmu

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

Platné názvy jsou `myers` (výchozí), `patience`, `histogram` a `minimal`. Jakýkoli jiný název vyvolá `InvalidArgumentException`.

## Struktura DiffResult

Každý `DiffResult` představuje rozdíl pro jeden soubor.

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

### Formátovaný výstup

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

Vytváří výstup jednotného rozdílu. U upraveného textového souboru odpovídá `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()
     {
```

### Kontrola změn

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

## Struktura hunku

Každý `Hunk` představuje souvislou oblast změn s kontextovými řádky.

```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 @@"
```

## Algoritmy rozdílů

Pitmaster implementuje čtyři algoritmy pro výpočet rozdílů. Výchozí je Myersův algoritmus.

### Myersův algoritmus

Standardní algoritmus O(ND), stejný jako výchozí algoritmus git. Vytváří minimální skripty úprav.

```php
use Pitmaster\Diff\MyersDiff;

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

Metoda `diff()` přijímá dva řetězce a vrací pole objektů `Hunk`. Rozdělí vstup podle nových řádků, vypočítá nejkratší posloupnost úprav a seskupí změny do bloků se 3 řádky kontextu (podle výchozího nastavení git).

### Diff metodou trpělivosti

Lepší strukturální diffy kódu. Nejprve porovná jedinečné řádky a pak doplní obsah mezi nimi.

```php
use Pitmaster\Diff\PatienceDiff;

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

### Histogramový diff

Varianta diffu metodou trpělivosti, která lépe zvládá opakující se řádky. Používají ji JGit a Eclipse.

```php
use Pitmaster\Diff\HistogramDiff;

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

### Minimální diff

Vytvoří co nejmenší diff (s nejmenším počtem změněných řádků), za cenu delšího výpočtu.

```php
use Pitmaster\Diff\MinimalDiff;

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

## Detekce binárních souborů

Pitmaster považuje obsah za binární, pokud se v jeho prvních 8192 bajtech nachází bajt NUL (git kontroluje prvních 8000).

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

Binární soubory vytvoří `DiffResult` s `$binary = true` a prázdným polem `$hunks`. Formátovaný výstup:

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

## Barevný výstup

CLI pro výstup do terminálu používá `ColorDiff` a ANSI barvy.

```php
use Pitmaster\Diff\ColorDiff;

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

- Přidané řádky (`+`) se zobrazují zeleně
- Odebrané řádky (`-`) se zobrazují červeně
- Záhlaví bloků (`@@`) se zobrazují azurově
- Záhlaví souborů (`diff --git`, `index`, `---`, `+++`) se zobrazují tučně

`pitmaster diff` vždy obarví výstup, i když je přesměrován. `pitmaster show` obarví výstup pouze tehdy, když je stdout terminál.

## Diff stromu (interní)

Třída `TreeDiff` rekurzivně porovnává dva objekty stromu a pro každý změněný soubor vytváří `DiffResult`.

```php
use Pitmaster\Diff\TreeDiff;

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

Diff stromu zpracovává:
- Přidané soubory (jsou pouze v novém stromu)
- Smazané soubory (jsou pouze ve starém stromu)
- Upravené soubory (stejná cesta, jiný hash blobu)
- Rekurzivní porovnání podadresářů
- Párování přejmenovaných souborů mezi smazanými a přidanými (shoda celého hashe nebo alespoň 50 % společných řádků)

Položky se stejným hashem se přeskočí, takže samotná změna režimu nevytvoří žádný výsledek.

## Diff slov

Pro změny jednotlivých slov uvnitř řádků.

```php
use Pitmaster\Diff\WordDiff;

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