
# مرجع 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 تفاصيل اتصال قاعدة البيانات من متغيرات البيئة. ويمكنك الاتصال باستخدام سلسلة PDO DSN أو معلمات MySQL منفردة.

### الاتصال باستخدام DSN

استخدم `MERQL_DB_DSN` للاتصالات المعتمدة على PDO. وهذا هو النهج الموصى به لـ SQLite والإعدادات غير القياسية. يجب أن يشير DSN إلى قاعدة بيانات MySQL أو SQLite: فلا توجد طريقة لدى CLI لتسجيل برنامج تشغيل لمحركات أخرى.

| المتغير | الوصف | المثال |
|---|---|---|
| `MERQL_DB_DSN` | سلسلة PDO DSN كاملة | `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/ar/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()`، والعكس صحيح، ما دام الاثنان يستخدمان الدليل نفسه.
