
# CLI Referenz

merql enthält eine CLI, mit der du Snapshots erstellen, Unterschiede berechnen und Zusammenführungen direkt im Terminal ausführen kannst. Composer installiert sie unter `vendor/bin/merql`; in einem Checkout des merql-Repositorys findest du sie unter `bin/merql`.

`vendor/bin/merql` funktioniert seit merql 0.4.1 auch bei der Installation als Abhängigkeit. Frühere Versionen suchten den Composer-Autoloader nur im eigenen `vendor/`-Verzeichnis des Pakets und schlugen fehl, wenn merql als Abhängigkeit installiert war.

## Schnellstart

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

Für eine Drei-Wege-Zusammenführung müssen ours und theirs von zwei Datenbanken stammen, die sich ausgehend von base auseinanderentwickelt haben. Verweise `MERQL_DB_DSN` für jeden Snapshot auf die passende Datenbank:

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

Alle drei Snapshots nacheinander aus derselben Datenbank zu erstellen, funktioniert nicht als Zusammenführung: theirs würde die Änderungen von ours bereits enthalten, sodass jede von ours eingefügte Zeile als `insert_insert`-Konflikt gemeldet würde.

## Umgebungsvariablen

CLI liest Verbindungsdaten zur Datenbank aus Umgebungsvariablen. Du kannst
entweder eine PDO-DSN-Zeichenfolge oder einzelne MySQL-Parameter verwenden.

### Verbindung über DSN

Verwende `MERQL_DB_DSN` für PDO-basierte Verbindungen. Das ist der empfohlene Ansatz
für SQLite und nicht standardmäßige Konfigurationen. Die DSN muss auf eine MySQL-
oder SQLite-Datenbank verweisen: Der CLI kann keine Treiber für andere Engines registrieren.

| Variable | Beschreibung | Beispiel |
|---|---|---|
| `MERQL_DB_DSN` | Vollständige PDO-DSN-Zeichenfolge | `sqlite:/path/to/db.sqlite` |
| `MERQL_DB_USER` | Benutzername (optional mit DSN) | `root` |
| `MERQL_DB_PASS` | Passwort (optional mit 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"
```

Verwende eine dateibasierte SQLite-Datenbank. Jeder CLI-Befehl läuft in einem eigenen Prozess, daher
öffnet `sqlite::memory:` immer eine neue, leere Datenbank und ein Snapshot davon
enthält keine Tabellen.

### MySQL-Kurzform

Wenn `MERQL_DB_DSN` nicht gesetzt ist, greift CLI auf MySQL-spezifische Variablen zurück.

| Variable | Standardwert | Beschreibung |
|---|---|---|
| `MERQL_DB_NAME` | (erforderlich) | Datenbankname |
| `MERQL_DB_HOST` | `127.0.0.1` | MySQL-Host |
| `MERQL_DB_PORT` | `3306` | MySQL-Port |
| `MERQL_DB_USER` | `root` | MySQL-Benutzername |
| `MERQL_DB_PASS` | (leer) | MySQL-Passwort |
| `MERQL_DB_ADAPTER` | `pdo` | Auf `mysqli` setzen, um den mysqli-Adapter zu verwenden |

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

## Befehle

### `merql snapshot <name>`

Erfasse den aktuellen Stand der Datenbank, die durch die Umgebungsvariablen angegeben ist, und speichere ihn als benannten Snapshot. Nur dieser Befehl stellt eine Verbindung zu einer Datenbank her.

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

Ausgabe:

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

Snapshots werden als JSON-Dateien in `.merql/snapshots/` im Arbeitsverzeichnis gespeichert (siehe [Snapshot-Speicherung](#snapshot-storage)). Der Name darf nur alphanumerische Zeichen, Bindestriche und Unterstriche enthalten.

Typischer Ablauf: Erstelle vor Änderungen einen Snapshot und danach einen weiteren. Die beiden Snapshots bilden die Grundlage für einen Vergleich oder eine Zusammenführung.

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

Vergleiche zwei benannte Snapshots und zeige eine Zusammenfassung der Unterschiede an.

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

Ausgabe:

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

Beide Snapshots müssen bereits vorhanden sein (zuvor mit `merql snapshot` erstellt). Der Vergleich benötigt keine Datenbankverbindung, da er mit gespeicherten Snapshot-Dateien arbeitet.

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

Führe drei benannte Snapshots zusammen.

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

Ausgabe:

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

Alle drei Snapshots müssen bereits vorhanden sein. Die Zusammenführung benötigt keine Datenbankverbindung, da sie mit gespeicherten Snapshot-Dateien arbeitet, und schreibt niemals in eine Datenbank: Die CLI hat keinen Anwendungsbefehl. Verwende PHP API, um ein Zusammenführungsergebnis anzuwenden.

CLI führt Zusammenführungen auf Spaltenebene aus. TEXT- oder JSON-Spalten werden nicht auf Zellenebene zusammengeführt. Daher führen zwei unterschiedliche Änderungen derselben Spalte hier immer zu einem Konflikt ([#7](https://github.com/inline0/merql/issues/7)). Verwende `ThreeWayMerge` mit einem `CellMergeConfig` in PHP für Zusammenführungen auf Zellenebene.

#### `--dry-run`

Füge das Flag `--dry-run` hinzu, um die SQL-Anweisungen anzuzeigen, die bei der Zusammenführung erzeugt würden, ohne etwas auszuführen. Die Anweisungen zeigen, wie der Basiszustand in das zusammengeführte Ergebnis überführt wird, und enthalten daher die Änderungen beider Seiten.

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

Ausgabe:

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

## Entwicklungsbefehle

Ein Checkout des merql-Repositorys enthält unter `bin/` weitere Skripte für die oracle-gesteuerte Testsuite. Sie sind nicht Teil des Composer-Pakets, daher steht bei einer Installation als Abhängigkeit nur `vendor/bin/merql` zur Verfügung. Führe sie nach `composer install` im Stammverzeichnis des Repositorys aus.

| Befehl | Beschreibung |
|---|---|
| `./bin/oracle <name>` | Erwartetes Zusammenführungsergebnis für ein Szenario berechnen |
| `./bin/actual <name>` | merql für ein Szenario ausführen und die Ausgabe erfassen |
| `./bin/compare <name>` | oracle- und tatsächliche Ausgabe vergleichen |
| `./bin/test-scenario <name>` | Vollständige Pipeline: Setup, oracle, tatsächliche Ausgabe, Vergleich |
| `./bin/test-regression` | Alle Szenarien ausführen |
| `./bin/test-regression --category clean` | Eine Kategorie ausführen: `clean`, `conflict`, `identity`, `types`, `scale` oder `edge` |
| `./bin/test-regression --fast` | Nur Erfolg oder Fehler, keine Berichte |
| `./bin/verify-compliance` | Vollständiger Konformitätsbericht |
| `./bin/verify-all` | PHPStan + Code-Stil + PHPUnit + oracle-Regression |

`bin/test-regression` akzeptiert auch `--jobs`, ignoriert den Wert derzeit jedoch und führt die Szenarien nacheinander aus ([#5](https://github.com/inline0/merql/issues/5)). Ein unbekanntes `--category` gibt `No scenarios found` aus und wird erfolgreich beendet.

Eine Beschreibung des Szenarioaufbaus findest du unter [Testing Strategy](https://merql.inline0.dev/de/docs/advanced/testing).

## Exit-Status

`merql snapshot`, `diff` und `merge` werden mit Status 1 beendet, wenn ein Argument fehlt; `snapshot` auch, wenn keine Verbindung konfiguriert ist. Fehlt ein Snapshot, endet der Befehl mit einem nicht abgefangenen `SnapshotException` und einem Status ungleich null. Ein unbekannter Befehl gibt `Unknown command` aus, wird derzeit aber mit Status 0 beendet ([#3](https://github.com/inline0/merql/issues/3)).

## Snapshot-Speicherort

Snapshots werden relativ zum Arbeitsverzeichnis unter `.merql/snapshots/` gespeichert. Jeder Snapshot ist eine einzelne JSON-Datei namens `<name>.json`. Führe alle Befehle im selben Verzeichnis aus, damit sie die Snapshots der anderen finden.

Für CLI gibt es keine Option und keine Umgebungsvariable für einen anderen Speicherort ([#8](https://github.com/inline0/merql/issues/8)). Wechsle vor dem Ausführen in ein anderes Arbeitsverzeichnis, um einen anderen Speicherort zu verwenden. Lege in PHP das Verzeichnis fest, bevor du Snapshots erstellst oder lädst:

```php
use Merql\Snapshot\SnapshotStore;

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

Snapshot-Dateien sind portabel. Du kannst sie zwischen Rechnern kopieren, in die Versionsverwaltung übernehmen oder zur gemeinsamen Fehlersuche teilen. Einen mit CLI erstellten Snapshot kannst du mit `SnapshotStore::load()` in PHP laden und umgekehrt, sofern beide dasselbe Verzeichnis verwenden.
