
# Różnice

Pitmaster oblicza różnice w ujednoliconym formacie git. Silnik różnic obsługuje wiele algorytmów, formatowanie ujednoliconych różnic, wykrywanie plików binarnych i rekursywne porównywanie na poziomie drzew.

Dla zmodyfikowanego pliku tekstowego w trybie `100644`, `format()` generuje te same bajty co `git diff`. Obecnie pozostałe przypadki są formatowane inaczej ([#3](https://github.com/inline0/pitmaster/issues/3)):

- Nowe i usunięte pliki nie mają wiersza `new file mode` / `deleted file mode`, wiersza `index` ani `/dev/null`: obie strony są oznaczone jako `a/<path>` i `b/<path>`, a zakres fragmentu pustej strony jest zapisywany jako `-1,0` lub `+1,0` tam, gdzie git zapisuje `-0,0` lub `+0,0`.
- Wiersz `index` zawsze kończy się na `100644`, niezależnie od trybu pliku.
- Zmiany dotyczące wyłącznie trybu (na przykład `chmod +x`) nie dają wyniku.
- Pliki binarne wyświetlają `Binary files differ` bez wiersza `index`, podczas gdy git wyświetla `Binary files a/<path> and b/<path> differ`.
- Zmiany nazw są pokazywane jako jeden wpis `diff --git a/<old> b/<new>` z różnicą zawartości, bez wierszy `similarity index`, `rename from` ani `rename to`.

## Operacje na różnicach

### Niezatwierdzone zmiany (drzewo robocze a indeks)

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

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

Porównuje każdy plik w indeksie z jego bieżącym stanem w drzewie roboczym. Wykrywa modyfikacje, usunięcia i pliki binarne.

### Zmiany w indeksie (indeks a HEAD)

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

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

Porównuje bieżący indeks z drzewem commita HEAD. Pokazuje, co zostanie uwzględnione w następnym commicie. Usunięte i dodane pliki o podobnej zawartości są zgłaszane jako jeden wpis zmiany nazwy. Przed pierwszym commitem nie ma HEAD, więc wynik jest pusty ([#5](https://github.com/inline0/pitmaster/issues/5)).

Obecnie dopasowywanie par przy zmianie nazw obejmuje też niezwiązane, jednoliniowe pliki, ponieważ końcowy pusty wiersz jest liczony jako wspólna zawartość ([#4](https://github.com/inline0/pitmaster/issues/4)).

### Różnice między drzewami

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

Porównuje rekurencyjnie dwa drzewa. Przekaż identyfikatory drzew: identyfikatory commitów nie są rozwijane do drzew, więc wynik jest pusty. Ten sam silnik `TreeDiff` obsługuje `diffStaged()`, `show()` i `logPath()`.

### Filtrowanie według ścieżki

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

Specyfikacja ścieżki to dokładna ścieżka do pliku. `diffStaged()` przyjmuje ten sam argument, ale obecnie go ignoruje, więc zawsze zwraca wszystkie pliki w indeksie ([#5](https://github.com/inline0/pitmaster/issues/5)).

### Wybór algorytmu

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

Prawidłowe nazwy to `myers` (domyślna), `patience`, `histogram` i `minimal`. Każda inna nazwa powoduje wyjątek `InvalidArgumentException`.

## Struktura DiffResult

Każdy `DiffResult` reprezentuje różnice w pojedynczym pliku.

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

### Sformatowany wynik

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

Generuje wynik w ujednoliconym formacie różnic. W przypadku zmodyfikowanego pliku tekstowego odpowiada on `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()
     {
```

### Sprawdzanie zmian

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

## Struktura fragmentów

Każdy `Hunk` reprezentuje ciągły obszar zmian wraz z wierszami kontekstu.

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

## Algorytmy różnic

Pitmaster implementuje cztery algorytmy różnic. Domyślnym jest Myers.

### Różnice Myersa

Standardowy algorytm O(ND), taki sam jak domyślnie używany przez git. Generuje minimalne skrypty edycji.

```php
use Pitmaster\Diff\MyersDiff;

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

Metoda `diff()` przyjmuje dwa ciągi znaków i zwraca tablicę obiektów `Hunk`. Dzieli dane wejściowe w miejscach nowych wierszy, oblicza najkrótszy skrypt edycji i grupuje zmiany w fragmenty z 3 wierszami kontekstu (tak jak domyślnie w git).

### Różnice cierpliwe

Zapewnia lepsze strukturalne różnice w kodzie. Najpierw dopasowuje unikatowe wiersze, a potem wypełnia luki między nimi.

```php
use Pitmaster\Diff\PatienceDiff;

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

### Różnice histogramowe

Odmiana algorytmu cierpliwego, która lepiej radzi sobie z powtarzającymi się wierszami. Używana przez JGit i Eclipse.

```php
use Pitmaster\Diff\HistogramDiff;

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

### Minimalne różnice

Generuje najmniejsze możliwe różnice (najmniej zmienionych wierszy), kosztem większej liczby obliczeń.

```php
use Pitmaster\Diff\MinimalDiff;

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

## Wykrywanie plików binarnych

Pitmaster traktuje zawartość jako binarną, jeśli w pierwszych 8192 bajtach znajduje się bajt NUL (git sprawdza pierwsze 8000).

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

Pliki binarne dają obiekt `DiffResult` z `$binary = true` i pustą tablicą `$hunks`. Sformatowany wynik to:

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

## Kolorowanie wyniku

CLI używa `ColorDiff` do wyświetlania w terminalu z kolorami ANSI.

```php
use Pitmaster\Diff\ColorDiff;

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

- Dodane wiersze (`+`) są wyświetlane na zielono
- Usunięte wiersze (`-`) są wyświetlane na czerwono
- Nagłówki fragmentów (`@@`) są wyświetlane na turkusowo
- Nagłówki plików (`diff --git`, `index`, `---`, `+++`) są wyświetlane pogrubioną czcionką

`pitmaster diff` zawsze koloruje wynik, także gdy jest on przekierowany potokiem. `pitmaster show` koloruje go tylko wtedy, gdy stdout jest terminalem.

## Różnice między drzewami (wewnętrzne)

Klasa `TreeDiff` rekurencyjnie porównuje dwa obiekty drzewa, tworząc `DiffResult` dla każdego zmienionego pliku.

```php
use Pitmaster\Diff\TreeDiff;

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

Różnice między drzewami obsługują:
- Dodane pliki (obecne tylko w nowym drzewie)
- Usunięte pliki (obecne tylko w starym drzewie)
- Zmodyfikowane pliki (ta sama ścieżka, inny skrót bloba)
- Rekurencyjne porównywanie podkatalogów
- Dopasowywanie par plików usuniętych i dodanych przy zmianie nazwy (dokładne dopasowanie skrótu lub co najmniej 50% wspólnych wierszy)

Wpisy z niezmienionym skrótem są pomijane, więc sama zmiana trybu nie daje wyniku.

## Różnice słowne

Do wykrywania zmian pojedynczych słów w wierszach.

```php
use Pitmaster\Diff\WordDiff;

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