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 rigaindexné/dev/null: entrambi i lati sono etichettatia/<path>eb/<path>e l’intervallo dell’hunk sul lato vuoto è indicato come-1,0o+1,0, dove git scrive-0,0o+0,0. - La riga
indextermina sempre con100644, 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 differsenza una rigaindex; git stampaBinary 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 righesimilarity index,rename fromorename to.
Operazioni diff
Modifiche non preparate (working tree rispetto all'indice)
$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)
$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
$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
$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
$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.
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
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
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.
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.
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.
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.
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.
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).
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.
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.
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.
use Pitmaster\Diff\WordDiff;
$result = WordDiff::diff('the quick fox', 'the slow fox');
// 'the [-quick-]{+slow+} fox'