
# Referencia de CLI

merql incluye una CLI para capturar instantáneas, calcular diferencias y ejecutar fusiones directamente desde el terminal. Composer la instala como `vendor/bin/merql`; en una copia del repositorio de merql, es `bin/merql`.

`vendor/bin/merql` funciona tras instalar una dependencia porque merql 0.4.1. Las versiones anteriores buscaban el cargador automático de Composer solo en el directorio `vendor/` del paquete y fallaban cuando merql se instalaba como dependencia.

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

Una fusión de tres vías requiere capturar ours y theirs de dos bases de datos que divergieron de base. Indique a `MERQL_DB_DSN` la base de datos correcta para cada instantánea:

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

Tomar las tres instantáneas de la misma base de datos, una tras otra, no sirve para una fusión: theirs ya contendría los cambios de ours, por lo que cada fila insertada por ours se marcaría como un conflicto `insert_insert`.

## Variables de entorno

CLI lee los detalles de conexión a la base de datos de las variables de entorno. Puede conectarse mediante una cadena DSN de PDO o parámetros MySQL individuales.

### Conexión mediante DSN

Use `MERQL_DB_DSN` para conexiones basadas en PDO. Es el enfoque recomendado para SQLite y para configuraciones no estándar. El DSN debe apuntar a una base de datos MySQL o SQLite: CLI no puede registrar un controlador para otros motores.

| Variable | Descripción | Ejemplo |
|---|---|---|
| `MERQL_DB_DSN` | Cadena DSN de PDO completa | `sqlite:/path/to/db.sqlite` |
| `MERQL_DB_USER` | Nombre de usuario (opcional con DSN) | `root` |
| `MERQL_DB_PASS` | Contraseña (opcional con 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 una base de datos SQLite respaldada por un archivo. Cada comando CLI se ejecuta en su propio proceso, así que `sqlite::memory:` siempre abre una base de datos nueva y vacía, y la instantánea que se obtiene no contiene tablas.

### Forma abreviada de MySQL

Si no se establece `MERQL_DB_DSN`, la CLI recurre a las variables específicas de MySQL.

| Variable | Valor predeterminado | Descripción |
|---|---|---|
| `MERQL_DB_NAME` | (obligatorio) | Nombre de la base de datos |
| `MERQL_DB_HOST` | `127.0.0.1` | Host de MySQL |
| `MERQL_DB_PORT` | `3306` | Puerto de MySQL |
| `MERQL_DB_USER` | `root` | Nombre de usuario de MySQL |
| `MERQL_DB_PASS` | (vacío) | Contraseña de MySQL |
| `MERQL_DB_ADAPTER` | `pdo` | Establezca el valor `mysqli` para usar el 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 el estado actual de la base de datos indicada por las variables de entorno y guárdelo como una instantánea con nombre. Este es el único comando que se conecta a una base de datos.

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

Salida:

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

Las instantáneas se guardan como archivos JSON en `.merql/snapshots/`, dentro del directorio de trabajo (consulte [Almacenamiento de instantáneas](#snapshot-storage)). El nombre solo puede contener caracteres alfanuméricos, guiones y guiones bajos.

Flujo de trabajo habitual: capture una instantánea antes de hacer cambios y, después, capture otra. Las dos instantáneas sirven de base para una comparación o una fusión.

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

Compare dos instantáneas con nombre y muestre un resumen de las diferencias.

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

Salida:

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

Ambas instantáneas deben existir de antemano (capturadas previamente con `merql snapshot`). La comparación no requiere conexión a una base de datos, ya que opera sobre archivos de instantáneas guardados.

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

Realice una fusión de tres vías de tres instantáneas con nombre.

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

Salida:

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

Las tres instantáneas deben existir de antemano. La fusión no requiere conexión a una base de datos, ya que opera sobre archivos de instantáneas guardados, y nunca escribe en una base de datos: CLI no tiene un comando apply. Use PHP API para aplicar el resultado de la fusión.

CLI fusiona a nivel de columna. No aplica la fusión a nivel de celda a las columnas TEXT ni JSON, por lo que dos modificaciones distintas en la misma columna siempre generan un conflicto ([#7](https://github.com/inline0/merql/issues/7)). Para fusionar a nivel de celda, use `ThreeWayMerge` con un `CellMergeConfig` en PHP.

#### `--dry-run`

Añada la opción `--dry-run` para previsualizar las instrucciones SQL que generaría la fusión, sin ejecutar nada. Las instrucciones describen cómo transformar el estado base en el resultado fusionado, por lo que incluyen los cambios de ambos lados.

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

Salida:

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

Una copia del repositorio de merql incluye más scripts en `bin/` para su conjunto de pruebas basado en un oráculo. No forman parte del paquete Composer, por lo que al instalarlo como dependencia solo se dispone de `vendor/bin/merql`. Ejecútelos desde la raíz del repositorio después de `composer install`.

| Comando | Descripción |
|---|---|
| `./bin/oracle <name>` | Calcular el resultado esperado de una fusión para un escenario |
| `./bin/actual <name>` | Ejecutar merql en un escenario y capturar la salida |
| `./bin/compare <name>` | Comparar la salida del oráculo con la real |
| `./bin/test-scenario <name>` | Flujo completo: configuración, oráculo, ejecución y comparación |
| `./bin/test-regression` | Ejecutar todos los escenarios |
| `./bin/test-regression --category clean` | Ejecutar una categoría: `clean`, `conflict`, `identity`, `types`, `scale` o `edge` |
| `./bin/test-regression --fast` | Solo aprobado/suspenso, sin informes |
| `./bin/verify-compliance` | Informe completo de cumplimiento |
| `./bin/verify-all` | PHPStan + estilo de código + PHPUnit + regresión del oráculo |

`bin/test-regression` también acepta `--jobs`, pero por ahora ignora el valor y ejecuta los escenarios uno tras otro ([#5](https://github.com/inline0/merql/issues/5)). Si se indica un `--category` desconocido, se muestra `No scenarios found` y se sale correctamente.

Consulte [Estrategia de pruebas](https://merql.inline0.dev/es/docs/advanced/testing) para ver la estructura de los escenarios.

## Estado de salida

`merql snapshot`, `diff` y `merge` salen con el código 1 si falta un argumento; `snapshot` también lo hace si no hay ninguna conexión configurada. Si falta una instantánea, el comando termina con una `SnapshotException` no capturada y un código distinto de cero. Si se indica un comando desconocido, se muestra `Unknown command`, pero por ahora se sale con el código 0 ([#3](https://github.com/inline0/merql/issues/3)).

## Almacenamiento de instantáneas

Las instantáneas se guardan en `.merql/snapshots/`, en relación con el directorio de trabajo. Cada instantánea es un único archivo JSON llamado `<name>.json`. Ejecute todos los comandos desde el mismo directorio para que encuentren las instantáneas de los demás.

CLI no ofrece ninguna opción ni variable de entorno para especificar otra ubicación ([#8](https://github.com/inline0/merql/issues/8)). Para usar otra, cambie a un directorio de trabajo distinto antes de ejecutarlo. En PHP, establezca el directorio antes de capturar o cargar instantáneas:

```php
use Merql\Snapshot\SnapshotStore;

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

Los archivos de instantáneas son portátiles. Puede copiarlos entre máquinas, confirmarlos en el control de versiones o compartirlos para depurar en equipo. Una instantánea capturada por CLI se puede cargar en PHP con `SnapshotStore::load()`, y viceversa, siempre que ambos usen el mismo directorio.
