Розумний HTTP
Pitmaster обмінюється даними із серверами git через HTTPS, використовуючи протокол smart HTTP. Це основний мережевий транспорт. Він використовує власні потокові функції PHP (fopen із контекстом потоку) для запитів HTTP, тому allow_url_fopen має бути ввімкнено.
SmartHttpClient
use Pitmaster\Protocol\SmartHttpClient;
$http = new SmartHttpClient();
// Or with custom timeout:
$http = new SmartHttpClient(timeout: 60);
Час очікування за замовчуванням дорівнює константі PITMASTER_HTTP_TIMEOUT (ціле число) або 30 секундам, якщо її не визначено.
Виявлення посилань
$discovery = $http->discoverRefs('https://github.com/user/repo.git');
foreach ($discovery->refs() as $name => $id) {
echo "{$id->hex} {$name}\n";
}
// HEAD symref (e.g., 'refs/heads/main')
$defaultBranch = $discovery->headSymref();
// Specific ref
$mainId = $discovery->ref('refs/heads/main');
// Server capabilities (a Capability object, or null)
$caps = $discovery->capabilities();
discoverReceivePackRefs() виконує те саме для git-receive-pack. Запит на виявлення посилань надсилається до:
GET <url>/info/refs?service=git-upload-pack
Відповідь — це закодований у pkt-line список посилань із можливостями в першому рядку.
Надсилання пакета (fetch)
$response = $http->uploadPack($url, $requestBody);
Надсилає POST до <url>/git-upload-pack із тілом запиту для узгодження want/have. Повертає необроблене тіло відповіді (рядки pkt-line, NAK і дані side-band). UploadPackClient формує запит і витягує пакет:
use Pitmaster\Protocol\UploadPackClient;
$client = new UploadPackClient($http);
$packData = $client->fetch($url, $wants, $haves); // ObjectId[] wants and haves
Отримання пакета (push)
$response = $http->receivePack($url, $requestBody);
Надсилає POST до <url>/git-receive-pack із командами оновлення посилань і даними пакета.
Кодування PktLine
Протокол git використовує кадрування pkt-line для всього обміну даними. Перед кожним рядком указується його загальна довжина (разом із 4-байтовим префіксом довжини), закодована як 4 шістнадцяткові цифри.
use Pitmaster\Protocol\PktLine;
// Encode a single line
$encoded = PktLine::encode("want abc123def456\n");
// "0016want abc123def456\n"
// Flush packet (end of section)
$flush = PktLine::flush();
// "0000"
// Delimiter packet (protocol v2)
$delim = PktLine::delimiter();
// "0001"
// Decode a stream
$lines = PktLine::decode($rawData);
// Each element is: string (data), null (flush), or false (delimiter)
Правила довжини
- Мінімальний рядок даних:
0005(корисне навантаження 1 байт) - Максимальний рядок даних:
fff0(корисне навантаження 65516 байтів) - Пакет скидання:
0000 - Пакет-роздільник:
0001 - Кінець відповіді:
0002(протокол v2; обробляєтьсяProtocolV2::decode(), тоді якPktLine::decode()його відхиляє)
PktLine::MAX_PAYLOAD; // 65516 (65520 - 4)
Виявлення посилань
Клас RefDiscovery аналізує оголошення посилань сервера.
use Pitmaster\Protocol\RefDiscovery;
$discovery = RefDiscovery::parse($pktLines);
// All refs
$refs = $discovery->refs();
// ['refs/heads/main' => ObjectId, 'refs/tags/v1.0' => ObjectId, ...]
// Single ref
$id = $discovery->ref('refs/heads/main');
// HEAD symbolic ref (from capabilities)
$symref = $discovery->headSymref();
// 'refs/heads/main' or null
// Server capabilities
$caps = $discovery->capabilities();
// Capability object (see below), or null
Формат оголошення посилань
Перший рядок оголошення містить можливості:
<hash> HEAD\0<capability list>\n
<hash> refs/heads/main\n
<hash> refs/tags/v1.0\n
0000
Можливості
Клас Capability аналізує протокольні можливості та керує ними.
use Pitmaster\Protocol\Capability;
$caps = Capability::parse('thin-pack side-band-64k ofs-delta symref=HEAD:refs/heads/main');
$caps->has('thin-pack'); // true
$caps->get('symref'); // 'HEAD:refs/heads/main'
$caps->all(); // ['thin-pack' => null, ..., 'symref' => 'HEAD:refs/heads/main']
Поширені можливості
| Можливість | Значення |
|---|---|
multi_ack |
Сервер підтримує узгодження multi-ack |
thin-pack |
Сервер надсилає тонкі пакети (базові об’єкти можуть бути відсутні). Pitmaster запитує їх, але поки не може індексувати тонкі пакети (#16) |
side-band |
Сервер використовує side-band для даних пакета (обмеження — 1000 байтів) |
side-band-64k |
Сервер використовує side-band 64K для даних пакета |
ofs-delta |
У пакеті можуть бути об’єкти OFS_DELTA |
shallow |
Сервер підтримує поверхневе клонування |
no-done |
Серверу не потрібен рядок "done" |
symref=HEAD:refs/heads/main |
Символьна ціль HEAD |
agent=git/2.45.0 |
Версія git сервера |
Демультиплексування side-band
Коли сервер використовує side-band, дані пакета мультиплексуються з повідомленнями про перебіг і помилки. Перший байт корисного навантаження кожного рядка pkt-line позначає канал:
| Канал | Значення |
|---|---|
| 1 | Дані пакета |
| 2 | Повідомлення про перебіг (stderr) |
| 3 | Повідомлення про критичні помилки |
Pitmaster видаляє байти каналів із UploadPackClient і повторно збирає дані пакета.
Версії протоколу
v1 (за замовчуванням)
Традиційний протокол. Під час виявлення посилань надсилаються всі посилання одразу. Pitmaster узгоджує дані за один цикл: надсилає потрібні йому об’єкти, кожну локальну вершину посилання як have, а також done.
v2
Простіший протокол із кращою продуктивністю.
Основні відмінності від v1:
- Виявлення посилань — це окрема команда
ls-refs(можна фільтрувати посилання) - Fetch — це одна команда
fetchзі структурованими параметрами - Для розділення секцій використовуються пакети-роздільники (
0001)
clone() і fetch() завжди використовують v1. v2 доступний, якщо викликати його явно:
$discovery = $http->discoverRefsV2($url); // ls-refs
$packData = (new UploadPackClient($http))->fetchV2($url, $wants, $haves);
Потік запиту HTTP
Клонування/отримання
1. GET /info/refs?service=git-upload-pack
Response: pkt-line encoded ref list
2. POST /git-upload-pack
Request: want <hash>\n ... have <hash>\n ... done\n
Response: NAK\n + pack data (possibly side-band wrapped)
Надсилання
1. GET /info/refs?service=git-receive-pack
Response: pkt-line encoded ref list
2. POST /git-receive-pack
Request: <old-hash> <new-hash> refs/heads/main\0<capabilities>\n
0000
PACK<pack data>
Response: unpack ok\n + ref status lines
Обробка помилок
Помилки мережі призводять до винятку ProtocolException. Якщо обмін через smart HTTP під час clone() або fetch() завершується помилкою, Pitmaster повторює спробу через dumb HTTP і, якщо вона теж не вдається, повторно викидає початкову помилку. Інші збої (невідомий віддалений репозиторій, дескриптор без процесу, пакет, який неможливо індексувати) спричиняють винятки інших типів:
use Pitmaster\Exceptions\ProtocolException;
try {
$repo->fetch();
} catch (ProtocolException $e) {
echo "Network error: {$e->getMessage()}\n";
}
Поширені помилки:
- Сервер недоступний (час очікування вичерпано)
- Потрібна автентифікація або репозиторій не знайдено (повідомляється як
Unexpected HTTP status 401,403або404) - Неочікуваний
Content-Typeу відповіді - Некоректні дані pkt-line
- Повідомлення про помилку в каналі side-band