
# Справочник CLI

В merql входит CLI для создания снимков, вычисления различий и выполнения слияний прямо из терминала. Composer устанавливает его как `vendor/bin/merql`; в клоне репозитория merql это `bin/merql`.

`vendor/bin/merql` работает и при установке как зависимости, поскольку merql 0.4.1. В предыдущих выпусках автозагрузчик Composer искался только в каталоге `vendor/` самого пакета, из-за чего merql не работал при установке как зависимость.

## Быстрый старт

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

Для слияния трёх ветвей нужны снимки ours и theirs из двух баз данных, разошедшихся относительно base. Укажите для каждого снимка соответствующую базу данных в `MERQL_DB_DSN`:

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

Последовательное создание всех трёх снимков из одной базы данных не подходит для слияния: снимок theirs уже будет содержать изменения ours, поэтому каждая добавленная ours строка будет отмечена как конфликт `insert_insert`.

## Переменные среды

CLI читает сведения о подключении к базе данных из переменных среды. Подключиться можно с помощью строки DSN PDO или отдельных параметров MySQL.

### Подключение через DSN

Используйте `MERQL_DB_DSN` для подключений через PDO. Это рекомендуемый способ для SQLite и нестандартных конфигураций. DSN должен указывать на базу данных MySQL или SQLite: CLI не умеет регистрировать драйверы для других движков.

| Переменная | Описание | Пример |
|---|---|---|
| `MERQL_DB_DSN` | Полная строка DSN PDO | `sqlite:/path/to/db.sqlite` |
| `MERQL_DB_USER` | Имя пользователя (необязательно при использовании DSN) | `root` |
| `MERQL_DB_PASS` | Пароль (необязательно при использовании 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"
```

Используйте базу данных SQLite, хранящуюся в файле. Каждая команда CLI запускается в отдельном процессе, поэтому `sqlite::memory:` всегда открывает новую пустую базу данных, и её снимок не содержит таблиц.

### Сокращённая запись MySQL

Если `MERQL_DB_DSN` не задана, CLI использует переменные, специфичные для MySQL.

| Переменная | Значение по умолчанию | Описание |
|---|---|---|
| `MERQL_DB_NAME` | (обязательно) | Имя базы данных |
| `MERQL_DB_HOST` | `127.0.0.1` | Хост MySQL |
| `MERQL_DB_PORT` | `3306` | Порт MySQL |
| `MERQL_DB_USER` | `root` | Имя пользователя MySQL |
| `MERQL_DB_PASS` | (пусто) | Пароль MySQL |
| `MERQL_DB_ADAPTER` | `pdo` | Установите значение `mysqli`, чтобы использовать адаптер 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"
```

## Команды

### `merql snapshot <name>`

Сохраните текущее состояние базы данных, имя которой задано переменными среды, в виде именованного снимка. Это единственная команда, которая подключается к базе данных.

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

Вывод:

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

Снимки по умолчанию сохраняются в виде файлов JSON в каталоге `.merql/snapshots/` внутри рабочей директории (см. раздел [Хранение снимков](#snapshot-storage)). Имя может содержать только буквенно-цифровые символы, дефисы и подчёркивания.

Обычный рабочий процесс: создайте снимок до внесения изменений, а затем ещё один после. Эти два снимка служат основой для сравнения или слияния.

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

Сравните два именованных снимка и выведите сводку различий.

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

Вывод:

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

Оба снимка должны уже существовать (быть созданы с помощью `merql snapshot`). Для сравнения не требуется подключение к базе данных, поскольку оно работает с сохранёнными файлами снимков.

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

Выполните слияние трёх именованных снимков.

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

Вывод:

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

Все три снимка должны уже существовать. Для слияния не требуется подключение к базе данных, поскольку оно работает с сохранёнными файлами снимков и никогда не записывает данные в базу: у CLI нет команды применения. Используйте PHP API, чтобы применить результат слияния.

CLI выполняет слияние на уровне столбцов. Он не объединяет отдельные ячейки в столбцах TEXT или JSON, поэтому два разных изменения одного столбца всегда считаются конфликтом ([#7](https://github.com/inline0/merql/issues/7)). Для слияния на уровне ячеек используйте `ThreeWayMerge` с `CellMergeConfig` в PHP.

#### `--dry-run`

Добавьте флаг `--dry-run`, чтобы просмотреть инструкции SQL, которые сгенерировало бы слияние, не выполняя их. Инструкции описывают преобразование базового состояния в итоговое и включают изменения обеих сторон.

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

Вывод:

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

## Команды разработки

В клоне репозитория merql есть дополнительные скрипты в `bin/` для набора тестов с оракулом. Они не входят в пакет Composer, поэтому при установке как зависимости доступны только `vendor/bin/merql`. Запускайте их из корня репозитория после `composer install`.

| Команда | Описание |
|---|---|
| `./bin/oracle <name>` | Вычислить ожидаемый результат слияния для сценария |
| `./bin/actual <name>` | Запустить merql для сценария и сохранить вывод |
| `./bin/compare <name>` | Сравнить вывод оракула с фактическим |
| `./bin/test-scenario <name>` | Полный конвейер: настройка, оракул, фактический результат, сравнение |
| `./bin/test-regression` | Запустить все сценарии |
| `./bin/test-regression --category clean` | Запустить одну категорию: `clean`, `conflict`, `identity`, `types`, `scale` или `edge` |
| `./bin/test-regression --fast` | Только результат: пройдено или нет, без отчётов |
| `./bin/verify-compliance` | Полный отчёт о соответствии |
| `./bin/verify-all` | PHPStan + стиль кода + PHPUnit + регрессионные тесты оракула |

`bin/test-regression` также принимает `--jobs`, но пока игнорирует его значение и запускает сценарии по очереди ([#5](https://github.com/inline0/merql/issues/5)). Если указать неизвестное значение `--category`, выводится `No scenarios found`, а выполнение завершается успешно.

См. [Стратегия тестирования](https://merql.inline0.dev/ru/docs/advanced/testing), чтобы узнать структуру сценариев.

## Код завершения

Команды `merql snapshot`, `diff` и `merge` завершаются с кодом 1, если отсутствует аргумент; для команды `snapshot` это также происходит, если подключение не настроено. При отсутствии снимка команда завершается из-за необработанного исключения `SnapshotException` с ненулевым кодом. Неизвестная команда выводит `Unknown command`, но пока завершается с кодом 0 ([#3](https://github.com/inline0/merql/issues/3)).

## Хранение снимков

Снимки хранятся в `.merql/snapshots/` относительно рабочей директории. Каждый снимок представляет собой отдельный файл JSON с именем `<name>.json`. Запускайте все команды из одной директории, чтобы они находили снимки друг друга.

У CLI нет параметра или переменной среды для указания другого расположения ([#8](https://github.com/inline0/merql/issues/8)). Чтобы использовать другую директорию, перейдите в неё перед запуском. В PHP укажите директорию перед созданием или загрузкой снимков:

```php
use Merql\Snapshot\SnapshotStore;

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

Файлы снимков переносимы. Их можно копировать между компьютерами, добавлять в систему контроля версий или передавать для совместной отладки. Снимок, созданный с помощью CLI, можно загрузить в PHP с помощью `SnapshotStore::load()` и наоборот, если обе программы используют одну директорию.
