Vai al contenuto

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):

  • 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).

L'abbinamento delle ridenominazioni attualmente associa anche file non correlati di una sola riga, perché la riga vuota finale viene considerata contenuto condiviso (#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).

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:

Codice
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 è:

Codice
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'