
# PHP API

## `Pitmaster\Pitmaster`

Клас `Pitmaster\Pitmaster` — це статична точка входу. Він надає фабричні методи для відкриття, створення та клонування репозиторіїв, а також допоміжні методи виявлення.

```php
use Pitmaster\Pitmaster;
```

### `open`

```php
public static function open(string $path, array $options = []): Repository
```

Відкриває наявний репозиторій за шляхом `$path`. Шлях має вести до кореня робочого дерева (де міститься `.git/`). Також підтримуються пов’язані робочі дерева (де `.git` — файл із `gitdir: <path>`) і чисті репозиторії (де `$path` — це сам каталог `.git`).

`$options` приймає два логічні значення; для обох типове значення — `true`:

- `hooks`: запускати сценарії `.git/hooks`. Передайте `false`, щоб вимкнути їх для цього дескриптора.
- `processes`: дозволяти все, що запускає процес або відкриває мережевий транспорт. Передайте `false`, щоб вимкнути перехоплювачі, перехоплювач fsmonitor, clone, fetch, push і транспорт процесів SSH. Локальне зчитування й запис продовжують працювати.

Якщо шлях не є репозиторієм git, генерується `InvalidArgumentException`.

```php
$repo = Pitmaster::open('/home/user/project');
$repo = Pitmaster::open('/home/user/project', ['hooks' => false]);
$repo = Pitmaster::open('/home/user/project', Pitmaster::processFreeOptions());
```

### `init`

```php
public static function init(string $path, string $objectFormat = 'sha1', array $options = []): Repository
```

Ініціалізує новий репозиторій за шляхом `$path`. Створює структуру каталогів `.git` із `objects/`, `refs/heads/`, `refs/tags/`, `hooks/`, `info/`, `HEAD` (що вказує на `refs/heads/main`) і типовим `config`.

`$objectFormat` — це `'sha1'` або `'sha256'`. Репозиторій SHA-256 отримує `repositoryformatversion = 1` і `extensions.objectformat = sha256` у конфігурації. Будь-яке інше значення спричиняє `InvalidArgumentException`. `$options` приймає ті самі ключі, що й `open()`.

Якщо за цим шляхом уже існує репозиторій, генерується `RuntimeException`.

```php
$repo = Pitmaster::init('/home/user/new-project');
$repo = Pitmaster::init('/home/user/sha256-project', 'sha256');
```

### `clone`

```php
public static function clone(string $url, string $path, ?int $depth = null, array $options = []): Repository
```

Клонує віддалений репозиторій. Ініціалізує новий репозиторій, знаходить віддалені посилання, завантажує pack-файл, налаштовує гілки відстеження віддалених репозиторіїв і створює робоче дерево. URL SSH (`ssh://...` або `user@host:path`) використовують транспорт SSH; для інших URL використовується smart HTTP із переходом на dumb HTTP, якщо smart-обмін завершується помилкою. Передайте `$depth` для поверхневого клонування.

Якщо `$options['processes']` має значення `false`, генерується `RuntimeException`. Якщо клонування не вдається, а `$path` раніше не існував, частково створений каталог видаляється.

```php
$repo = Pitmaster::clone('https://github.com/user/repo.git', '/tmp/repo');
$repo = Pitmaster::clone('https://github.com/user/repo.git', '/tmp/shallow', 1);
$repo = Pitmaster::clone('https://github.com/user/repo.git', '/tmp/repo', null, ['hooks' => false]);
```

### `processFreeOptions`

```php
public static function processFreeOptions(): array
```

Повертає `['processes' => false, 'hooks' => false]` для середовищ виконання, у яких не можна запускати процеси хоста. Передайте його в `open()` або `init()`.

### `isRepository`

```php
public static function isRepository(string $path): bool
```

Перевіряє, чи є шлях репозиторієм git. Повертає `true` для звичайних репозиторіїв (каталог `.git/`), пов’язаних робочих дерев (файл `.git`) і чистих репозиторіїв (наявний файл `HEAD`).

```php
if (Pitmaster::isRepository('/path/to/check')) {
    // It's a git repo
}
```

### `isWorktree`

```php
public static function isWorktree(string $path): bool
```

Перевіряє, чи є шлях пов’язаним робочим деревом (а не основним репозиторієм). Повертає `true`, лише якщо `.git` — файл із посиланням `gitdir:`.

```php
if (Pitmaster::isWorktree('/path/to/worktree')) {
    // It's a linked worktree
}
```

### `commonGitDir`

```php
public static function commonGitDir(string $path): ?string
```

Визначає спільний каталог git за будь-яким шляхом отриманої копії. Для звичайних репозиторіїв це каталог `.git`. Для пов’язаних робочих дерев — спільний каталог git, що містить об’єкти, конфігурацію та packed-refs. Повертає `null`, якщо шлях не є репозиторієм.

```php
$common = Pitmaster::commonGitDir('/path/to/worktree');
// '/path/to/main-repo/.git'
```

---

## `Pitmaster\Repository`

Клас `Pitmaster\Repository` — основний дескриптор для всіх операцій із git. Отримайте його через `Pitmaster::open()`, `Pitmaster::init()` або `Pitmaster::clone()`.

```php
use Pitmaster\Repository;
```

### Методи доступу до шляхів

#### `gitDir`

```php
public function gitDir(): string
```

Повертає каталог git для окремого робочого дерева. Для звичайних репозиторіїв це `.git/`. Для пов’язаних робочих дерев — `.git/worktrees/<name>/`.

#### `commonGitDir`

```php
public function commonGitDir(): string
```

Повертає спільний каталог git. Для звичайних репозиторіїв це те саме, що `gitDir()`. Для пов’язаних робочих дерев це каталог `.git/` основного репозиторію (де зберігаються об’єкти, конфігурація та packed-refs).

#### `workDir`

```php
public function workDir(): string
```

Повертає шлях до кореня робочого дерева.

#### `isLinkedWorktree`

```php
public function isLinkedWorktree(): bool
```

Повертає `true`, якщо цей дескриптор репозиторію відповідає пов’язаному робочому дереву.

#### `isBare`, `hooksEnabled`, `processesEnabled`

```php
public function isBare(): bool
public function hooksEnabled(): bool
public function processesEnabled(): bool
```

Повідомляє, чи репозиторій не має робочого дерева, і які параметри `hooks` / `processes` діють для цього дескриптора. `hooksEnabled()` має значення `false`, коли процеси вимкнено.

---

### Об’єкти

#### `readObject`

```php
public function readObject(string $hash): GitObject
```

Зчитує об’єкт за повним шістнадцятковим хешем (40 символів або 64 у репозиторії SHA-256). Скорочені хеші не приймаються. Повертає екземпляр `Blob`, `Tree`, `Commit` або `Tag`. Спершу шукає серед окремих об’єктів, а потім у pack-файлах.

Якщо хеш не знайдено, генерується `ObjectNotFoundException`.

```php
$object = $repo->readObject('a1b2c3d4e5f6...');
```

#### `writeObject`

```php
public function writeObject(GitObject $object): ObjectId
```

Записує об’єкт у сховище окремих об’єктів. Повертає обчислене значення `ObjectId`.

```php
use Pitmaster\Object\Blob;

$blob = Blob::fromContent('Hello, world!');
$id = $repo->writeObject($blob);
```

#### `catFile`

```php
public function catFile(string $hash): string
```

Повертає необроблений вміст об’єкта; еквівалент `git cat-file -p`.

```php
echo $repo->catFile('a1b2c3d4...');
```

#### `objectExists`

```php
public function objectExists(string $hash): bool
```

Перевіряє, чи існує об’єкт у репозиторії (окремо або в pack-файлі).

#### `listObjects`

```php
public function listObjects(): array
```

Перелічує всі хеші об’єктів у репозиторії. Повертає масив шістнадцяткових рядків.

---

### Посилання

#### `head`

```php
public function head(): Commit
```

Повертає поточний коміт HEAD. Переходить за символічними посиланнями (HEAD -> refs/heads/main -> хеш коміту).

Якщо HEAD не вказує на дійсний коміт (e.g, порожній репозиторій), генерується `RuntimeException`.

#### `branch`

```php
public function branch(?string $name = null): ?string
```

Без аргументів повертає назву поточної гілки (e.g, `'main'`) або `null`, якщо HEAD від’єднано.

Якщо передано назву гілки, повертає хеш її коміту (шістнадцятковий рядок) або `null`, якщо такої гілки немає.

```php
$current = $repo->branch();        // 'main'
$hash = $repo->branch('feature');   // 'abc123...' or null
```

#### `branches`

```php
public function branches(): array
```

Повертає відсортований масив назв усіх гілок (без префікса `refs/heads/`).

```php
$branches = $repo->branches();
// ['bugfix/typo', 'feature/login', 'main']
```

#### `tags`

```php
public function tags(): array
```

Повертає відсортований масив назв усіх тегів (без префікса `refs/tags/`).

#### `resolve`

```php
public function resolve(string $revision): ObjectId
```

Розв’язує вираз ревізії в `ObjectId`. Підтримуються:
- Повні шістнадцяткові хеші (40 символів або 64 для SHA-256). Скорочені хеші, як-от `abc1234`, не розпізнаються.
- `HEAD`
- Назви гілок (`main`, `feature/login`)
- Назви тегів (`v1.0.0`)
- Повні шляхи посилань (`refs/heads/main`)
- Вирази ревізій (`HEAD~3`, `main^2`)

Якщо ревізію не вдалося розпізнати, генерується `RuntimeException`.

```php
$id = $repo->resolve('HEAD~3');
$id = $repo->resolve('v1.0.0');
$id = $repo->resolve('feature/login');
```

#### `allRefs`

```php
public function allRefs(): array
```

Повертає всі посилання як асоціативний масив: назва посилання — шістнадцятковий хеш.

```php
$refs = $repo->allRefs();
// ['refs/heads/main' => 'abc123...', 'refs/tags/v1.0.0' => 'def456...']
```

#### `updateRef`

```php
public function updateRef(string $name, ObjectId $target): void
```

Оновлює посилання, щоб воно вказувало на нову ціль.

```php
$repo->updateRef('refs/heads/main', $commitId);
```

#### `createBranch`

```php
public function createBranch(string $name, ?ObjectId $from = null): void
```

Створює нову гілку. Якщо `$from` має значення null, вона створюється від поточного HEAD.

```php
$repo->createBranch('feature/new-thing');
$repo->createBranch('hotfix', $repo->resolve('v1.0.0'));
```

#### `deleteBranch`

```php
public function deleteBranch(string $name): void
```

Видаляє гілку за назвою. Якщо гілку отримано або її не існує, генерується `RuntimeException`. На відміну від `git branch -d`, цей метод не перевіряє, чи виконано злиття гілки.

```php
$repo->deleteBranch('feature/old');
```

#### `createLightweightTag`

```php
public function createLightweightTag(string $name, ?ObjectId $target = null): void
```

Створює легковаговий тег. Якщо `$target` має значення null, тег безпосередньо додається до поточного HEAD.

```php
$repo->createLightweightTag('v2.0.0');
$repo->createLightweightTag('hotfix-start', $repo->resolve('main~2'));
```

#### `createTag`

```php
public function createTag(string $name, string $message, ?ObjectId $target = null, ?string $tagger = null): ObjectId
```

Створює анотований тег. Якщо `$target` має значення null, позначає поточний HEAD тегом. Повертає `ObjectId` об’єкта тегу.

```php
$tagId = $repo->createTag('v2.0.0', 'Release version 2.0.0');
```

#### `deleteTag`

```php
public function deleteTag(string $name): void
```

Видаляє тег за назвою.

```php
$repo->deleteTag('v1.0.0');
```

#### `packRefs`

```php
public function packRefs(): void
```

Перезаписує спільний файл `packed-refs` на основі поточного набору вільних посилань і видаляє відповідні спільні вільні посилання. Для зв’язаних робочих дерев записує дані в спільний каталог git.

```php
$repo->packRefs();
```

#### `defaultBranch`

```php
public function defaultBranch(): string
```

Визначає гілку репозиторію за замовчуванням. Спочатку перевіряє символьне посилання HEAD віддаленого репозиторію, потім локальний HEAD, а далі використовує `main` або `master`.

#### `isBranchMerged`

```php
public function isBranchMerged(string $branch, ?string $target = null): bool
```

Перевіряє, чи повністю злито гілку з іншою гілкою (за замовчуванням — із гілкою за замовчуванням).

```php
if ($repo->isBranchMerged('feature/done')) {
    $repo->deleteBranch('feature/done');
}
```

#### `checkout`

```php
public function checkout(string $target): void
```

Перемикає гілки або від’єднує HEAD. Оновлює HEAD, індекс і робоче дерево. `$target` може бути назвою гілки, тегом, повним хешем або виразом ревізії; якщо це не гілка, HEAD від’єднується.

Викидає `RuntimeException`, якщо локальна зміна (проіндексована чи ні) або невідстежуваний файл є за шляхом, що відрізняється в поточному дереві та цільовому дереві.

Незакомічені зміни в інших відстежуваних файлах не переносяться: індекс і робоче дерево перебудовуються на основі цільового дерева, тому зміни у файлах, однакових в обох деревах, втрачаються; git зберіг би їх ([#2](https://github.com/inline0/pitmaster/issues/2)). Спочатку створіть коміт або сховайте зміни. `merge()`, `cherryPick()`, `revert()`, `rebase()` і методи bisect перебудовують робоче дерево так само.

```php
$repo->checkout('feature/login');
$repo->checkout($repo->resolve('v1.0.0')->hex);  // Detached HEAD
```

---

### Індекс

#### `index`

```php
public function index(): Index
```

Читає та повертає поточний індекс (область підготовки) з `.git/index`.

#### `add`

```php
public function add(string ...$paths): void
```

Додає файли до індексу. Читає кожен файл із робочого дерева, створює об’єкт blob і оновлює індекс. Кожен аргумент має бути шляхом до файлу (або символічного посилання) відносно кореня робочого дерева; каталоги та шаблони не розгортаються, а якщо файл відсутній, викидається `RuntimeException`.

```php
$repo->add('src/Feature.php', 'tests/FeatureTest.php');
```

#### `remove`

```php
public function remove(string ...$paths): void
public function removeCached(string ...$paths): void
```

Видаляє відстежувані шляхи і з індексу, і з робочого дерева. Підтримує `--cached` та `-r` / `--recursive` у списку аргументів для поведінки на кшталт Git.

```php
$repo->remove('old-file.txt');
$repo->remove('-r', 'src');
$repo->remove('--cached', 'old-file.txt');
$repo->removeCached('old-file.txt');
```

#### `mv`

```php
public function mv(string $source, string $destination): void
```

Переміщує або перейменовує файл і в робочому дереві, і в індексі.

```php
$repo->mv('old-name.php', 'new-name.php');
```

---

### Коміти

#### `commit`

```php
public function commit(?string $message = null, ?string $author = null): ObjectId
```

Створює коміт із поточного індексу. Будує ієрархію дерева на основі записів індексу, створює об’єкт коміту з HEAD як батьківським комітом (якщо він є) та оновлює HEAD. Повертає `ObjectId` нового коміту.

`$message` може мати значення `null` лише тоді, коли злиття, cherry-pick, скасування коміту або перебазування зупинилося через конфлікт; у такому разі використовується підготовлене повідомлення. В інших випадках повідомлення `null` спричиняє помилку `RuntimeException('Commit message required')`. Коміт також спричиняє помилку, якщо індекс містить нерозлиті шляхи, якщо в репозиторії без комітів індекс порожній або якщо дерево не змінилося порівняно з HEAD.

Параметр `$author` — це повний рядок автора git (`Name <email> timestamp timezone`). Якщо значення null, його визначають зі змінних середовища, констант і `.git/config` у порядку, описаному в розділі [Початок роботи](https://pitmaster.inline0.dev/uk/docs/getting-started).

```php
$id = $repo->commit('Fix null pointer bug');
```

#### `show`

```php
public function show(string $revision): array
```

Показує коміт і його різницю з першим батьківським комітом. Якщо ревізія відповідає анотованому тегу, повернене значення також містить `['tag' => Tag]`, а `commit` — це цільовий коміт після розіменування тегу.

```php
$result = $repo->show('HEAD');
echo $result['commit']->message;

foreach ($result['diff'] as $diff) {
    echo $diff->format();
}
```

#### `reset`

```php
public function reset(string $revision, string $mode = 'mixed'): void
```

Скидає HEAD до коміту. Режими:
- `'soft'`: перемістити лише HEAD
- `'mixed'`: перемістити HEAD і скинути індекс (за замовчуванням)
- `'hard'`: перемістити HEAD, скинути індекс і робоче дерево

```php
$repo->reset('HEAD~1', 'soft');
$repo->reset('main', 'hard');
```

#### `restore`

```php
public function restore(string $path, ?string $source = null, bool $staged = false, bool $worktree = false): void
```

Відновлює шлях з індексу або вказаної вихідної ревізії. Якщо не встановлено жодного прапорця, Pitmaster відновлює робоче дерево з індексу (або з `$source`, якщо його вказано). `staged: true` відновлює індекс із HEAD (або `$source`), не змінюючи робоче дерево; передайте `staged: true, worktree: true`, щоб відновити обидва.

```php
$repo->restore('src/File.php');                                        // Worktree from index
$repo->restore('src/File.php', staged: true);                          // Index from HEAD
$repo->restore('src/File.php', 'HEAD~1');                              // Worktree from another commit
$repo->restore('src/File.php', 'HEAD~1', staged: true, worktree: true); // Index + worktree from another commit
```

#### `cherryPick`

```php
public function cherryPick(string $revision): ObjectId
public function cherryPickContinue(): ObjectId
public function cherryPickAbort(): void
```

Застосовує коміт як новий коміт у поточній гілці, зберігаючи початкового автора. Якщо операцію зупинено через конфлікти, усуньте їх, додайте шляхи до індексу, а потім викличте `cherryPickContinue()` або `cherryPickAbort()`.

```php
$newId = $repo->cherryPick('feature~2');

// After resolving conflicts:
$repo->add('src/File.php');
$repo->cherryPickContinue();
```

#### `revert`

```php
public function revert(string $revision): ObjectId
public function revertContinue(): ObjectId
public function revertAbort(): void
```

Створює коміт, що скасовує інший коміт. Якщо скасування зупинено через конфлікти, усуньте їх, додайте шляхи до індексу, а потім викличте `revertContinue()` або `revertAbort()`.

```php
$revertId = $repo->revert('HEAD~1');

// After resolving conflicts:
$repo->add('src/File.php');
$repo->revertContinue();
```

#### `rebase`

```php
public function rebase(string $onto): array
public function rebaseContinue(): array
public function rebaseAbort(): void
public function rebaseSkip(): array
```

Перебазовує поточну гілку на іншу ревізію. Наразі Pitmaster підтримує лінійне перебазування зі станом зупинки через конфлікти у форматі Git, а також продовження, переривання та пропуск. HEAD має вказувати на гілку.

`rebase()`, `rebaseContinue()` і `rebaseSkip()` повертають `['success' => bool, 'commits' => int, 'conflicts' => list<string>]`: чи завершилося перебазування, скільки комітів застосовано та шляхи з конфліктами, якщо перебазування зупинилося.

```php
$result = $repo->rebase('main');

if (!$result['success']) {
    // Resolve conflicts, then continue or skip/abort.
    $repo->add('src/File.php');
    $repo->rebaseContinue();
}
```

---

### Стан

#### `status`

```php
public function status(): array
```

Обчислює стан робочого дерева. Повертає масив об’єктів `StatusEntry`, кожен із яких містить:
- `path`: шлях до файлу
- `index`: перелік `FileStatus` змін між HEAD та індексом
- `worktree`: перелік `FileStatus` змін між індексом і робочим деревом
- `origPath`, `renameScore`: задаються для перейменованих записів, інакше — `null`

Для невідстежуваних файлів в обох стовпцях указано `FileStatus::Untracked`. Ігноровані файли не відображаються.

```php
foreach ($repo->status() as $entry) {
    echo $entry->shortFormat() . "\n"; // "M  src/File.php", "?? notes.txt"
}
```

#### `statusPorcelainV2`

```php
public function statusPorcelainV2(): string
```

Виводить дані про стан у машиночитному форматі porcelain v2 від git.

```php
echo $repo->statusPorcelainV2();
// 1 M. N... 000000 000000 000000 0000...0000 0000...0000 modified-file.txt
// ? untracked-file.txt
```

---

### Різниця

#### `diff`

```php
public function diff(?string $pathspec = null, string $algorithm = 'myers'): array
```

Порівнює робоче дерево з індексом (непроіндексовані зміни). Повертає масив об’єктів `DiffResult`. `$pathspec` фільтрує за одним точним шляхом до файлу. `$algorithm` — це `'myers'` (за замовчуванням), `'patience'`, `'histogram'` або `'minimal'`; будь-яке інше значення спричиняє помилку `InvalidArgumentException`.

```php
$diffs = $repo->diff();

foreach ($diffs as $diff) {
    echo $diff->format();  // Unified diff output
}
```

#### `diffStaged`

```php
public function diffStaged(?string $pathspec = null, string $algorithm = 'myers'): array
```

Порівнює індекс із HEAD (проіндексовані зміни). Повертає `DiffResult[]` з виявленням перейменувань між видаленими та доданими файлами.

Аргумент `$pathspec` наразі ігнорується, тому повертаються всі проіндексовані файли; до першого коміту метод повертає порожній масив ([#5](https://github.com/inline0/pitmaster/issues/5)).

```php
$staged = $repo->diffStaged();
```

#### `diffTree`

```php
public function diffTree(ObjectId $a, ObjectId $b, string $algorithm = 'myers'): array
```

Порівнює два дерева за їхнім `ObjectId`. Повертає `DiffResult[]`. Передавайте ідентифікатори дерев, а не комітів: ідентифікатор коміту не розпізнається як дерево й повертає порожній результат.

```php
$old = $repo->readObject($repo->resolve('HEAD~1')->hex);
$new = $repo->head();
$diffs = $repo->diffTree($old->tree, $new->tree);
```

---

### Журнал

#### `log`

```php
public function log(int $limit = 50, ?ObjectId $from = null): array
```

Переглядає історію комітів, від найновішого за міткою часу комітера. Повертає `Commit[]`. Якщо `$from` має значення null, починає з HEAD. Повертає порожній масив, якщо в HEAD ще немає комітів.

```php
$commits = $repo->log(10);
```

#### `logAll`

```php
public function logAll(int $limit = 50): array
```

Переглядає коміти, досяжні з кожного посилання та HEAD, від найновішого. Повертає `Commit[]`.

#### `logOneline`

```php
public function logOneline(int $limit = 50, bool $all = false, ?string $path = null): array
```

Повертає рядки журналу у форматі `git log --oneline` (`abc1234 Subject`), починаючи з HEAD, з усіх посилань (`$all`) або з фільтром за шляхом.

#### `logPath`

```php
public function logPath(string $path, int $limit = 50): array
```

Фільтрує журнал за шляхом до файлу. Повертає лише коміти, у різниці яких із першим батьківським комітом цей точний файл додано, змінено, видалено або перейменовано. Шляхи до каталогів не збігаються.

Перевіряються лише `5 * $limit` найновіших комітів, тож старіші коміти, що стосуються файлу, можуть бути відсутні в результаті ([#7](https://github.com/inline0/pitmaster/issues/7)).

```php
$commits = $repo->logPath('src/Repository.php', 20);
```

---

### Злиття

#### `merge`

```php
public function merge(string $branch, string $strategy = 'recursive'): MergeResult
public function mergeOctopus(array $branches): MergeResult
public function mergeContinue(): ObjectId
public function mergeAbort(): void
```

Зливає гілку (або будь-яку ревізію) в HEAD. Автоматично виявляє злиття перемотуванням уперед. Для інших злиттів перевіряє конфлікти на рівні дерева та створює коміт злиття, якщо конфліктів немає. Якщо виникають конфлікти, записує стан злиття у форматі Git до індексу й робочого дерева; усуньте конфлікти, додайте шляхи до індексу, а потім викличте `mergeContinue()` або `mergeAbort()`.

Параметр `$strategy` `'ours'` записує коміт злиття, який зберігає дерево HEAD; будь-яке інше значення вмикає стандартне рекурсивне злиття. `mergeOctopus()` зливає дві або більше гілок в один коміт. Якщо в будь-якій гілці є конфлікти, повертається результат із конфліктами без запису стану злиття та створення коміту.

Злиття гілки, яка вже міститься в HEAD, створює ще один коміт злиття замість повідомлення «Уже актуально» ([#6](https://github.com/inline0/pitmaster/issues/6)). Незакомічені зміни у відстежуваних файлах перезаписуються, як описано в розділі `checkout()`.

Повертає `MergeResult` із такими полями:
- `clean`: чи виконалося злиття без конфліктів
- `commitId`: ідентифікатор коміту злиття (якщо конфліктів немає)
- `conflictPaths`: масив шляхів файлів із конфліктами (якщо конфлікти є)
- `mergedContents`: шлях із конфліктом до файлу з маркерами конфлікту (якщо конфлікти є)

```php
$result = $repo->merge('feature/login');

if ($result->clean) {
    echo "Merged: {$result->commitId->hex}\n";
} else {
    echo "Conflicts in: " . implode(', ', $result->conflictPaths) . "\n";
    $repo->add('src/File.php');
    $repo->mergeContinue();
}
```

#### `mergeBase`

```php
public function mergeBase(ObjectId $a, ObjectId $b): ?ObjectId
```

Знаходить базу злиття (найближчого спільного предка) двох комітів. Повертає null, якщо спільного предка немає.

```php
$base = $repo->mergeBase(
    $repo->resolve('main'),
    $repo->resolve('feature/login'),
);
```

### Бісекція

```php
public function bisectStart(string $bad, string $good): ObjectId
public function bisectGood(?string $revision = null): ?ObjectId
public function bisectBad(?string $revision = null): ?ObjectId
public function bisectReset(): void
```

Знаходить бінарним пошуком коміт, у якому з’явилася зміна. `bisectStart()` записує погану та добру ревізії, перемикається на першого кандидата й повертає його. `bisectGood()` і `bisectBad()` позначають поточний HEAD (або `$revision`), перемикаються на наступного кандидата та повертають його. Коли залишається один підозрюваний коміт, він повертається й залишається кандидатом: це перший поганий коміт. `bisectReset()` завершує сеанс і повертає до гілки або коміту, з якого він почався.

```php
$candidate = $repo->bisectStart('HEAD', 'v1.0.0');
// test $candidate ...
$candidate = $repo->bisectBad();
$candidate = $repo->bisectGood();
$repo->bisectReset();
```

---

### Мережа

#### `fetch`

```php
public function fetch(string $remote = 'origin', ?int $depth = null): void
```

Отримує дані з віддаленого репозиторію. Завантажує нові об’єкти й оновлює віддалені відстежувані посилання. Передайте `$depth`, щоб поглибити або обмежити неглибоку історію.

`fetch()` і методи `push*()` викидають `RuntimeException` для дескриптора, відкритого з `['processes' => false]`.

```php
$repo->fetch();
$repo->fetch('upstream');
```

#### `push`

```php
public function push(string $remote = 'origin', ?string $branch = null): void
public function pushForceWithLease(string $remote = 'origin', ?string $branch = null, ?ObjectId $expected = null): void
public function pushAtomic(string $remote = 'origin', array $branches = []): void
public function pushMirror(string $remote = 'origin'): void
```

Надсилає поточну гілку (або вказану гілку) до віддаленого репозиторію. Для суворішого або ширшого оновлення посилань доступні варіанти force-with-lease, atomic і mirror.

```php
$repo->push();
$repo->push('origin', 'feature/login');
$repo->pushForceWithLease('origin', 'main', $repo->resolve('refs/remotes/origin/main'));
$repo->pushAtomic('origin', ['main', 'release']);
$repo->pushMirror();
```

---

### Робочі дерева

#### `addWorktree`

```php
public function addWorktree(string $path, string $branch, ?ObjectId $from = null, ?string $name = null): Worktree
```

Додає зв’язане робоче дерево. Створює гілку, якщо її ще немає, налаштовує метадані робочого дерева й створює файли робочого дерева. Передайте `$name`, якщо потрібен детермінований ідентифікатор метаданих, відмінний від назви каталогу робочої копії.

```php
$wt = $repo->addWorktree('/tmp/review', 'feature/review');
$wt = $repo->addWorktree('/tmp/checkouts/review', 'feature/review', name: 'review-wt');
```

#### `removeWorktree`

```php
public function removeWorktree(string $pathOrName, bool $force = false): void
```

Видаляє зв’язане робоче дерево. Використовуйте `$force = true`, щоб видалити його, навіть якщо воно заблоковане.

#### `worktrees`

```php
public function worktrees(): array
```

Перелічує всі робочі дерева (основне та зв’язані). Повертає `Worktree[]`.

```php
foreach ($repo->worktrees() as $wt) {
    echo "{$wt->path} [{$wt->branch}]\n";
}
```

---

### Конфігурація

#### `config`

```php
public function config(): GitConfig
```

Отримує доступ до конфігурації git репозиторію. Читає та записує значення конфігурації.

```php
$config = $repo->config();
$name = $config->get('user.name');
$config->set('user.email', 'new@example.com');
```

---

### Внутрішній доступ

Ці методи відкривають доступ до внутрішніх компонентів для складних випадків використання.

#### `objectDatabase`

```php
public function objectDatabase(): ObjectDatabase
```

Отримує доступ до бази даних необроблених об’єктів (вільних об’єктів і пакетів).

#### `refDatabase`

```php
public function refDatabase(): RefDatabase
```

Отримує доступ до бази даних необроблених посилань (вільних і упакованих посилань).

---

## Класи нижчого рівня

Ці класи не обгорнуті в `Repository` і створюються з його внутрішніх компонентів.

### `Pitmaster\Graph\Blame`

```php
public function blame(ObjectId $headId, string $path): array
```

Повертає по одному запису на рядок із `$path` за адресою `$headId`: `['line' => int, 'hash' => string, 'author' => string, 'content' => string]`. `author` — це повний рядок ідентифікації (`Name <email> timestamp timezone`).

```php
use Pitmaster\Graph\Blame;

$blame = new Blame($repo->objectDatabase());

foreach ($blame->blame($repo->resolve('HEAD'), 'src/File.php') as $line) {
    echo substr($line['hash'], 0, 8) . " {$line['content']}\n";
}
```

### `Pitmaster\Graph\Grep`

```php
public function grep(ObjectId $treeId, string $pattern, string $prefix = '', array $options = []): array
```

Шукає в blob-об’єктах дерева. Повертає `['path' => string, 'line' => int, 'content' => string]` для кожного рядка зі збігом. Параметри: `regex` (трактує шаблон як шаблон PCRE) і `ignore_case`. Некоректний регулярний вираз спричиняє помилку `InvalidArgumentException`.

```php
use Pitmaster\Graph\Grep;

$grep = new Grep($repo->objectDatabase());
$matches = $grep->grep($repo->head()->tree, 'TODO', '', ['ignore_case' => true]);
```

### `Pitmaster\Ref\Notes`

```php
public function get(ObjectId $commitId, string $notesRef = 'refs/notes/commits'): ?string
public function set(ObjectId $commitId, string $noteContent, string $notesRef = 'refs/notes/commits'): void
public function remove(ObjectId $commitId, string $notesRef = 'refs/notes/commits'): void
public function listAll(string $notesRef = 'refs/notes/commits'): array
public function merge(string $sourceRef, string $targetRef = 'refs/notes/commits'): void
```

Читає та записує нотатки git, що зберігаються за посиланням нотаток. `listAll()` повертає хеш коміту, зіставлений із вмістом нотатки.

```php
use Pitmaster\Ref\Notes;

$notes = new Notes($repo->objectDatabase(), $repo->refDatabase());
$notes->set($repo->resolve('HEAD'), "Reviewed\n");
echo $notes->get($repo->resolve('HEAD'));
```
