
# Erot

Pitmaster laskee erot gitin yhtenäisessä muodossa. Eromoottori tukee useita algoritmeja, yhtenäistä eromuotoilua, binääritiedostojen tunnistusta ja rekursiivista puiden vertailua.

Muutetulle tekstitiedostolle, jonka tila on `100644`, `format()` tuottaa samat tavut kuin `git diff`. Muut tapaukset muotoillaan tällä hetkellä eri tavoin ([#3](https://github.com/inline0/pitmaster/issues/3)):

- Uusilla ja poistetuilla tiedostoilla ei ole riviä `new file mode` / `deleted file mode`, riviä `index` eikä riviä `/dev/null`: molemmat puolet merkitään tunnisteilla `a/<path>` ja `b/<path>`, ja tyhjän puolen lohkoalueeksi kirjoitetaan `-1,0` tai `+1,0`, kun git kirjoittaa `-0,0` tai `+0,0`.
- Rivi `index` päättyy aina merkkiin `100644` tiedoston tilasta riippumatta.
- Pelkät tilamuutokset (esimerkiksi `chmod +x`) eivät tuota tulosta.
- Binääritiedostoista tulostetaan `Binary files differ` ilman riviä `index`, kun git tulostaa `Binary files a/<path> and b/<path> differ`.
- Uudelleennimeämiset näytetään yhtenä `diff --git a/<old> b/<new>`-merkintänä sisältöeroineen ilman rivejä `similarity index`, `rename from` tai `rename to`.

## Erotoiminnot

### Vaiheistamattomat muutokset (työhakemisto vs. indeksi)

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

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

Vertaa indeksin kutakin tiedostoa sen nykyiseen tilaan työhakemistossa. Tunnistaa muutokset, poistot ja binääritiedostot.

### Vaiheistetut muutokset (indeksi vs. HEAD)

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

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

Vertaa nykyistä indeksiä HEAD-toimituksen puuhun. Näyttää, mitä seuraavaan toimitukseen sisällytetään. Samankaltaista sisältöä sisältävät poistetut ja lisätyt tiedostot ilmoitetaan yhtenä uudelleennimettynä merkintänä. Ennen ensimmäistä toimitusta HEADia ei ole, joten tulos on tyhjä ([#5](https://github.com/inline0/pitmaster/issues/5)).

Uudelleennimeämisten parittaminen yhdistää tällä hetkellä myös toisiinsa liittymättömiä yksirivisiä tiedostoja, koska lopun tyhjä rivi lasketaan yhteiseksi sisällöksi ([#4](https://github.com/inline0/pitmaster/issues/4)).

### Puiden välinen ero

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

Vertaa kahta puuta rekursiivisesti. Anna puiden tunnisteet: toimitustunnisteitä ei pureta puiksi, joten tulos on tyhjä. Sama `TreeDiff`-moottori on myös toimintojen `diffStaged()`, `show()` ja `logPath()` taustalla.

### Suodatus polun mukaan

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

Polkumäärite on yhden tiedoston tarkka polku. `diffStaged()` hyväksyy saman argumentin mutta ohittaa sen tällä hetkellä, joten se palauttaa aina kaikki vaiheistetut tiedostot ([#5](https://github.com/inline0/pitmaster/issues/5)).

### Algoritmin valinta

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

Kelvolliset nimet ovat `myers` (oletus), `patience`, `histogram` ja `minimal`. Mikä tahansa muu nimi aiheuttaa poikkeuksen `InvalidArgumentException`.

## DiffResult-rakenne

Kukin `DiffResult` kuvaa yhden tiedoston eroa.

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

### Muotoiltu tuloste

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

Tuottaa yhtenäisen erotulosteen. Muutetun tekstitiedoston tulos vastaa `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()
     {
```

### Muutosten tarkistus

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

## Lohkon rakenne

Kukin `Hunk` kuvaa yhtenäistä muutosaluetta, jossa on asiayhteysrivejä.

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

## Eroalgoritmit

Pitmaster toteuttaa neljä eroalgoritmia. Oletuksena käytetään Myersin algoritmia.

### Myersin eroalgoritmi

Tavanomainen O(ND)-algoritmi, sama kuin gitin oletus. Tuottaa mahdollisimman lyhyet muokkaussarjat.

```php
use Pitmaster\Diff\MyersDiff;

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

`diff()`-menetelmä ottaa kaksi merkkijonoa ja palauttaa joukon `Hunk`-olioita. Se jakaa syötteen rivinvaihtojen kohdalta, laskee lyhimmän muokkaussarjan ja ryhmittelee muutokset lohkoihin, joissa on kolme asiayhteysriviä (gitin oletuksen mukaisesti).

### Patience-eroalgoritmi

Paremmat rakenteelliset erot koodille. Yhdistää ensin yksilölliset rivit ja täydentää sitten niiden välit.

```php
use Pitmaster\Diff\PatienceDiff;

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

### Histogrammieroalgoritmi

Patience-eroalgoritmin muunnelma, joka käsittelee toistuvat rivit paremmin. Käytössä toiminnoissa JGit ja Eclipse.

```php
use Pitmaster\Diff\HistogramDiff;

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

### Minimaalinen ero

Tuottaa mahdollisimman pienen eron (mahdollisimman vähän muutettuja rivejä), mutta vaatii enemmän laskentaa.

```php
use Pitmaster\Diff\MinimalDiff;

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

## Binääritiedostojen tunnistus

Pitmaster käsittelee sisältöä binäärisenä, jos ensimmäisten 8192 tavun joukossa on NUL-tavu (git tarkistaa ensimmäiset 8000 tavua).

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

Binääritiedostot tuottavat `DiffResult`-tuloksen, jossa on `$binary = true` ja tyhjä `$hunks`-taulukko. Muotoiltu tuloste on:

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

## Värillinen tuloste

CLI käyttää `ColorDiff`-värejä päätetulosteessa.

```php
use Pitmaster\Diff\ColorDiff;

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

- Lisätyt rivit (`+`) näytetään vihreinä
- Poistetut rivit (`-`) näytetään punaisina
- Lohkojen otsakkeet (`@@`) näytetään syaaneina
- Tiedosto-otsakkeet (`diff --git`, `index`, `---`, `+++`) näytetään lihavoituina

`pitmaster diff` lisää tulosteeseen aina värit, myös silloin kun tuloste ohjataan putkeen. `pitmaster show` lisää värit vain, kun vakiotuloste on pääte.

## Puiden välinen ero (sisäinen)

`TreeDiff`-luokka vertaa kahta puuoliota rekursiivisesti ja tuottaa kullekin muutetulle tiedostolle `DiffResult`-olion.

```php
use Pitmaster\Diff\TreeDiff;

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

Puiden välisten erojen käsittely kattaa seuraavat:
- Lisätyt tiedostot (vain uudessa puussa)
- Poistetut tiedostot (vain vanhassa puussa)
- Muutetut tiedostot (sama polku, eri blob-tiiviste)
- Alikansioiden rekursiivinen vertailu
- Poistettujen ja lisättyjen tiedostojen yhdistäminen uudelleennimeämisiksi (täsmäävä tiiviste tai vähintään 50 % yhteisiä rivejä)

Merkinnät, joiden tiiviste ei ole muuttunut, ohitetaan, joten pelkkä tilan muutos ei tuota tulosta.

## Sanojen välinen ero

Rivien sisäisten, sanakohtaisten muutosten tarkasteluun.

```php
use Pitmaster\Diff\WordDiff;

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