
# Diff

Az Pitmaster a git egységes formátumában számítja ki a különbségeket. A diffmotor több algoritmust, egységes diffformázást, binárisfájl-felismerést és rekurzív, faszintű összehasonlítást támogat.

`100644` módú módosított szövegfájl esetén az `format()` ugyanazokat a bájtokat állítja elő, mint az `git diff`. A többi eset formázása jelenleg eltér ([#3](https://github.com/inline0/pitmaster/issues/3)):

- Az új és törölt fájlokban nincs `new file mode` / `deleted file mode` sor, `index` sor és `/dev/null` sem: mindkét oldal címkéje `a/<path>` és `b/<path>`, az üres oldal hunk-tartománya pedig `-1,0` vagy `+1,0` alakban szerepel ott, ahol a git `-0,0` vagy `+0,0` formát használ.
- Az `index` sor mindig `100644` végződik, a fájl módjától függetlenül.
- A csak módot érintő módosítások (például `chmod +x`) nem adnak eredményt.
- A bináris fájlok `Binary files differ` formában jelennek meg `index` sor nélkül, míg a git `Binary files a/<path> and b/<path> differ` formát használ.
- Az átnevezéseket egyetlen `diff --git a/<old> b/<new>` bejegyzésként jeleníti meg tartalmi diffel, `similarity index`, `rename from` és `rename to` sorok nélkül.

## Diffműveletek

### Nem előkészített módosítások (munkakönyvtár az indexhez képest)

```php
$diffs = $repo->diff();

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

Az indexben lévő minden fájlt összehasonlít az aktuális munkakönyvtári állapotával. Felismeri a módosításokat, törléseket és bináris fájlokat.

### Előkészített módosítások (index a HEAD-hez képest)

```php
$diffs = $repo->diffStaged();

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

Az aktuális indexet összehasonlítja a HEAD commit fájafájával. Megmutatja, mi kerül bele a következő commitba. A hasonló tartalmú törölt és hozzáadott fájlokat egy átnevezett bejegyzésként jelenti. Az első commit előtt nincs HEAD, ezért az eredmény üres ([#5](https://github.com/inline0/pitmaster/issues/5)).

Az átnevezések párosítása jelenleg egymástól független, egysoros fájlokat is összepárosít, mert a záró üres sort közös tartalomnak veszi ([#4](https://github.com/inline0/pitmaster/issues/4)).

### Fák összehasonlítása

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

Két fát hasonlít össze rekurzívan. Faazonosítókat adjon meg: a commitazonosítókat nem bontja fel fáikra, így üres eredményt adnak. Ugyanaz az `TreeDiff` motor szolgál az `diffStaged()`, az `show()` és az `logPath()` működésének alapjául.

### Szűrés elérési út alapján

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

A pathspec egy pontos fájlelérési út. Az `diffStaged()` ugyanilyen argumentumot fogad, de jelenleg figyelmen kívül hagyja, ezért minden előkészített fájlt visszaad ([#5](https://github.com/inline0/pitmaster/issues/5)).

### Algoritmus kiválasztása

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

Érvényes nevek: `myers` (alapértelmezett), `patience`, `histogram` és `minimal`. Más név esetén `InvalidArgumentException` kivétel keletkezik.

## A DiffResult felépítése

Minden `DiffResult` egyetlen fájl diffjét képviseli.

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

### Formázott kimenet

```php
echo $diff->format();
```

Egységes formátumú diffkimenetet állít elő. Módosított szövegfájl esetén megegyezik az `git diff` eredményével:

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

### Változások ellenőrzése

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

## Hunkok felépítése

Minden `Hunk` a módosítások összefüggő, kontextussorokat tartalmazó szakaszát jelenti.

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

## Diffalgoritmusok

Az Pitmaster négy diffalgoritmust valósít meg. Az alapértelmezett a Myers-algoritmus.

### Myers-féle diff

A szabványos O(ND) algoritmus, ugyanaz, mint a git alapértelmezettje. Minimális módosítási szkripteket állít elő.

```php
use Pitmaster\Diff\MyersDiff;

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

Az `diff()` metódus két karakterláncot fogad, és `Hunk` objektumok tömbjét adja vissza. A bemenetet sortörések mentén részekre bontja, kiszámítja a legrövidebb módosítási szkriptet, majd a változásokat 3 kontextussort tartalmazó hunkokba csoportosítja (a git alapértelmezett működését követve).

### Patience diff

Jobb szerkezetű diffet ad a kódhoz. Először az egyedi sorokat párosítja, majd kitölti a közöttük lévő részeket.

```php
use Pitmaster\Diff\PatienceDiff;

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

### Hisztogram diff

A patience diff egyik változata, amely jobban kezeli az ismétlődő sorokat. Az JGit és az Eclipse használja.

```php
use Pitmaster\Diff\HistogramDiff;

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

### Minimális diff

A lehető legkisebb diffet állítja elő (a lehető legkevesebb módosított sorral), több számítás árán.

```php
use Pitmaster\Diff\MinimalDiff;

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

## Bináris fájlok felismerése

Az Pitmaster akkor kezeli binárisként a tartalmat, ha annak első 8192 bájtja között NUL bájt található (a git az első 8000 bájtot ellenőrzi).

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

A bináris fájlok olyan `DiffResult` értéket adnak, amelyben az `$binary = true` szerepel, az `$hunks` tömb pedig üres. A formázott kimenet:

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

## Színezett kimenet

Az CLI az `ColorDiff` használatával, ANSI-színekkel jeleníti meg a terminálkimenetet.

```php
use Pitmaster\Diff\ColorDiff;

$colorized = ColorDiff::colorize($diff->format());
echo $colorized;
```

- A hozzáadott sorok (`+`) zölden jelennek meg
- A törölt sorok (`-`) pirosan jelennek meg
- A hunkfejlécek (`@@`) cián színűek
- A fájlfejlécek (`diff --git`, `index`, `---`, `+++`) félkövéren jelennek meg

`pitmaster diff` mindig színez, akkor is, ha a kimenete át van irányítva. `pitmaster show` csak akkor színez, ha a szabványos kimenet terminálra mutat.

## Fák összehasonlítása (belső)

Az `TreeDiff` osztály rekurzívan hasonlít össze két faobjektumot, és minden módosult fájlhoz létrehoz egy `DiffResult` elemet.

```php
use Pitmaster\Diff\TreeDiff;

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

A fák összehasonlítása a következőket kezeli:
- Hozzáadott fájlok (csak az új fában szerepelnek)
- Törölt fájlok (csak a régi fában szerepelnek)
- Módosított fájlok (azonos útvonal, eltérő blobhash)
- Alkönyvtárak rekurzív összehasonlítása
- Törölt és hozzáadott fájlok párosítása átnevezésként (azonos hash esetén, vagy ha a sorok legalább 50%-a közös)

A változatlan hashű bejegyzéseket kihagyja, ezért önmagában egy módváltozás nem eredményez kimenetet.

## Szavak összehasonlítása

Sorokon belüli, szószintű változásokhoz.

```php
use Pitmaster\Diff\WordDiff;

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