Протоколы
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.
use Pitmaster\Protocol\SmartHttpClient;
$http = new SmartHttpClient(timeout: 30);
$discovery = $http->discoverRefs('https://github.com/user/repo.git');
Подробнее см. в разделе Smart HTTP.
Протокол v1
Традиционный протокол smart HTTP. При обнаружении отправляются все ссылки, а согласование выполняется с помощью строк want/have/done.
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.
use Pitmaster\Protocol\DumbHttpClient;
$client = new DumbHttpClient();
Ограничения Dumb HTTP:
- Нет согласования (клиент должен скачать всё)
- Нет дельта-сжатия при передаче
- Значительно медленнее, чем smart HTTP
- Некоторые серверы (GitHub, GitLab) его не поддерживают
Протокол dumb загружает:
GET /info/refs(обычный текст, без pkt-line)GET /HEAD(ветку по умолчанию)GET /objects/info/packs(список доступных pack-файлов)GET /objects/pack/pack-<hash>.packи.idx(каждый указанный pack-файл и его индекс)GET /objects/<xx>/<rest>(отдельные свободные объекты, обход начиная с вершин ссылок)
Шаг 5 выполняется только тогда, когда сервер не указывает pack-файлы. Если pack-файлы указаны, объекты, существующие на сервере только в виде отдельных файлов, не загружаются (#18). Клиент dumb принимает только идентификаторы объектов длиной 40 символов (SHA-1). Если обмен smart HTTP завершается с ошибкой, clone() и fetch() используют резервный вариант dumb HTTP.
SSH
Транспорт SSH использует расширение ssh2 PHP, если оно загружено. Иначе он запускает системный клиент ssh через proc_open(). В обоих случаях на удалённом сервере выполняется git-upload-pack или git-receive-pack.
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).
Транспорт 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:// этому протоколу, поэтому используйте клиент напрямую.
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-файлы с информацией о ссылках. Они позволяют передавать данные репозитория без сетевого подключения.
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 поддерживает неглубокие клоны, в которых загружается только ограниченная глубина истории коммитов.
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).