Treci la conținut

Diff

Pitmaster calculează diferențe în formatul unificat al lui git. Motorul de diferențe acceptă mai mulți algoritmi, formatarea diff-ului unificat, detectarea fișierelor binare și compararea recursivă la nivel de arbore.

Pentru un fișier text modificat cu modul 100644, format() produce aceiași octeți ca git diff. Celelalte cazuri sunt formatate diferit momentan (#3):

  • Fișierele noi și cele șterse nu au nicio linie new file mode / deleted file mode, nicio linie index și nicio /dev/null: ambele părți sunt etichetate a/<path> și b/<path>, iar intervalul secțiunii de modificări de pe partea goală este scris ca -1,0 sau +1,0, acolo unde git scrie -0,0 sau +0,0.
  • Linia index se termină întotdeauna cu 100644, indiferent de modul fișierului.
  • Modificările doar de mod (de exemplu chmod +x) nu produc niciun rezultat.
  • Fișierele binare afișează Binary files differ fără o linie index, în timp ce git afișează Binary files a/<path> and b/<path> differ.
  • Redenumirile sunt afișate ca o singură intrare diff --git a/<old> b/<new> cu o diferență de conținut, fără linii similarity index, rename from sau rename to.

Operații Diff

Modificări nestagiate (arbore de lucru față de index)

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

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

Compară fiecare fișier din index cu starea sa actuală din arborele de lucru. Detectează modificări, ștergeri și fișiere binare.

Modificări stagiate (index față de HEAD)

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

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

Compară indexul actual cu arborele commitului HEAD. Arată ce va fi inclus în următorul commit. Fișierele șterse și adăugate cu conținut similar sunt raportate ca o singură intrare redenumită. Înainte de primul commit, HEAD nu există, iar rezultatul este gol (#5).

Asocierea redenumirilor potrivește momentan și fișiere fără legătură care au o singură linie, deoarece linia goală finală este considerată conținut comun (#4).

Diferență arbore la arbore

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

Compară recursiv doi arbori. Transmiteți ID-urile arborilor: ID-urile commiturilor nu sunt transformate în arborii lor și produc un rezultat gol. Același motor TreeDiff stă la baza lui diffStaged(), show() și logPath().

Filtrare după cale

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

Pathspec este o cale exactă de fișier. diffStaged() acceptă același argument, dar momentan îl ignoră, așa că returnează întotdeauna toate fișierele stadiate (#5).

Alegerea unui algoritm

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

Numele valide sunt myers (implicit), patience, histogram și minimal. Orice alt nume generează InvalidArgumentException.

Structura DiffResult

Fiecare DiffResult reprezintă diferența pentru un singur fișier.

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

Rezultat formatat

PHP
echo $diff->format();

Produce un rezultat diff unificat. Pentru un fișier text modificat, corespunde lui git diff:

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

Verificarea modificărilor

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

Structura secțiunii de modificări

Fiecare Hunk reprezintă o regiune contiguă de modificări, cu linii de context.

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 implementează patru algoritmi diff. Algoritmul implicit este Myers.

Diff Myers

Algoritmul standard O(ND), la fel ca valoarea implicită din git. Produce scripturi de editare minimale.

PHP
use Pitmaster\Diff\MyersDiff;

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

Metoda diff() primește două șiruri și returnează o matrice de obiecte Hunk. Împarte intrarea la liniile noi, calculează cel mai scurt script de editare și grupează modificările în secțiuni cu 3 linii de context (conform valorii implicite din git).

Diff Patience

Diferențe structurale mai bune pentru cod. Potrivește mai întâi liniile unice, apoi completează spațiile dintre ele.

PHP
use Pitmaster\Diff\PatienceDiff;

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

Diff Histogram

O variantă a diff-ului Patience care gestionează mai bine liniile repetate. Este folosită de JGit și Eclipse.

PHP
use Pitmaster\Diff\HistogramDiff;

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

Diff Minimal

Produce cea mai mică diferență posibilă (cu cele mai puține linii modificate), cu prețul unui volum mai mare de calcul.

PHP
use Pitmaster\Diff\MinimalDiff;

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

Detectarea fișierelor binare

Pitmaster tratează conținutul ca binar dacă acesta conține un octet NUL în primii 8192 octeți (git verifică primii 8000).

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

Fișierele binare produc un DiffResult cu $binary = true și o matrice $hunks goală. Rezultatul formatat este:

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

Rezultat colorat

CLI folosește ColorDiff pentru rezultatul în terminal, cu culori ANSI.

PHP
use Pitmaster\Diff\ColorDiff;

$colorized = ColorDiff::colorize($diff->format());
echo $colorized;
  • Liniile adăugate (+) sunt afișate cu verde
  • Liniile eliminate (-) sunt afișate cu roșu
  • Antetele secțiunilor de modificări (@@) sunt afișate cu cyan
  • Antetele fișierelor (diff --git, index, ---, +++) sunt afișate cu caractere aldine

pitmaster diff colorează întotdeauna rezultatul, inclusiv când este redirecționat. pitmaster show îl colorează doar când stdout este un terminal.

Diff între arbori (intern)

Clasa TreeDiff compară recursiv două obiecte arbore, producând un DiffResult pentru fiecare fișier modificat.

PHP
use Pitmaster\Diff\TreeDiff;

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

Diff-ul între arbori gestionează:

  • Fișiere adăugate (prezente doar în arborele nou)
  • Fișiere șterse (prezente doar în arborele vechi)
  • Fișiere modificate (aceeași cale, hash blob diferit)
  • Compararea recursivă a subdirectoarelor
  • Asocierea redenumirilor între fișierele șterse și cele adăugate (hash identic sau cel puțin 50% linii comune)

Intrările al căror hash nu s-a modificat sunt omise, astfel că o simplă schimbare de mod nu produce niciun rezultat.

Diff de cuvinte

Pentru modificări la nivel de cuvânt, în linie.

PHP
use Pitmaster\Diff\WordDiff;

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