
# Diff

Pitmaster calculează diferențe în formatul unificat al lui git. Motorul de diferențe acceptă mai mulți algoritmi, formatarea diff-ului unificat, detectarea fișierelor binare și compararea recursivă la nivel de arbore.

Pentru un fișier text modificat cu modul `100644`, `format()` produce aceiași octeți ca `git diff`. Celelalte cazuri sunt formatate diferit momentan ([#3](https://github.com/inline0/pitmaster/issues/3)):

- Fișierele noi și cele șterse nu au nicio linie `new file mode` / `deleted file mode`, nicio linie `index` și nicio `/dev/null`: ambele părți sunt etichetate `a/<path>` și `b/<path>`, iar intervalul secțiunii de modificări de pe partea goală este scris ca `-1,0` sau `+1,0`, acolo unde git scrie `-0,0` sau `+0,0`.
- Linia `index` se termină întotdeauna cu `100644`, indiferent de modul fișierului.
- Modificările doar de mod (de exemplu `chmod +x`) nu produc niciun rezultat.
- Fișierele binare afișează `Binary files differ` fără o linie `index`, în timp ce git afișează `Binary files a/<path> and b/<path> differ`.
- Redenumirile sunt afișate ca o singură intrare `diff --git a/<old> b/<new>` cu o diferență de conținut, fără linii `similarity index`, `rename from` sau `rename to`.

## Operații Diff

### Modificări nestagiate (arbore de lucru față de index)

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

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

Compară fiecare fișier din index cu starea sa actuală din arborele de lucru. Detectează modificări, ștergeri și fișiere binare.

### Modificări stagiate (index față de HEAD)

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

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

Compară indexul actual cu arborele commitului HEAD. Arată ce va fi inclus în următorul commit. Fișierele șterse și adăugate cu conținut similar sunt raportate ca o singură intrare redenumită. Înainte de primul commit, HEAD nu există, iar rezultatul este gol ([#5](https://github.com/inline0/pitmaster/issues/5)).

Asocierea redenumirilor potrivește momentan și fișiere fără legătură care au o singură linie, deoarece linia goală finală este considerată conținut comun ([#4](https://github.com/inline0/pitmaster/issues/4)).

### Diferență arbore la arbore

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

Compară recursiv doi arbori. Transmiteți ID-urile arborilor: ID-urile commiturilor nu sunt transformate în arborii lor și produc un rezultat gol. Același motor `TreeDiff` stă la baza lui `diffStaged()`, `show()` și `logPath()`.

### Filtrare după cale

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

Pathspec este o cale exactă de fișier. `diffStaged()` acceptă același argument, dar momentan îl ignoră, așa că returnează întotdeauna toate fișierele stadiate ([#5](https://github.com/inline0/pitmaster/issues/5)).

### Alegerea unui algoritm

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

Numele valide sunt `myers` (implicit), `patience`, `histogram` și `minimal`. Orice alt nume generează `InvalidArgumentException`.

## Structura DiffResult

Fiecare `DiffResult` reprezintă diferența pentru un singur fișier.

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

### Rezultat formatat

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

Produce un rezultat diff unificat. Pentru un fișier text modificat, corespunde lui `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()
     {
```

### Verificarea modificărilor

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

## Structura secțiunii de modificări

Fiecare `Hunk` reprezintă o regiune contiguă de modificări, cu linii de context.

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

## Algoritmi Diff

Pitmaster implementează patru algoritmi diff. Algoritmul implicit este Myers.

### Diff Myers

Algoritmul standard O(ND), la fel ca valoarea implicită din git. Produce scripturi de editare minimale.

```php
use Pitmaster\Diff\MyersDiff;

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

Metoda `diff()` primește două șiruri și returnează o matrice de obiecte `Hunk`. Împarte intrarea la liniile noi, calculează cel mai scurt script de editare și grupează modificările în secțiuni cu 3 linii de context (conform valorii implicite din git).

### Diff Patience

Diferențe structurale mai bune pentru cod. Potrivește mai întâi liniile unice, apoi completează spațiile dintre ele.

```php
use Pitmaster\Diff\PatienceDiff;

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

### Diff Histogram

O variantă a diff-ului Patience care gestionează mai bine liniile repetate. Este folosită de JGit și Eclipse.

```php
use Pitmaster\Diff\HistogramDiff;

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

### Diff Minimal

Produce cea mai mică diferență posibilă (cu cele mai puține linii modificate), cu prețul unui volum mai mare de calcul.

```php
use Pitmaster\Diff\MinimalDiff;

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

## Detectarea fișierelor binare

Pitmaster tratează conținutul ca binar dacă acesta conține un octet NUL în primii 8192 octeți (git verifică primii 8000).

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

Fișierele binare produc un `DiffResult` cu `$binary = true` și o matrice `$hunks` goală. Rezultatul formatat este:

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

## Rezultat colorat

CLI folosește `ColorDiff` pentru rezultatul în terminal, cu culori ANSI.

```php
use Pitmaster\Diff\ColorDiff;

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

- Liniile adăugate (`+`) sunt afișate cu verde
- Liniile eliminate (`-`) sunt afișate cu roșu
- Antetele secțiunilor de modificări (`@@`) sunt afișate cu cyan
- Antetele fișierelor (`diff --git`, `index`, `---`, `+++`) sunt afișate cu caractere aldine

`pitmaster diff` colorează întotdeauna rezultatul, inclusiv când este redirecționat. `pitmaster show` îl colorează doar când stdout este un terminal.

## Diff între arbori (intern)

Clasa `TreeDiff` compară recursiv două obiecte arbore, producând un `DiffResult` pentru fiecare fișier modificat.

```php
use Pitmaster\Diff\TreeDiff;

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

Diff-ul între arbori gestionează:
- Fișiere adăugate (prezente doar în arborele nou)
- Fișiere șterse (prezente doar în arborele vechi)
- Fișiere modificate (aceeași cale, hash blob diferit)
- Compararea recursivă a subdirectoarelor
- Asocierea redenumirilor între fișierele șterse și cele adăugate (hash identic sau cel puțin 50% linii comune)

Intrările al căror hash nu s-a modificat sunt omise, astfel că o simplă schimbare de mod nu produce niciun rezultat.

## Diff de cuvinte

Pentru modificări la nivel de cuvânt, în linie.

```php
use Pitmaster\Diff\WordDiff;

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