
# Referência de CLI

merql inclui uma CLI para capturar snapshots, calcular diferenças e executar mesclagens diretamente pelo terminal. Composer a instala como `vendor/bin/merql`; em um checkout do repositório merql, ela é `bin/merql`.

`vendor/bin/merql` funciona a partir de uma instalação de dependência porque merql 0.4.1. Versões anteriores procuravam o autoloader Composer apenas no diretório `vendor/` do próprio pacote e falhavam quando merql era instalado como dependência.

## Início rápido

```bash
# Capture a baseline from your database.
export MERQL_DB_DSN="sqlite:/path/to/db.sqlite"
vendor/bin/merql snapshot base

# Make some changes, then capture again.
vendor/bin/merql snapshot current

# See what changed.
vendor/bin/merql diff base current
```

Uma mesclagem de três vias exige que ours e theirs sejam capturados de dois bancos de dados que divergiram da base. Aponte `MERQL_DB_DSN` para o banco de dados correto para cada snapshot:

```bash
# Base: live before the sandbox was copied from it.
MERQL_DB_DSN="sqlite:/data/live.sqlite" vendor/bin/merql snapshot base

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

MERQL_DB_DSN="sqlite:/data/sandbox.sqlite" vendor/bin/merql snapshot ours
MERQL_DB_DSN="sqlite:/data/live.sqlite" vendor/bin/merql snapshot theirs

# Three-way merge with dry-run preview.
vendor/bin/merql merge base ours theirs --dry-run
```

Capturar os três snapshots do mesmo banco de dados, um após o outro, não funciona como mesclagem: theirs já conteria as alterações de ours, então cada linha inserida por ours seria relatada como um conflito `insert_insert`.

## Variáveis de ambiente

O CLI lê os detalhes da conexão do banco de dados a partir de variáveis de ambiente. Você pode
conectar usando uma string DSN do PDO ou parâmetros MySQL individuais.

### Conexão baseada em DSN

Use `MERQL_DB_DSN` para conexões baseadas em PDO. Essa é a abordagem recomendada
para SQLite e para configurações não padrão. O DSN deve apontar para um banco de dados MySQL
ou SQLite: o CLI não tem como registrar um driver para outros mecanismos.

| Variável | Descrição | Exemplo |
|---|---|---|
| `MERQL_DB_DSN` | String DSN completa do PDO | `sqlite:/path/to/db.sqlite` |
| `MERQL_DB_USER` | Nome de usuário (opcional com DSN) | `root` |
| `MERQL_DB_PASS` | Senha (opcional com DSN) | `secret` |

```bash
# SQLite file.
export MERQL_DB_DSN="sqlite:/var/data/myapp.sqlite"

# MySQL via DSN.
export MERQL_DB_DSN="mysql:host=127.0.0.1;dbname=mydb;charset=utf8mb4"
export MERQL_DB_USER="root"
export MERQL_DB_PASS="secret"
```

Use um banco de dados SQLite baseado em arquivo. Cada comando CLI é executado em seu próprio processo, portanto
`sqlite::memory:` sempre abre um banco de dados novo e vazio, e um snapshot dele
não contém tabelas.

### Atalho de MySQL

Quando `MERQL_DB_DSN` não está definido, o CLI recorre às variáveis específicas de MySQL.

| Variável | Padrão | Descrição |
|---|---|---|
| `MERQL_DB_NAME` | (obrigatório) | Nome do banco de dados |
| `MERQL_DB_HOST` | `127.0.0.1` | Host de MySQL |
| `MERQL_DB_PORT` | `3306` | Porta de MySQL |
| `MERQL_DB_USER` | `root` | Nome de usuário de MySQL |
| `MERQL_DB_PASS` | (vazio) | Senha de MySQL |
| `MERQL_DB_ADAPTER` | `pdo` | Defina como `mysqli` para usar o adaptador mysqli |

```bash
export MERQL_DB_NAME="wordpress"
export MERQL_DB_HOST="127.0.0.1"
export MERQL_DB_PORT="3306"
export MERQL_DB_USER="root"
export MERQL_DB_PASS="password"
```

## Comandos

### `merql snapshot <name>`

Capture o estado atual do banco de dados indicado pelas variáveis de ambiente e salve-o como um snapshot nomeado. Este é o único comando que se conecta a um banco de dados.

```bash
vendor/bin/merql snapshot baseline
```

Saída:

```
Snapshot 'baseline' saved
Tables: posts, comments, users, settings
```

Os snapshots são persistidos como arquivos JSON em `.merql/snapshots/` no diretório de trabalho (consulte [Armazenamento de snapshots](#snapshot-storage)). O nome deve conter apenas caracteres alfanuméricos, hífens e sublinhados.

Fluxo de trabalho típico: capture um snapshot antes de fazer alterações e depois capture outro. Os dois snapshots formam a base de uma comparação ou mesclagem.

### `merql diff <base> <current>`

Compare dois snapshots nomeados e exiba um resumo das diferenças.

```bash
vendor/bin/merql diff baseline current
```

Saída:

```
Changeset: baseline -> current
  Inserts: 3
  Updates: 7
  Deletes: 1
```

Os dois snapshots já devem existir (capturados anteriormente com `merql snapshot`). A comparação não exige conexão com um banco de dados, pois opera em arquivos de snapshot salvos.

### `merql merge <base> <ours> <theirs>`

Faça uma mesclagem de três vias de três snapshots nomeados.

```bash
vendor/bin/merql merge base ours theirs
```

Saída:

```
Merge: base + ours + theirs
  Operations: 12
  Conflicts:  0
  Clean:      yes
```

Os três snapshots já devem existir. A mesclagem não exige conexão com um banco de dados, pois opera em arquivos de snapshot salvos, e nunca grava em um banco de dados: a CLI não tem comando de aplicação. Use o PHP API para aplicar um resultado de mesclagem.

O CLI mescla no nível da coluna. Ele não aplica mesclagem no nível da célula às colunas TEXT ou JSON, portanto duas edições diferentes na mesma coluna sempre geram um conflito aqui ([#7](https://github.com/inline0/merql/issues/7)). Use `ThreeWayMerge` com um `CellMergeConfig` em PHP para mesclagem no nível da célula.

#### `--dry-run`

Adicione a opção `--dry-run` para visualizar as instruções SQL que a mesclagem geraria, sem executar nada. As instruções descrevem como transformar o estado base no resultado mesclado, portanto incluem as alterações dos dois lados.

```bash
vendor/bin/merql merge base ours theirs --dry-run
```

Saída:

```
Merge: base + ours + theirs
  Operations: 3
  Conflicts:  0
  Clean:      yes

Dry run SQL:
  INSERT INTO `posts` (`id`, `title`, `status`) VALUES ('10', 'New Post', 'draft');
  UPDATE `posts` SET `title` = 'Updated Title' WHERE `id` = '5';
  DELETE FROM `comments` WHERE `id` = '3';
```

## Comandos de desenvolvimento

Um checkout do repositório merql tem mais scripts em `bin/` para sua suíte de testes orientada por oráculo. Eles não fazem parte do pacote Composer, portanto uma instalação de dependência só tem `vendor/bin/merql`. Execute-os na raiz do repositório depois de `composer install`.

| Comando | Descrição |
|---|---|
| `./bin/oracle <name>` | Calcula o resultado esperado da mesclagem para um cenário |
| `./bin/actual <name>` | Executa merql em um cenário e captura a saída |
| `./bin/compare <name>` | Compara a saída do oráculo com a saída real |
| `./bin/test-scenario <name>` | Fluxo completo: configuração, oráculo, execução, comparação |
| `./bin/test-regression` | Executa todos os cenários |
| `./bin/test-regression --category clean` | Executa uma categoria: `clean`, `conflict`, `identity`, `types`, `scale` ou `edge` |
| `./bin/test-regression --fast` | Somente aprovação/reprovação, sem relatórios |
| `./bin/verify-compliance` | Relatório completo de conformidade |
| `./bin/verify-all` | PHPStan + estilo de código + PHPUnit + regressão do oráculo |

`bin/test-regression` também aceita `--jobs`, mas atualmente ignora o valor e executa os cenários um após o outro ([#5](https://github.com/inline0/merql/issues/5)). Uma opção `--category` desconhecida exibe `No scenarios found` e termina com sucesso.

Consulte [Estratégia de testes](https://merql.inline0.dev/pt/docs/advanced/testing) para ver a estrutura dos cenários.

## Código de saída

`merql snapshot`, `diff` e `merge` terminam com status 1 quando falta um argumento, assim como `snapshot` quando nenhuma conexão está configurada. A falta de um snapshot encerra o comando com uma exceção `SnapshotException` não capturada e status diferente de zero. Um comando desconhecido exibe `Unknown command`, mas atualmente termina com status 0 ([#3](https://github.com/inline0/merql/issues/3)).

## Armazenamento de snapshots

Os snapshots são armazenados em `.merql/snapshots/` em relação ao diretório de trabalho. Cada snapshot é um único arquivo JSON chamado `<name>.json`. Execute todos os comandos no mesmo diretório para que encontrem os snapshots uns dos outros.

A CLI não tem opção nem variável de ambiente para outro local ([#8](https://github.com/inline0/merql/issues/8)). Para usar outro local, mude para um diretório de trabalho diferente antes de executá-la. Em PHP, defina o diretório antes de capturar ou carregar snapshots:

```php
use Merql\Snapshot\SnapshotStore;

SnapshotStore::setDirectory('/var/data/merql-snapshots');
```

Os arquivos de snapshot são portáteis. Você pode copiá-los entre máquinas, incluí-los no controle de versão ou compartilhá-los para depuração colaborativa. Um snapshot capturado pela CLI pode ser carregado em PHP com `SnapshotStore::load()`, e vice-versa, desde que ambos usem o mesmo diretório.
