
# Fusión

Pitmaster realiza fusiones de ramas mediante una fusión de tres vías, como `git merge`. Detecta oportunidades de avance rápido, calcula las bases de fusión, realiza fusiones a nivel de árbol y de contenido, y genera marcadores de conflicto estándar cuando los archivos entran en conflicto.

## Fusión básica

```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 clase `MergeResult` es un objeto de valor de solo lectura:

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

Cuando la fusión se detiene por conflictos, el estado del conflicto se escribe en el índice y el árbol de trabajo. Resuelve los archivos, `add()` y llama a `$repo->mergeContinue()`, o llama a `$repo->mergeAbort()` para restaurar el estado anterior a la fusión.

Antes de fusionar, confirma o guarda en el stash los cambios locales: la fusión reconstruye el árbol de trabajo a partir del resultado y sobrescribe los cambios sin confirmar en los archivos bajo seguimiento ([#2](https://github.com/inline0/pitmaster/issues/2)).

## Fusiones de avance rápido

Cuando el HEAD actual es antecesor de la rama de destino, Pitmaster realiza una fusión de avance rápido: simplemente mueve HEAD al commit de destino sin crear un commit de fusión.

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

Aún no se detecta el caso inverso: fusionar una rama que HEAD ya contiene crea un nuevo commit de fusión con un árbol sin cambios, en lugar de indicar «Already up to date» ([#6](https://github.com/inline0/pitmaster/issues/6)).

## Fusión de tres vías

Cuando las ramas han divergido, Pitmaster realiza una fusión de tres vías:

1. Buscar la base de fusión (antecesor común más cercano)
2. Comparar los árboles de la base con el nuestro y con el de ellos
3. Para cada archivo modificado en ambas ramas, realizar una fusión a nivel de contenido
4. Crear un commit de fusión con dos padres

```
Before:              After merge:

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

## Base de fusión

La base de fusión es el antecesor común más reciente de dos commits. Pitmaster la encuentra recorriendo el grafo de commits. Si hay varias bases de fusión (historial cruzado), `merge()` las fusiona primero y usa el resultado como base.

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

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

Para acceder directamente al algoritmo `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);
```

## Comprobación de fusión de ramas

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

## Marcadores de conflicto

Cuando un archivo se modifica en ambas ramas y los cambios se solapan, Pitmaster genera marcadores de conflicto estándar. `merge()` etiqueta los lados como `HEAD` y con el nombre de la rama fusionada:

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

La clase `ConflictMarker` envuelve dos versiones con marcadores:

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

## Fusión de tres vías a nivel de contenido

La clase `ThreeWayMerge` fusiona el contenido de blobs (no archivos):

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

Toma tres cadenas (la versión base, nuestra versión y la versión de ellos), además de etiquetas opcionales y un estilo de conflicto (`'merge'` de forma predeterminada o `'diff3'` para incluir la sección base).

## Estrategias de fusión

### Fusión recursiva

La estrategia predeterminada (`$repo->merge($branch)`) gestiona historiales de fusiones cruzadas con varias bases de fusión. CLI también acepta `-s ort` y `-s recursive` para esta estrategia.

### Fusión nuestra

Registra un commit de fusión que mantiene sin cambios el árbol de HEAD. Es útil para conservar el historial de ramas sin incorporar cambios.

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

### Fusión octopus

Fusiona dos o más ramas en un único commit de fusión. Si hay conflictos en alguna rama, devuelve un resultado no limpio sin escribir el estado de fusión ni crear un commit.

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

Las clases `RecursiveMerge`, `OursMerge` y `OctopusMerge` de `Pitmaster\Merge` son auxiliares a nivel de contenido; `Repository` implementa por sí misma las estrategias anteriores.

## Rerere (reutilizar resolución registrada)

Pitmaster incluye una clase independiente `Rerere` que almacena resoluciones en la estructura `rr-cache` de 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()` no la llama: las resoluciones solo se registran y se vuelven a aplicar cuando el código lo indica. `isEnabled()` busca el texto literal `rerere.enabled = true` en `.git/config` y no reconoce la sección `[rerere]` que escribe `git config rerere.enabled true` ([#20](https://github.com/inline0/pitmaster/issues/20)).

## Flujo de fusión a nivel de árbol

El proceso completo de fusión:

1. `MergeBase::findAll()` localiza los antecesores comunes
2. Se comparan las rutas de los árboles base, nuestro y de ellos
3. Para los archivos modificados en una sola rama: se aplica ese cambio
4. Para los archivos modificados en ambas ramas:
   - Si ambos se modificaron del mismo modo: se aplica cualquiera (coinciden)
   - Si los cambios difieren: se ejecuta `ThreeWayMerge::merge()` sobre el contenido
5. Si todas las fusiones se completan sin conflictos: se crea el árbol fusionado y un commit de fusión
6. Si hay conflictos: se devuelve `MergeResult` con `clean = false` y las rutas en conflicto

## Uso de CLI

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

Salida si la operación tiene éxito:
```
Merge successful.
```

Salida en caso de conflicto:
```
CONFLICT in: src/File.php, src/Other.php
```
