Przejdź do treści

Różnice

Pitmaster oblicza różnice w ujednoliconym formacie git. Silnik różnic obsługuje wiele algorytmów, formatowanie ujednoliconych różnic, wykrywanie plików binarnych i rekursywne porównywanie na poziomie drzew.

Dla zmodyfikowanego pliku tekstowego w trybie 100644, format() generuje te same bajty co git diff. Obecnie pozostałe przypadki są formatowane inaczej (#3):

  • Nowe i usunięte pliki nie mają wiersza new file mode / deleted file mode, wiersza index ani /dev/null: obie strony są oznaczone jako a/<path> i b/<path>, a zakres fragmentu pustej strony jest zapisywany jako -1,0 lub +1,0 tam, gdzie git zapisuje -0,0 lub +0,0.
  • Wiersz index zawsze kończy się na 100644, niezależnie od trybu pliku.
  • Zmiany dotyczące wyłącznie trybu (na przykład chmod +x) nie dają wyniku.
  • Pliki binarne wyświetlają Binary files differ bez wiersza index, podczas gdy git wyświetla Binary files a/<path> and b/<path> differ.
  • Zmiany nazw są pokazywane jako jeden wpis diff --git a/<old> b/<new> z różnicą zawartości, bez wierszy similarity index, rename from ani rename to.

Operacje na różnicach

Niezatwierdzone zmiany (drzewo robocze a indeks)

PHP
$diffs = $repo->diff();

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

Porównuje każdy plik w indeksie z jego bieżącym stanem w drzewie roboczym. Wykrywa modyfikacje, usunięcia i pliki binarne.

Zmiany w indeksie (indeks a HEAD)

PHP
$diffs = $repo->diffStaged();

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

Porównuje bieżący indeks z drzewem commita HEAD. Pokazuje, co zostanie uwzględnione w następnym commicie. Usunięte i dodane pliki o podobnej zawartości są zgłaszane jako jeden wpis zmiany nazwy. Przed pierwszym commitem nie ma HEAD, więc wynik jest pusty (#5).

Obecnie dopasowywanie par przy zmianie nazw obejmuje też niezwiązane, jednoliniowe pliki, ponieważ końcowy pusty wiersz jest liczony jako wspólna zawartość (#4).

Różnice między drzewami

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

Porównuje rekurencyjnie dwa drzewa. Przekaż identyfikatory drzew: identyfikatory commitów nie są rozwijane do drzew, więc wynik jest pusty. Ten sam silnik TreeDiff obsługuje diffStaged(), show() i logPath().

Filtrowanie według ścieżki

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

Specyfikacja ścieżki to dokładna ścieżka do pliku. diffStaged() przyjmuje ten sam argument, ale obecnie go ignoruje, więc zawsze zwraca wszystkie pliki w indeksie (#5).

Wybór algorytmu

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

Prawidłowe nazwy to myers (domyślna), patience, histogram i minimal. Każda inna nazwa powoduje wyjątek InvalidArgumentException.

Struktura DiffResult

Każdy DiffResult reprezentuje różnice w pojedynczym pliku.

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

Sformatowany wynik

PHP
echo $diff->format();

Generuje wynik w ujednoliconym formacie różnic. W przypadku zmodyfikowanego pliku tekstowego odpowiada on git diff:

Kod
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()
     {

Sprawdzanie zmian

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

Struktura fragmentów

Każdy Hunk reprezentuje ciągły obszar zmian wraz z wierszami kontekstu.

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

Algorytmy różnic

Pitmaster implementuje cztery algorytmy różnic. Domyślnym jest Myers.

Różnice Myersa

Standardowy algorytm O(ND), taki sam jak domyślnie używany przez git. Generuje minimalne skrypty edycji.

PHP
use Pitmaster\Diff\MyersDiff;

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

Metoda diff() przyjmuje dwa ciągi znaków i zwraca tablicę obiektów Hunk. Dzieli dane wejściowe w miejscach nowych wierszy, oblicza najkrótszy skrypt edycji i grupuje zmiany w fragmenty z 3 wierszami kontekstu (tak jak domyślnie w git).

Różnice cierpliwe

Zapewnia lepsze strukturalne różnice w kodzie. Najpierw dopasowuje unikatowe wiersze, a potem wypełnia luki między nimi.

PHP
use Pitmaster\Diff\PatienceDiff;

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

Różnice histogramowe

Odmiana algorytmu cierpliwego, która lepiej radzi sobie z powtarzającymi się wierszami. Używana przez JGit i Eclipse.

PHP
use Pitmaster\Diff\HistogramDiff;

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

Minimalne różnice

Generuje najmniejsze możliwe różnice (najmniej zmienionych wierszy), kosztem większej liczby obliczeń.

PHP
use Pitmaster\Diff\MinimalDiff;

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

Wykrywanie plików binarnych

Pitmaster traktuje zawartość jako binarną, jeśli w pierwszych 8192 bajtach znajduje się bajt NUL (git sprawdza pierwsze 8000).

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

Pliki binarne dają obiekt DiffResult z $binary = true i pustą tablicą $hunks. Sformatowany wynik to:

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

Kolorowanie wyniku

CLI używa ColorDiff do wyświetlania w terminalu z kolorami ANSI.

PHP
use Pitmaster\Diff\ColorDiff;

$colorized = ColorDiff::colorize($diff->format());
echo $colorized;
  • Dodane wiersze (+) są wyświetlane na zielono
  • Usunięte wiersze (-) są wyświetlane na czerwono
  • Nagłówki fragmentów (@@) są wyświetlane na turkusowo
  • Nagłówki plików (diff --git, index, ---, +++) są wyświetlane pogrubioną czcionką

pitmaster diff zawsze koloruje wynik, także gdy jest on przekierowany potokiem. pitmaster show koloruje go tylko wtedy, gdy stdout jest terminalem.

Różnice między drzewami (wewnętrzne)

Klasa TreeDiff rekurencyjnie porównuje dwa obiekty drzewa, tworząc DiffResult dla każdego zmienionego pliku.

PHP
use Pitmaster\Diff\TreeDiff;

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

Różnice między drzewami obsługują:

  • Dodane pliki (obecne tylko w nowym drzewie)
  • Usunięte pliki (obecne tylko w starym drzewie)
  • Zmodyfikowane pliki (ta sama ścieżka, inny skrót bloba)
  • Rekurencyjne porównywanie podkatalogów
  • Dopasowywanie par plików usuniętych i dodanych przy zmianie nazwy (dokładne dopasowanie skrótu lub co najmniej 50% wspólnych wierszy)

Wpisy z niezmienionym skrótem są pomijane, więc sama zmiana trybu nie daje wyniku.

Różnice słowne

Do wykrywania zmian pojedynczych słów w wierszach.

PHP
use Pitmaster\Diff\WordDiff;

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