
# Diff

Pitmaster calcola le differenze nel formato unificato di git. Il motore delle differenze supporta più algoritmi, la formattazione diff unificata, il rilevamento dei file binari e il confronto ricorsivo a livello di albero.

Per un file di testo modificato con modalità `100644`, `format()` produce gli stessi byte di `git diff`. Gli altri casi sono formattati diversamente al momento ([#3](https://github.com/inline0/pitmaster/issues/3)):

- I file nuovi ed eliminati non hanno una riga `new file mode` / `deleted file mode`, una riga `index` né `/dev/null`: entrambi i lati sono etichettati `a/<path>` e `b/<path>` e l’intervallo dell’hunk sul lato vuoto è indicato come `-1,0` o `+1,0`, dove git scrive `-0,0` o `+0,0`.
- La riga `index` termina sempre con `100644`, qualunque sia la modalità del file.
- Le modifiche che riguardano solo la modalità (ad esempio `chmod +x`) non producono risultati.
- I file binari stampano `Binary files differ` senza una riga `index`; git stampa `Binary files a/<path> and b/<path> differ`.
- Le rinominazioni vengono mostrate come una singola voce `diff --git a/<old> b/<new>` con una diff del contenuto, senza righe `similarity index`, `rename from` o `rename to`.

## Operazioni diff

### Modifiche non preparate (working tree rispetto all'indice)

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

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

Confronta ogni file nell'indice con lo stato corrente nel working tree. Rileva modifiche, eliminazioni e file binari.

### Modifiche preparate (indice rispetto a HEAD)

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

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

Confronta l'indice corrente con l'albero del commit HEAD. Mostra cosa verrà incluso nel prossimo commit. I file eliminati e aggiunti con contenuti simili vengono segnalati come un'unica voce rinominata. Prima del primo commit HEAD non esiste e il risultato è vuoto ([#5](https://github.com/inline0/pitmaster/issues/5)).

L'abbinamento delle ridenominazioni attualmente associa anche file non correlati di una sola riga, perché la riga vuota finale viene considerata contenuto condiviso ([#4](https://github.com/inline0/pitmaster/issues/4)).

### Differenza tra alberi

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

Confronta ricorsivamente due alberi. Passate gli ID degli alberi: gli ID dei commit non vengono ricondotti ai relativi alberi e restituiscono un risultato vuoto. Lo stesso motore `TreeDiff` è usato da `diffStaged()`, `show()` e `logPath()`.

### Filtrare per percorso

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

La pathspec è un percorso esatto di file. `diffStaged()` accetta lo stesso argomento, ma al momento lo ignora, quindi restituisce sempre tutti i file preparati ([#5](https://github.com/inline0/pitmaster/issues/5)).

### Scegliere un algoritmo

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

I nomi validi sono `myers` (predefinito), `patience`, `histogram` e `minimal`. Qualsiasi altro nome genera `InvalidArgumentException`.

## Struttura di DiffResult

Ogni `DiffResult` rappresenta la differenza di un singolo file.

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

### Output formattato

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

Produce un output diff unificato. Per un file di testo modificato corrisponde a `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()
     {
```

### Verificare la presenza di modifiche

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

## Struttura degli hunk

Ogni `Hunk` rappresenta una regione contigua di modifiche con righe di contesto.

```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 implementa quattro algoritmi diff. Quello predefinito è Myers.

### Diff di Myers

L’algoritmo standard O(ND), uguale all’impostazione predefinita di git. Produce script di modifica minimi.

```php
use Pitmaster\Diff\MyersDiff;

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

Il metodo `diff()` accetta due stringhe e restituisce un array di oggetti `Hunk`. Divide l’input in corrispondenza delle nuove righe, calcola lo script di modifica più breve e raggruppa le modifiche in hunk con 3 righe di contesto (come nell’impostazione predefinita di git).

### Diff di pazienza

Migliora le differenze strutturali del codice. Abbina prima le righe univoche, poi completa gli spazi tra di esse.

```php
use Pitmaster\Diff\PatienceDiff;

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

### Diff a istogramma

Una variante del diff di pazienza che gestisce meglio le righe ripetute. Usata da JGit e Eclipse.

```php
use Pitmaster\Diff\HistogramDiff;

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

### Diff minimo

Produce la differenza più piccola possibile (con il minor numero di righe modificate), a costo di una maggiore elaborazione.

```php
use Pitmaster\Diff\MinimalDiff;

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

## Rilevamento dei file binari

Pitmaster considera il contenuto binario se contiene un byte NUL nei primi 8192 byte (git controlla i primi 8000).

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

I file binari producono un `DiffResult` con `$binary = true` e un array `$hunks` vuoto. L'output formattato è:

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

## Output a colori

CLI usa `ColorDiff` per l'output nel terminale con colori ANSI.

```php
use Pitmaster\Diff\ColorDiff;

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

- Le righe aggiunte (`+`) sono mostrate in verde
- Le righe rimosse (`-`) sono mostrate in rosso
- Le intestazioni degli hunk (`@@`) sono mostrate in ciano
- Le intestazioni dei file (`diff --git`, `index`, `---`, `+++`) sono mostrate in grassetto

`pitmaster diff` applica sempre la colorazione, anche quando l'output viene reindirizzato. `pitmaster show` applica la colorazione solo quando stdout è un terminale.

## Diff degli alberi (interno)

La classe `TreeDiff` confronta ricorsivamente due oggetti tree e produce un `DiffResult` per ogni file modificato.

```php
use Pitmaster\Diff\TreeDiff;

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

Il diff degli alberi gestisce:
- File aggiunti (presenti solo nel nuovo albero)
- File eliminati (presenti solo nel vecchio albero)
- File modificati (stesso percorso, hash del blob diverso)
- Confronto ricorsivo delle sottodirectory
- Abbinamento dei rinominati tra file eliminati e aggiunti (hash identico oppure almeno il 50% di righe condivise)

Le voci con hash invariato vengono ignorate, quindi una modifica della sola modalità non produce un risultato.

## Diff delle parole

Per le modifiche inline a livello di parola all'interno delle righe.

```php
use Pitmaster\Diff\WordDiff;

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