
# Mesclagem de três vias

Uma mesclagem de três vias compara dois conjuntos de alterações com um ancestral comum e os combina em um único resultado. merql aplica esse algoritmo a linhas de banco de dados em vez de linhas de arquivos.

```php
use Merql\Merge\ThreeWayMerge;

$merge = new ThreeWayMerge();
$result = $merge->merge($base, $ours, $theirs);

echo $result->operationCount(); // resolved operations
echo $result->conflictCount();  // unresolved conflicts
```

## Os três estados

Toda mesclagem exige exatamente três snapshots:

- **Base** -- o estado do banco de dados no momento em que os dois branches divergiram.
- **Ours** -- um branch após suas alterações, por exemplo, uma cópia de sandbox que foi editada.
- **Theirs** -- o outro branch após suas alterações, por exemplo, o banco de dados em produção que continuou em funcionamento.

Ours e theirs devem ser capturados cada um do banco de dados que contém o respectivo branch. As regras de mesclagem tratam os dois lados da mesma forma; os nomes determinam a `source` de cada operação, como os tipos de conflito são nomeados (`update_delete` significa que ours atualizou e theirs excluiu) e qual lado `ConflictPolicy::OursWins` e `TheirsWins` favorecem.

```
         Base (snapshot at fork time)
        /                              \
   Ours (our changes)                   Theirs (their changes)
        \                              /
         --------- MERGE ----------
                     |
              Merged result
              + conflicts (if any)
```

Sem uma base comum, não é possível distinguir "adicionado" de "inalterado". A comparação de dois lados mostra que o lado A tem um valor e o lado B não, mas não consegue determinar se A o adicionou ou se B o excluiu. A base responde a essa pergunta.

## Como os conjuntos de alterações são calculados

O `Differ` compara cada snapshot com a base para gerar dois conjuntos de alterações. Cada conjunto contém três tipos de operação:

| Base | Atual | Operação |
|---|---|---|
| A linha existe | A linha existe, mesma impressão digital | Nenhuma alteração |
| A linha existe | A linha existe, impressão digital diferente | **UPDATE** (diferenças por coluna) |
| A linha existe | Linha ausente | **DELETE** |
| Linha ausente | A linha existe | **INSERT** |

As impressões digitais (hashes SHA-256 do conteúdo normalizado da linha) oferecem um caminho rápido. Se duas impressões digitais forem iguais, a linha não foi alterada e não é necessário comparar as colunas.

```php
use Merql\Diff\Differ;

$differ = new Differ();
$oursChangeset = $differ->diff($base, $ours);
$theirsChangeset = $differ->diff($base, $theirs);
```

## Regras de mesclagem

Em seguida, os dois conjuntos de alterações são mesclados operação por operação. Esta tabela define o resultado para cada combinação:

| Ours | Theirs | Resultado |
|---|---|---|
| Nenhuma alteração | Nenhuma alteração | Manter a base |
| Nenhuma alteração | Atualizada | Aceitar theirs |
| Atualizada | Nenhuma alteração | Aceitar ours |
| Atualizada (mesmo valor) | Atualizada (mesmo valor) | Aceitar (ambos concordam) |
| Atualizada (valor diferente) | Atualizada (valor diferente) | **Conflito** |
| Nenhuma alteração | Excluída | Aceitar exclusão |
| Excluída | Nenhuma alteração | Aceitar exclusão |
| Excluída | Excluída | Aceitar exclusão (ambos concordam) |
| Atualizada | Excluída | **Conflito** |
| Excluída | Atualizada | **Conflito** |
| Inserida (somente ours) | -- | Aceitar inserção |
| -- | Inserida (somente theirs) | Aceitar inserção |
| Inserida (mesma PK) | Inserida (mesma PK) | **Conflito** |

Quando os dois lados atualizam a mesma linha, merql não declara imediatamente um conflito. Ele passa à mesclagem no nível da coluna, comparando cada coluna de forma independente. Consulte [Mesclagem no nível da coluna](https://merql.inline0.dev/pt/docs/merge/column-level) para obter detalhes.

## Usando a facade

A facade `Merql` opera em snapshots nomeados que foram persistidos em disco. Chame `Merql::init()` com o banco de dados que contém cada estado antes de criar o snapshot:

```php
use Merql\Connection;
use Merql\Merql;

$live    = Connection::sqlite('/data/live.sqlite');
$sandbox = Connection::sqlite('/data/sandbox.sqlite');

Merql::init($live);
Merql::snapshot('base');      // before the sandbox is copied from live

// ... copy live.sqlite to sandbox.sqlite, then both databases change ...

Merql::init($sandbox);
Merql::snapshot('ours');
Merql::init($live);
Merql::snapshot('theirs');

$result = Merql::merge('base', 'ours', 'theirs');
```

Não crie os três snapshots em sequência a partir de um único banco de dados. Nesse caso, theirs também conteria as alterações de ours, e toda linha inserida por ours apareceria como inserida nos dois lados e seria relatada como um conflito `insert_insert`.

`Merql::merge()`, `Merql::patch()` e `Merql::plan()` criam um `ThreeWayMerge` sem um `CellMergeConfig`; portanto, mesclam apenas no nível da coluna. Para mesclar no nível das células, carregue os snapshots e use diretamente a classe, como abaixo.

## Usando as classes diretamente

Para ter mais controle, instancie os componentes por conta própria. `Snapshotter::capture()` não persiste nada; portanto, mantenha os snapshots na memória ou salve-os com `SnapshotStore::save()`:

```php
use Merql\CellMerge\CellMergeConfig;
use Merql\Merge\ThreeWayMerge;
use Merql\Snapshot\Snapshotter;
use Merql\Snapshot\SnapshotStore;

$base   = (new Snapshotter($live))->capture('base');
// ... copy live to the sandbox, then both databases change ...
$ours   = (new Snapshotter($sandbox))->capture('ours');
$theirs = (new Snapshotter($live))->capture('theirs');

$merge = new ThreeWayMerge();
$result = $merge->merge($base, $ours, $theirs);

// Snapshots saved earlier, merged with cell-level merge for TEXT and JSON columns.
$merge = new ThreeWayMerge(CellMergeConfig::auto());
$result = $merge->merge(
    SnapshotStore::load('base'),
    SnapshotStore::load('ours'),
    SnapshotStore::load('theirs'),
);
```

## O atalho de patch

Quando apenas um lado tem alterações (sem modificações simultâneas), use `patch()`. Esta é uma mesclagem de dois lados em que ours é igual à base, portanto, não há possibilidade de conflitos:

```php
$result = $merge->patch($base, $changes);
// Always clean: ours is identical to base.
```

Pela facade:

```php
$result = Merql::patch('base', 'changes');
```

`patch()` é equivalente a `merge($base, $base, $changes)`. Como ours nunca divergiu da base, todas as alterações de theirs são aceitas sem conflito.

## Detecção de incompatibilidades de esquema

Se colunas forem adicionadas ou removidas de uma tabela entre snapshots, a mesclagem ainda será executada, mas registrará as incompatibilidades. Tipos de coluna alterados não são comparados, e uma coluna renomeada aparece como uma removida e outra adicionada; consulte [Validação de esquema](https://merql.inline0.dev/pt/docs/advanced/schema).

```php
$result = $merge->merge($base, $ours, $theirs);

if ($result->hasSchemaMismatches()) {
    foreach ($result->schemaMismatches() as $mismatch) {
        echo $mismatch->getMessage() . "\n";
    }
}
```

merql não migra esquemas automaticamente. As alterações de esquema são responsabilidade de quem faz a chamada. A mesclagem as informa para que você decida como prosseguir.

## O MergeResult

O objeto de resultado contém tudo de que você precisa:

```php
$result->isClean();            // true if no conflicts
$result->operations();         // list of MergeOperation objects
$result->conflicts();          // list of Conflict objects
$result->operationCount();     // number of resolved operations
$result->conflictCount();      // number of unresolved conflicts
$result->hasSchemaMismatches(); // true if schemas differ
$result->baseSnapshot();       // the base snapshot used for the merge
```

Cada `MergeOperation` tem um tipo (`insert`, `update` ou `delete`), um nome de tabela, uma chave de linha, os valores das colunas e uma origem (`ours`, `theirs` ou `merged`). `merged` indica uma linha atualizada pelos dois lados (com as colunas combinadas individualmente) ou excluída pelos dois lados.

## Onde aplicar um resultado de mesclagem

Um resultado de mesclagem descreve como transformar a base no estado mesclado. Suas operações contêm as alterações dos dois lados: tanto uma inserção de ours quanto uma inserção de theirs aparecem como inserções.

- `Merql::apply()` (ou `Applier`) executa cada operação no banco de dados conectado. É aplicado sem problemas a um banco de dados que ainda contém a base, como uma cópia nova do estado da base. Se for aplicado a um banco de dados que já contém as alterações de um dos lados, uma inserção de uma linha que já existe falha e a transação é revertida.
- Para levar as alterações de ours a um banco de dados que já contém theirs, como ao promover edições da sandbox para produção, aplique apenas as operações que faltam nesse banco: as de origem `ours` e as atualizações `merged`. Use um [plano de mesclagem](https://merql.inline0.dev/pt/docs/merge/plans) para selecioná-las e [Aplicação protegida](https://merql.inline0.dev/pt/docs/apply/guarded) para gravá-las com pré-condições sobre as linhas ativas.
