
# Référence CLI

merql inclut une CLI pour capturer des instantanés, calculer des différences et exécuter des fusions directement depuis le terminal. Composer l'installe sous le nom `vendor/bin/merql` ; dans une copie du dépôt merql, il s'agit de `bin/merql`.

`vendor/bin/merql` fonctionne après une installation de dépendance, car merql 0.4.1. Les versions précédentes recherchaient le chargeur automatique Composer uniquement dans le répertoire `vendor/` propre au paquet et échouaient lorsque merql était installé comme dépendance.

## Démarrage rapide

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

Une fusion à trois voies nécessite que les instantanés « ours » et « theirs » proviennent de deux bases de données qui ont divergé à partir de « base ». Indiquez à `MERQL_DB_DSN` la base de données appropriée pour chaque instantané :

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

Prendre les trois instantanés de la même base de données l'un après l'autre ne fonctionne pas pour une fusion : « theirs » contiendrait déjà les changements de « ours », et chaque ligne insérée par « ours » serait signalée comme un conflit `insert_insert`.

## Variables d’environnement

CLI lit les détails de connexion à la base de données dans les variables d'environnement. Vous pouvez vous connecter à l'aide d'une chaîne DSN PDO ou de paramètres MySQL individuels.

### Connexion basée sur DSN

Utilisez `MERQL_DB_DSN` pour les connexions basées sur PDO. C'est l'approche recommandée pour SQLite et les configurations non standard. Le DSN doit pointer vers une base de données MySQL ou SQLite : CLI ne peut pas enregistrer de pilote pour d'autres moteurs.

| Variable | Description | Exemple |
|---|---|---|
| `MERQL_DB_DSN` | Chaîne DSN PDO complète | `sqlite:/path/to/db.sqlite` |
| `MERQL_DB_USER` | Nom d'utilisateur (facultatif avec DSN) | `root` |
| `MERQL_DB_PASS` | Mot de passe (facultatif avec 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"
```

Utilisez une base de données SQLite adossée à un fichier. Chaque commande CLI s'exécute dans son propre processus ; `sqlite::memory:` ouvre donc toujours une nouvelle base de données vide, dont l'instantané ne contient aucune table.

### Raccourci MySQL

Lorsque `MERQL_DB_DSN` n'est pas défini, CLI utilise les variables propres à MySQL en solution de repli.

| Variable | Valeur par défaut | Description |
|---|---|---|
| `MERQL_DB_NAME` | (requis) | Nom de la base de données |
| `MERQL_DB_HOST` | `127.0.0.1` | Hôte MySQL |
| `MERQL_DB_PORT` | `3306` | Port MySQL |
| `MERQL_DB_USER` | `root` | Nom d'utilisateur MySQL |
| `MERQL_DB_PASS` | (vide) | Mot de passe MySQL |
| `MERQL_DB_ADAPTER` | `pdo` | Définissez sur `mysqli` pour utiliser l'adaptateur 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"
```

## Commandes

### `merql snapshot <name>`

Capturez l'état actuel de la base de données désignée par les variables d'environnement et enregistrez-le sous forme d'instantané nommé. C'est la seule commande qui se connecte à une base de données.

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

Sortie :

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

Les instantanés sont enregistrés sous forme de fichiers JSON dans `.merql/snapshots/`, sous le répertoire de travail (voir [Stockage des instantanés](#snapshot-storage)). Le nom ne doit contenir que des caractères alphanumériques, des tirets et des traits de soulignement.

Workflow courant : capturez un instantané avant d'effectuer des modifications, puis capturez-en un autre après. Les deux instantanés servent de base à une comparaison ou à une fusion.

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

Comparez deux instantanés nommés et affichez un résumé des différences.

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

Sortie :

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

Les deux instantanés doivent déjà exister (avoir été capturés précédemment avec `merql snapshot`). La comparaison ne nécessite pas de connexion à une base de données, car elle utilise les fichiers d'instantané enregistrés.

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

Effectuez une fusion à trois voies de trois instantanés nommés.

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

Sortie :

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

Les trois instantanés doivent déjà exister. La fusion ne nécessite pas de connexion à une base de données, car elle utilise les fichiers d'instantané enregistrés, et elle n'écrit jamais dans une base de données : la CLI ne possède pas de commande d'application. Utilisez la PHP API pour appliquer un résultat de fusion.

La CLI effectue les fusions au niveau des colonnes. Elle ne fusionne pas les cellules des colonnes TEXT ou JSON ; deux modifications différentes de la même colonne constituent donc toujours un conflit ([#7](https://github.com/inline0/merql/issues/7)). Utilisez `ThreeWayMerge` avec une `CellMergeConfig` dans PHP pour fusionner au niveau des cellules.

#### `--dry-run`

Ajoutez l'option `--dry-run` pour prévisualiser les instructions SQL que la fusion générerait, sans rien exécuter. Ces instructions décrivent comment transformer l'état de base en résultat fusionné ; elles incluent donc les changements des deux côtés.

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

Sortie :

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

## Commandes de développement

Une copie du dépôt merql contient davantage de scripts dans `bin/` pour sa suite de tests pilotée par oracle. Ils ne font pas partie du paquet Composer ; une installation de dépendance ne fournit donc que `vendor/bin/merql`. Exécutez-les depuis la racine du dépôt après `composer install`.

| Commande | Description |
|---|---|
| `./bin/oracle <name>` | Calculer le résultat de fusion attendu pour un scénario |
| `./bin/actual <name>` | Exécuter merql pour un scénario et capturer la sortie |
| `./bin/compare <name>` | Comparer la sortie de l'oracle à la sortie réelle |
| `./bin/test-scenario <name>` | Pipeline complet : configuration, oracle, exécution, comparaison |
| `./bin/test-regression` | Exécuter tous les scénarios |
| `./bin/test-regression --category clean` | Exécuter une catégorie : `clean`, `conflict`, `identity`, `types`, `scale` ou `edge` |
| `./bin/test-regression --fast` | Réussite ou échec seulement, sans rapports |
| `./bin/verify-compliance` | Rapport de conformité complet |
| `./bin/verify-all` | PHPStan + style de code + PHPUnit + régression de l'oracle |

`bin/test-regression` accepte aussi `--jobs`, mais ignore actuellement la valeur et exécute les scénarios l'un après l'autre ([#5](https://github.com/inline0/merql/issues/5)). Une valeur `--category` inconnue affiche `No scenarios found` et se termine avec succès.

Voir [Stratégie de test](https://merql.inline0.dev/fr/docs/advanced/testing) pour la structure des scénarios.

## Code de sortie

`merql snapshot`, `diff` et `merge` se terminent avec le code 1 lorsqu'un argument est manquant, et `snapshot` le fait aussi lorsqu'aucune connexion n'est configurée. Un instantané manquant interrompt la commande avec une `SnapshotException` non interceptée et un code différent de zéro. Une commande inconnue affiche `Unknown command`, mais se termine actuellement avec le code 0 ([#3](https://github.com/inline0/merql/issues/3)).

## Stockage des instantanés

Les instantanés sont stockés dans `.merql/snapshots/` par rapport au répertoire de travail. Chaque instantané est un fichier JSON unique nommé `<name>.json`. Exécutez toutes les commandes depuis le même répertoire afin qu'elles trouvent les instantanés des autres.

La CLI ne propose aucune option ni variable d'environnement pour choisir un autre emplacement ([#8](https://github.com/inline0/merql/issues/8)). Pour en utiliser un autre, changez de répertoire de travail avant de l'exécuter. Dans PHP, définissez le répertoire avant de capturer ou de charger des instantanés :

```php
use Merql\Snapshot\SnapshotStore;

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

Les fichiers d'instantané sont portables. Vous pouvez les copier d'une machine à l'autre, les valider dans le contrôle de version ou les partager pour déboguer en collaboration. Un instantané capturé par la CLI peut être chargé dans PHP avec `SnapshotStore::load()`, et inversement, à condition que les deux utilisent le même répertoire.
