Diff
Pitmaster berechnet Diffs im einheitlichen Format von git. Die Diff-Engine unterstützt mehrere Algorithmen, einheitliche Diff-Formatierung, die Erkennung von Binärdateien und rekursive Vergleiche auf Baumebene.
Für eine geänderte Textdatei mit Modus 100644 erzeugt format() dieselben Bytes wie git diff. Andere Fälle werden derzeit anders formatiert (#3):
- Neue und gelöschte Dateien haben keine Zeile
new file mode/deleted file mode, keine Zeileindexund kein/dev/null: Beide Seiten sind mita/<path>undb/<path>beschriftet, und der Hunk-Bereich der leeren Seite wird als-1,0oder+1,0angegeben, wo git-0,0oder+0,0schreibt. - Die Zeile
indexendet unabhängig vom Dateimodus immer mit100644. - Änderungen nur am Modus (zum Beispiel
chmod +x) erzeugen keine Ausgabe. - Bei Binärdateien wird
Binary files differohne eine Zeileindexausgegeben; git gibtBinary files a/<path> and b/<path> differaus. - Umbenennungen erscheinen als einzelner Eintrag
diff --git a/<old> b/<new>mit Inhalts-Diff, ohne Zeilensimilarity index,rename fromoderrename to.
Diff-Operationen
Nicht bereitgestellte Änderungen (Arbeitsbaum gegenüber Index)
$diffs = $repo->diff();
foreach ($diffs as $diff) {
echo $diff->format();
}
Vergleicht jede Datei im Index mit ihrem aktuellen Zustand im Arbeitsbaum. Erkennt Änderungen, Löschungen und Binärdateien.
Bereitgestellte Änderungen (Index gegenüber HEAD)
$diffs = $repo->diffStaged();
foreach ($diffs as $diff) {
echo $diff->format();
}
Vergleicht den aktuellen Index mit dem Tree des HEAD-Commits. Zeigt, was im nächsten Commit enthalten sein wird. Gelöschte und hinzugefügte Dateien mit ähnlichem Inhalt werden als ein umbenannter Eintrag gemeldet. Vor dem ersten Commit gibt es keinen HEAD, daher ist das Ergebnis leer (#5).
Beim Zuordnen von Umbenennungen werden derzeit auch nicht zusammengehörige einzeilige Dateien erkannt, weil die abschließende Leerzeile als gemeinsamer Inhalt zählt (#4).
Tree-zu-Tree-Diff
$a = $repo->readObject($repo->resolve('HEAD~1')->hex)->tree;
$b = $repo->head()->tree;
$diffs = $repo->diffTree($a, $b);
Vergleicht zwei Trees rekursiv. Übergib Tree-IDs: Commit-IDs werden nicht auf ihre Trees zurückgeführt und ergeben ein leeres Ergebnis. Dieselbe TreeDiff-Engine liegt diffStaged(), show() und logPath() zugrunde.
Nach Pfad filtern
$diffs = $repo->diff('src/specific-file.php');
Die Pfadangabe ist ein exakter Dateipfad. diffStaged() akzeptiert dasselbe Argument, ignoriert es derzeit jedoch und gibt daher immer alle bereitgestellten Dateien zurück (#5).
Algorithmus auswählen
$diffs = $repo->diff(null, 'patience');
$diffs = $repo->diffStaged(null, 'histogram');
$diffs = $repo->diffTree($a, $b, 'minimal');
Gültige Namen sind myers (Standard), patience, histogram und minimal. Jeder andere Name löst InvalidArgumentException aus.
DiffResult-Struktur
Jedes DiffResult stellt den Diff für eine einzelne Datei dar.
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'
}
}
}
Formatierte Ausgabe
echo $diff->format();
Erzeugt eine einheitliche Diff-Ausgabe. Bei einer geänderten Textdatei entspricht sie 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()
{
Auf Änderungen prüfen
if ($diff->hasChanges()) {
// File was modified (or is binary with changes)
}
Hunk-Struktur
Jedes Hunk stellt einen zusammenhängenden Änderungsbereich mit Kontextzeilen dar.
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 @@"
Diff-Algorithmen
Pitmaster implementiert vier Diff-Algorithmen. Standardmäßig wird Myers verwendet.
Myers-Diff
Der Standardalgorithmus O(ND), identisch mit der Standardeinstellung von git. Erzeugt minimale Bearbeitungsskripte.
use Pitmaster\Diff\MyersDiff;
$hunks = MyersDiff::diff($oldContent, $newContent);
Die Methode diff() akzeptiert zwei Zeichenfolgen und gibt ein Array von Hunk-Objekten zurück. Sie teilt die Eingabe an Zeilenumbrüchen auf, berechnet das kürzeste Bearbeitungsskript und gruppiert Änderungen in Hunks mit 3 Kontextzeilen (wie bei der Standardeinstellung von git).
Patience-Diff
Liefert strukturell bessere Diffs für Code. Ordnet zuerst eindeutige Zeilen zu und füllt dann die Bereiche dazwischen auf.
use Pitmaster\Diff\PatienceDiff;
$hunks = PatienceDiff::diff($oldContent, $newContent);
Histogramm-Diff
Eine Variante des Patience-Diffs, die besser mit wiederholten Zeilen umgeht. Wird von JGit und Eclipse verwendet.
use Pitmaster\Diff\HistogramDiff;
$hunks = HistogramDiff::diff($oldContent, $newContent);
Minimaler Diff
Erzeugt den kleinstmöglichen Diff (mit den wenigsten geänderten Zeilen), benötigt dafür aber mehr Rechenaufwand.
use Pitmaster\Diff\MinimalDiff;
$hunks = MinimalDiff::diff($oldContent, $newContent);
Binärerkennung
Pitmaster behandelt Inhalte als Binärdaten, wenn eines der ersten 8192 Bytes ein NUL-Byte enthält (git prüft die ersten 8000).
if (MyersDiff::isBinary($content)) {
// Binary file, skip line-level diff
}
Binärdateien erzeugen ein DiffResult mit $binary = true und einem leeren $hunks-Array. Die formatierte Ausgabe lautet:
diff --git a/image.png b/image.png
Binary files differ
Farbige Ausgabe
CLI verwendet ColorDiff für die Terminalausgabe mit ANSI-Farben.
use Pitmaster\Diff\ColorDiff;
$colorized = ColorDiff::colorize($diff->format());
echo $colorized;
- Hinzugefügte Zeilen (
+) werden grün dargestellt - Entfernte Zeilen (
-) werden rot dargestellt - Hunk-Header (
@@) werden cyan dargestellt - Datei-Header (
diff --git,index,---,+++) werden fett dargestellt
pitmaster diff färbt die Ausgabe immer ein, auch wenn sie weitergeleitet wird. pitmaster show färbt sie nur ein, wenn stdout ein Terminal ist.
Tree-Diff (intern)
Die Klasse TreeDiff vergleicht zwei Tree-Objekte rekursiv und erzeugt für jede geänderte Datei ein DiffResult.
use Pitmaster\Diff\TreeDiff;
$treeDiff = new TreeDiff($repo->objectDatabase());
$diffs = $treeDiff->diff($oldTreeId, $newTreeId);
Der Tree-Diff verarbeitet:
- Hinzugefügte Dateien (nur im neuen Tree vorhanden)
- Gelöschte Dateien (nur im alten Tree vorhanden)
- Geänderte Dateien (gleicher Pfad, anderer Blob-Hash)
- Rekursiven Vergleich von Unterverzeichnissen
- Zuordnung von Umbenennungen zwischen gelöschten und hinzugefügten Dateien (exakte Hash-Übereinstimmung oder mindestens 50 % gemeinsame Zeilen)
Einträge mit unverändertem Hash werden übersprungen, daher erzeugt eine reine Modusänderung kein Ergebnis.
Wort-Diff
Für Änderungen auf Wortebene innerhalb von Zeilen.
use Pitmaster\Diff\WordDiff;
$result = WordDiff::diff('the quick fox', 'the slow fox');
// 'the [-quick-]{+slow+} fox'