
# Merge

Pitmaster esegue il merge dei branch con un merge a tre vie, come `git merge`. Rileva le opportunità di fast-forward, calcola le basi di merge, esegue merge a livello di albero e di contenuto e genera i marcatori di conflitto standard quando i file sono in conflitto.

## Merge di base

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

La classe `MergeResult` è un oggetto valore in sola lettura:

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

Quando il merge si interrompe a causa di conflitti, lo stato del conflitto viene scritto nell'indice e nell'albero di lavoro. Risolvete i file, `add()`, quindi chiamate `$repo->mergeContinue()` oppure chiamate `$repo->mergeAbort()` per ripristinare lo stato precedente al merge.

Prima del merge, eseguite il commit o mettete da parte le modifiche locali: il merge ricostruisce l'albero di lavoro a partire dal risultato e sovrascrive le modifiche non confermate nei file tracciati ([#2](https://github.com/inline0/pitmaster/issues/2)).

## Merge fast-forward

Quando l'HEAD corrente è un antenato del branch di destinazione, Pitmaster esegue un merge fast-forward: sposta semplicemente HEAD sul commit di destinazione senza creare un commit di merge.

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

Il caso inverso non è ancora rilevato: un merge di un branch già contenuto in HEAD crea un nuovo commit di merge con un albero invariato, invece di segnalare "Already up to date" ([#6](https://github.com/inline0/pitmaster/issues/6)).

## Merge a tre vie

Quando i branch sono divergenti, Pitmaster esegue un merge a tre vie:

1. Trovare la base di merge (l'antenato comune più prossimo)
2. Confrontare la base con il nostro branch e con quello altrui a livello di albero
3. Per ogni file modificato in entrambi i branch, eseguire il merge a livello di contenuto
4. Creare un commit di merge con due genitori

```
Before:              After merge:

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

## Base di merge

La base di merge è l'antenato comune più recente di due commit. Pitmaster la trova percorrendo il grafo dei commit. Se ci sono più basi di merge (cronologia incrociata), `merge()` le unisce prima e usa il risultato come base.

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

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

Per accedere direttamente all'algoritmo di `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);
```

## Verifica del merge dei branch

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

## Marcatori di conflitto

Quando un file viene modificato in entrambi i branch e le modifiche si sovrappongono, Pitmaster genera i marcatori di conflitto standard. `merge()` etichetta i due lati con `HEAD` e con il nome del branch risultante dal merge:

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

La classe `ConflictMarker` racchiude due versioni tra marcatori:

```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');
```

## Merge a tre vie a livello di contenuto

La classe `ThreeWayMerge` unisce il contenuto dei blob (non i file):

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

Accetta tre stringhe (la versione di base, la nostra versione e quella altrui), oltre a etichette facoltative e a uno stile di conflitto (`'merge'` per impostazione predefinita, oppure `'diff3'` per includere la sezione di base).

## Strategie di merge

### Merge ricorsivo

La strategia predefinita (`$repo->merge($branch)`) gestisce le cronologie di merge incrociate con più basi di merge. Anche CLI accetta `-s ort` e `-s recursive`.

### Merge ours

Registra un commit di merge che lascia invariato l'albero di HEAD. Utile per mantenere la cronologia dei branch senza incorporare modifiche.

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

### Merge octopus

Unisce due o più branch in un unico commit di merge. Se un branch qualsiasi presenta conflitti, restituisce un risultato non pulito senza scrivere lo stato del merge né creare un commit.

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

Le classi `RecursiveMerge`, `OursMerge` e `OctopusMerge` in `Pitmaster\Merge` sono helper a livello di contenuto; `Repository` implementa autonomamente le strategie sopra descritte.

## Rerere (riutilizzo della risoluzione registrata)

Pitmaster include una classe `Rerere` autonoma che memorizza le risoluzioni nel formato `rr-cache` di 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()` non la chiama: le risoluzioni vengono registrate e riprodotte solo quando lo fa il vostro codice. `isEnabled()` cerca il testo letterale `rerere.enabled = true` in `.git/config` e non riconosce la sezione `[rerere]` scritta da `git config rerere.enabled true` ([#20](https://github.com/inline0/pitmaster/issues/20)).

## Flusso di merge a livello di albero

Il processo completo di merge:

1. `MergeBase::findAll()` individua gli antenati comuni
2. Gli alberi base, nostro e altrui vengono confrontati percorso per percorso
3. Per i file modificati in un solo branch: si applica quella modifica
4. Per i file modificati in entrambi i branch:
   - Se sono stati modificati nello stesso modo: si applica una delle due modifiche (sono identiche)
   - Se le modifiche differiscono: si esegue `ThreeWayMerge::merge()` sul contenuto
5. Se tutti i merge vanno a buon fine: si crea l'albero risultante e un commit di merge
6. Se ci sono conflitti: si restituisce `MergeResult` con `clean = false` e i percorsi in conflitto

## Utilizzo di CLI

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

Output in caso di successo:
```
Merge successful.
```

Output in caso di conflitto:
```
CONFLICT in: src/File.php, src/Other.php
```
