
# Протоколи

Pitmaster підтримує кілька мережевих транспортних протоколів для зв’язку із серверами git. Основним транспортом є Smart HTTP. Для ширшої сумісності доступні додаткові протоколи.

## Smart HTTP (основний)

Рекомендований транспорт. Працює через HTTPS і є найпоширенішим протоколом. Для власних заголовків або облікових даних API немає; облікові дані можна передати лише в URL (`https://user:token@host/repo.git`), які PHP надсилає як автентифікацію Basic. `clone()` зберігає URL, включно з обліковими даними, як `remote.origin.url` у `.git/config`. Запити використовують потокові обгортки PHP, тому `allow_url_fopen` має бути ввімкнено.

```php
use Pitmaster\Protocol\SmartHttpClient;

$http = new SmartHttpClient(timeout: 30);
$discovery = $http->discoverRefs('https://github.com/user/repo.git');
```

Див. [Smart HTTP](https://pitmaster.inline0.dev/uk/docs/network/smart-http), щоб дізнатися більше.

### Протокол v1

Традиційний протокол smart HTTP. Під час виявлення надсилаються всі посилання, а узгодження використовує рядки want/have/done.

```php
use Pitmaster\Protocol\ProtocolV1;
```

### Протокол v2

Новіший протокол зі структурованими командами (`ls-refs`, `fetch`). Ефективніший для репозиторіїв із багатьма посиланнями, оскільки клієнт може фільтрувати їхній список.

`clone()` і `fetch()` завжди використовують протокол v1. Протокол v2 можна явно використати через smart HTTP за допомогою `SmartHttpClient::discoverRefsV2()` і `UploadPackClient::fetchV2()`.

## Розумний HTTP

Резервний варіант для серверів, які не підтримують протокол smart HTTP. Протокол dumb завантажує об’єкти й пакети окремо за допомогою звичайних запитів HTTP GET.

```php
use Pitmaster\Protocol\DumbHttpClient;

$client = new DumbHttpClient();
```

Обмеження Dumb HTTP:
- Немає узгодження (клієнт має завантажити все)
- Під час передавання немає дельта-стиснення
- Значно повільніший за Smart HTTP
- Деякі сервери (GitHub, GitLab) його не підтримують

Протокол dumb завантажує:
1. `GET /info/refs` (звичайний текст, без pkt-line)
2. `GET /HEAD` (гілка за замовчуванням)
3. `GET /objects/info/packs` (список доступних пакетів)
4. `GET /objects/pack/pack-<hash>.pack` і `.idx` (кожен зазначений пакет та його індекс)
5. `GET /objects/<xx>/<rest>` (окремі вільні об’єкти, обхід починається з вершин посилань)

Крок 5 виконується, лише якщо сервер не перелічує пакети. Якщо пакети перелічено, об’єкти, що є на сервері лише як вільні файли, не завантажуються ([#18](https://github.com/inline0/pitmaster/issues/18)). Клієнт dumb приймає лише ідентифікатори об’єктів завдовжки 40 символів (SHA-1). `clone()` і `fetch()` переходять на dumb HTTP, якщо обмін даними через smart HTTP завершується помилкою.

## SSH

Транспорт SSH використовує розширення `ssh2` PHP, якщо воно завантажене. Інакше він запускає системний клієнт `ssh` через `proc_open()`. В обох випадках на віддаленому сервері запускається `git-upload-pack` або `git-receive-pack`.

```php
use Pitmaster\Protocol\SshClient;

$client = new SshClient();
```

URL-адреси SSH:
```
git@github.com:user/repo.git
ssh://git@github.com/user/repo.git
```

Розширення не потрібне. Ці два способи працюють по-різному:

**Системний клієнт `ssh`** (використовується, коли `ssh2` не завантажено). Pitmaster запускає `ssh -o BatchMode=yes -o ConnectTimeout=30 -p <port> <user>@<host> <command>`, тому автентифікація та перевірка ключа хоста відповідають вашій конфігурації SSH та агенту. Ці змінні середовища змінюють команду:

| Змінна | Дія |
|----------|--------|
| `PITMASTER_SSH_COMMAND` | Виконуваний файл, який запускається замість `ssh` |
| `PITMASTER_SSH_IDENTITY_FILE` | Передається як `-i <file>` |
| `PITMASTER_SSH_KNOWN_HOSTS` | Передається як `-o UserKnownHostsFile=<file>` |
| `PITMASTER_SSH_STRICT_HOST_KEY_CHECKING` | Передається як `-o StrictHostKeyChecking=<value>` |

Цей спосіб запускає процес, тому він недоступний для дескрипторів, відкритих за допомогою `['processes' => false]`.

**Розширення `ssh2`** (завжди має перевагу, якщо завантажене). Pitmaster автентифікується за допомогою `~/.ssh/id_rsa`, потім `~/.ssh/id_ed25519`, якщо ключ RSA наявний, але його відхилено, а в інших випадках — за допомогою агента SSH. Змінні `PITMASTER_SSH_*` ігноруються, а ключ хоста сервера в цьому режимі не перевіряється ([#22](https://github.com/inline0/pitmaster/issues/22)).

Транспорт SSH запускає `git-upload-pack` або `git-receive-pack` як віддалену команду й обмінюється даними через канал SSH, використовуючи той самий протокол pkt-line, що й smart HTTP.

## Протокол Git (TCP)

Власний протокол git обмінюється даними через TCP-порт 9418 за допомогою сокетів PHP. `GitProtocolClient` реалізує виявлення посилань і `upload-pack` (завантаження); надсилати дані він не може. `clone()` та `fetch()` не спрямовують URL-адреси `git://` до нього, тож використовуйте клієнт безпосередньо.

```php
use Pitmaster\Protocol\GitProtocolClient;

$client = new GitProtocolClient();
```

URL-адреси Git:
```
git://github.com/user/repo.git
```

Характеристики:
- Без автентифікації (фактично лише для читання)
- Без шифрування
- Більшість хостинг-провайдерів вважають його застарілим
- Використовує те саме кадрування pkt-line, що й smart HTTP

Протокол git надсилає назву служби та шлях до репозиторію в першому запиті, а потім виконує стандартне виявлення посилань pkt-line та узгодження пакета.

## Пакети Bundle

Пакети Git — це автономні файли пакетів із інформацією про посилання. Вони дають змогу передавати дані репозиторію без мережевого з’єднання.

```php
use Pitmaster\Protocol\Bundle;

// Read a bundle file
$bundle = Bundle::open('/path/to/repo.bundle');
$refs = $bundle->refs();
$packData = $bundle->packData();
$prerequisites = $bundle->prerequisites();

// Create one
$bundle = Bundle::create(['refs/heads/main' => $commitId], $packData);
$bundle->writeTo('/path/to/out.bundle');
```

Pitmaster читає заголовки пакетів v2 і v3. Способу `Repository` імпортувати пакет немає: запишіть `packData()` у `.git/objects/pack/`, створіть його індекс за допомогою `PackIndexer::writeIndex()` і оновіть посилання самостійно.

Формат пакета:
```
# v2 git bundle\n
-<prerequisite hash>\n    (zero or more)
<hash> <refname>\n        (one or more)
\n
<pack data>               (standard pack file format)
```

Пакети корисні для:
- Передавання даних без мережі (USB-накопичувачі)
- Інкрементного резервного копіювання
- Підготовки клонів у середовищах без мережевого підключення

## Поверхневі клони

Pitmaster підтримує поверхневі клони, у яких завантажується лише обмежена глибина історії комітів.

```php
use Pitmaster\Protocol\ShallowClone;
```

Передайте глибину до `Pitmaster::clone($url, $path, $depth)` або `$repo->fetch($remote, $depth)`. Запит додає можливість `shallow` і рядок `deepen`:
```
want <hash>
deepen <depth>
```

Сервер відповідає рядками `shallow <hash>`, які вказують, де історію обрізано. Pitmaster обчислює межу поверхневої історії за самою глибиною та використовує рядки сервера `shallow`, якщо це обчислення нічого не знаходить; результат зберігається в `.git/shallow`. `ShallowClone::readShallow()`, `writeShallow()` і `isShallow()` працюють безпосередньо з цим файлом.

## Порівняння протоколів

| Протокол | Автентифікація | Шифрування | Push | Типове використання |
|----------|------|-----------|------|-------------|
| Smart HTTP | Облікові дані в URL | HTTPS | Так | Основний транспорт |
| Dumb HTTP | Облікові дані в URL | HTTPS | Ні | Резервний варіант для застарілих систем |
| SSH | Ключі або агент SSH | SSH | Так | Приватні репозиторії, CI |
| Git (TCP) | Немає | Немає | Ні | Дзеркала лише для читання |
| Bundle | Не застосовується | Не застосовується | Не застосовується | Передавання без мережі |

## Вибір транспорту

`Pitmaster::clone()`, `$repo->fetch()` і `$repo->push()` вибирають транспорт із URL:

| Шаблон URL | Транспорт |
|------------|-----------|
| `ssh://...` | SSH |
| `user@host:path` | SSH |
| Будь-що інше (`https://...`, `http://...`) | Smart HTTP; clone і fetch переходять на dumb HTTP |

Методи `git://` не підтримують URL-адреси й локальні шляхи: вони передаються клієнту smart HTTP і призводять до помилки. Для `git://` використовуйте безпосередньо `GitProtocolClient`. HTTPS URL, що містить і користувача, і порт (`https://user@host:8443/repo.git`), наразі помилково розпізнається як SSH URL ([#17](https://github.com/inline0/pitmaster/issues/17)).
