
# Scalanie

Pitmaster scala gałęzie metodą trójstronną, podobnie jak `git merge`. Wykrywa możliwość przewinięcia do przodu, oblicza bazy scalania, scala drzewa i zawartość oraz generuje standardowe znaczniki konfliktu, gdy pliki są w konflikcie.

## Podstawowe scalanie

```php
$result = $repo->merge('feature/login');

if ($result->clean) {
    echo "Merged successfully: {$result->commitId->hex}\n";
} else {
    echo "Conflicts:\n";
    foreach ($result->conflictPaths as $path) {
        echo "  {$path}\n";
    }
}
```

## MergeResult

Klasa `MergeResult` to obiekt wartości tylko do odczytu:

```php
use Pitmaster\Merge\MergeResult;

$result->clean;           // bool: true if merge completed without conflicts
$result->commitId;        // ?ObjectId: the merge commit (null if conflicts)
$result->conflictPaths;   // array: list of file paths with conflicts
$result->mergedContents;  // array: path => content with conflict markers (if conflicts)
```

Gdy scalanie zatrzyma się z powodu konfliktów, ich stan jest zapisywany w indeksie i drzewie roboczym. Rozwiąż konflikty w plikach, dodaj je do indeksu za pomocą `add()` i wywołaj `$repo->mergeContinue()` albo wywołaj `$repo->mergeAbort()`, aby przywrócić stan sprzed scalania.

Przed scalaniem zatwierdź lub odłóż lokalne zmiany na stos: scalanie odtwarza drzewo robocze na podstawie wyniku i nadpisuje niezatwierdzone zmiany w śledzonych plikach ([#2](https://github.com/inline0/pitmaster/issues/2)).

## Scalanie z przewinięciem do przodu

Jeśli bieżący HEAD jest przodkiem docelowej gałęzi, Pitmaster wykonuje scalanie przez przewinięcie do przodu: po prostu przesuwa HEAD do docelowego commita bez tworzenia commita scalającego.

```
Before:         After fast-forward:

A---B (main)    A---B---C---D (main)
     \
      C---D (feature)
```

```php
$result = $repo->merge('feature');
// $result->clean === true
// $result->commitId points to D
// No merge commit created
```

Odwrotny przypadek nie jest jeszcze wykrywany: scalenie gałęzi, którą HEAD już zawiera, tworzy nowy commit scalający z niezmienionym drzewem, zamiast wyświetlić komunikat „Already up to date” ([#6](https://github.com/inline0/pitmaster/issues/6)).

## Scalenie trójstronne

Gdy gałęzie rozeszły się, Pitmaster wykonuje scalanie trójstronne:

1. Znajduje bazę scalania (najniższego wspólnego przodka)
2. Porównuje bazę z naszą gałęzią i bazę z ich gałęzią na poziomie drzewa
3. Dla każdego pliku zmienionego w obu gałęziach wykonuje scalanie zawartości
4. Tworzy commit scalający z dwoma rodzicami

```
Before:              After merge:

      C---D (feature)         C---D
     /                       /     \
A---B---E---F (main)   A---B---E---F---G (main)
```

## Baza scalania

Baza scalania to najbliższy wspólny przodek dwóch commitów. Pitmaster znajduje ją, przechodząc przez graf commitów. Jeśli istnieje kilka baz scalania (historia typu criss-cross), `merge()` najpierw je scala i używa wyniku jako bazy.

```php
$base = $repo->mergeBase(
    $repo->resolve('main'),
    $repo->resolve('feature/login'),
);

if ($base !== null) {
    echo "Common ancestor: {$base->hex}\n";
}
```

Aby uzyskać bezpośredni dostęp do algorytmu `MergeBase`:

```php
use Pitmaster\Merge\MergeBase;

$finder = new MergeBase($repo->objectDatabase());
$base = $finder->find($commitA, $commitB);

// All merge bases (criss-cross histories can have more than one)
$bases = $finder->findAll($commitA, $commitB);

// Check ancestry
$isAncestor = $finder->isAncestor($oldCommit, $newCommit);
```

## Sprawdzanie scalania gałęzi

```php
if ($repo->isBranchMerged('feature/done')) {
    // All commits in feature/done are reachable from default branch
    $repo->deleteBranch('feature/done');
}

// Check against a specific target
if ($repo->isBranchMerged('feature/done', 'develop')) {
    // Merged into develop
}
```

## Znaczniki konfliktu

Gdy plik został zmodyfikowany w obu gałęziach, a zmiany na siebie nachodzą, Pitmaster generuje standardowe znaczniki konfliktu. `merge()` oznacza strony jako `HEAD` i nazwą scalanej gałęzi:

```
<<<<<<< HEAD
    return $this->value + 1;
=======
    return $this->value * 2;
>>>>>>> feature
```

Klasa `ConflictMarker` otacza dwie wersje znacznikami:

```php
use Pitmaster\Merge\ConflictMarker;

$marked = ConflictMarker::mark($oursContent, $theirsContent, 'HEAD', 'feature');

// diff3 style, with the base section
$marked = ConflictMarker::mark($oursContent, $theirsContent, 'HEAD', 'feature', $baseContent, 'base');
```

## Trójstronne scalanie zawartości

Klasa `ThreeWayMerge` scala zawartość blobów (nie plików):

```php
use Pitmaster\Merge\ThreeWayMerge;

$result = ThreeWayMerge::merge($baseContent, $oursContent, $theirsContent, 'HEAD', 'feature');

$result['content'];   // merged text, with conflict markers if needed
$result['clean'];     // true when nothing conflicted
$result['conflicts']; // number of conflict regions
```

Przyjmuje trzy ciągi znaków (wersję bazową, naszą wersję i ich wersję), opcjonalne etykiety oraz styl konfliktu (domyślnie `'merge'` lub `'diff3'`, aby uwzględnić sekcję bazową).

## Strategie scalania

### Scalanie rekurencyjne

Strategia domyślna (`$repo->merge($branch)`), obsługuje historie scalania typu criss-cross z wieloma bazami scalania. CLI przyjmuje dla niej także `-s ort` i `-s recursive`.

### Scalanie naszej wersji

Zapisuje commit scalający, który pozostawia drzewo HEAD bez zmian. Przydatne do zachowania historii gałęzi bez uwzględniania zmian.

```php
$result = $repo->merge('feature/old', 'ours');
```

### Scalanie typu octopus

Scala dwie lub więcej gałęzi w jeden commit scalający. Jeśli wystąpi konflikt w którejkolwiek gałęzi, zwraca wynik z konfliktami, nie zapisując stanu scalania ani nie tworząc commita.

```php
$result = $repo->mergeOctopus(['feature/a', 'feature/b', 'feature/c']);
```

Klasy `RecursiveMerge`, `OursMerge` i `OctopusMerge` w `Pitmaster\Merge` to pomocnicze narzędzia do scalania zawartości; `Repository` samodzielnie implementuje powyższe strategie.

## Rerere (ponowne użycie zapisanego rozwiązania)

Pitmaster zawiera samodzielną klasę `Rerere`, która przechowuje rozwiązania w układzie `rr-cache` używanym przez git:

```php
use Pitmaster\Merge\Rerere;

$rerere = new Rerere($repo->gitDir());

if ($rerere->isEnabled()) {                     // rerere.enabled = true in .git/config
    $rerere->record($conflicted, $resolved);    // remember a resolution
    $resolved = $rerere->resolve($conflicted);  // replay it, or null
}
```

`merge()` jej nie wywołuje: rozwiązania są zapisywane i odtwarzane tylko wtedy, gdy zrobi to Twój kod. `isEnabled()` szuka dosłownego tekstu `rerere.enabled = true` w `.git/config` i nie rozpoznaje sekcji `[rerere]`, którą zapisuje `git config rerere.enabled true` ([#20](https://github.com/inline0/pitmaster/issues/20)).

## Przebieg scalania na poziomie drzewa

Pełny proces scalania:

1. `MergeBase::findAll()` znajduje wspólnych przodków
2. Drzewa bazy, naszej gałęzi i ich gałęzi są porównywane ścieżka po ścieżce
3. W przypadku plików zmienionych tylko w jednej gałęzi: przyjmuje tę zmianę
4. W przypadku plików zmienionych w obu gałęziach:
   - Jeśli obie zmiany są takie same: przyjmuje dowolną z nich (są zgodne)
   - Jeśli zmiany się różnią: uruchamia `ThreeWayMerge::merge()` na zawartości
5. Jeśli wszystkie scalania przebiegną bez konfliktów: buduje scalone drzewo i tworzy commit scalający
6. Jeśli wystąpią konflikty: zwraca `MergeResult` wraz z `clean = false` i ścieżkami konfliktów

## Użycie CLI

```bash
./vendor/bin/pitmaster merge feature/login
```

Dane wyjściowe po pomyślnym zakończeniu:
```
Merge successful.
```

Wynik w przypadku konfliktu:
```
CONFLICT in: src/File.php, src/Other.php
```
