
# Протоколы

Pitmaster поддерживает несколько транспортных сетевых протоколов для связи с серверами git. Основной транспорт — Smart HTTP. Для большей совместимости доступны и другие протоколы.

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

Рекомендуемый транспорт. Работает поверх HTTPS и является наиболее распространённым протоколом. Для пользовательских заголовков или учётных данных нет API; учётные данные можно передать только в URL (`https://user:token@host/repo.git`), который PHP отправляет для базовой аутентификации. `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/ru/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 по отдельности загружает объекты и pack-файлы с помощью обычных GET-запросов HTTP.

```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` (список доступных pack-файлов)
4. `GET /objects/pack/pack-<hash>.pack` и `.idx` (каждый указанный pack-файл и его индекс)
5. `GET /objects/<xx>/<rest>` (отдельные свободные объекты, обход начиная с вершин ссылок)

Шаг 5 выполняется только тогда, когда сервер не указывает pack-файлы. Если pack-файлы указаны, объекты, существующие на сервере только в виде отдельных файлов, не загружаются ([#18](https://github.com/inline0/pitmaster/issues/18)). Клиент dumb принимает только идентификаторы объектов длиной 40 символов (SHA-1). Если обмен smart HTTP завершается с ошибкой, `clone()` и `fetch()` используют резервный вариант dumb 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 и согласование pack.

## Bundle-файлы

Bundle-файлы Git — это автономные pack-файлы с информацией о ссылках. Они позволяют передавать данные репозитория без сетевого подключения.

```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 читает заголовки bundle-файлов v2 и v3. Отдельного метода `Repository` для импорта bundle-файла нет: запишите `packData()` в `.git/objects/pack/`, создайте индекс с помощью `PackIndexer::writeIndex()` и самостоятельно обновите ссылки.

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

Bundle-файлы полезны для:
- Передачи данных без сети (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()`.

## Сравнение протоколов

| Протокол | Аутентификация | Шифрование | Отправка | Типичное применение |
|----------|------|-----------|------|-------------|
| 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 |

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