Smart HTTP
Pitmaster komunikuje się z serwerami git przez HTTPS, używając protokołu smart HTTP. To podstawowy transport sieciowy. Korzysta z natywnych funkcji strumieni PHP (fopen z kontekstem strumienia) do żądań HTTP, dlatego allow_url_fopen musi być włączone.
SmartHttpClient
use Pitmaster\Protocol\SmartHttpClient;
$http = new SmartHttpClient();
// Or with custom timeout:
$http = new SmartHttpClient(timeout: 60);
Domyślny limit czasu to stała PITMASTER_HTTP_TIMEOUT (liczba całkowita), a jeśli nie jest zdefiniowana, wynosi 30 sekund.
Wykrywanie refów
$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() wykonuje tę samą czynność dla git-receive-pack. Żądanie wykrycia refów jest wysyłane do:
GET <url>/info/refs?service=git-upload-pack
Odpowiedź to zakodowana jako pkt-line lista refów, z możliwościami w pierwszym wierszu.
Wysyłanie packa (fetch)
$response = $http->uploadPack($url, $requestBody);
Wysyła żądanie POST do <url>/git-upload-pack z treścią negocjacji want/have. Zwraca surową treść odpowiedzi (wiersze pkt-line, NAK i dane side-band). UploadPackClient buduje żądanie i wyodrębnia pack:
use Pitmaster\Protocol\UploadPackClient;
$client = new UploadPackClient($http);
$packData = $client->fetch($url, $wants, $haves); // ObjectId[] wants and haves
Odbieranie packa (push)
$response = $http->receivePack($url, $requestBody);
Wysyła żądanie POST do <url>/git-receive-pack z poleceniami aktualizacji refów i danymi packa.
Kodowanie PktLine
Protokół git używa ramkowania pkt-line do całej komunikacji. Każdy wiersz jest poprzedzony jego całkowitą długością (wraz z 4-bajtowym prefiksem długości), zakodowaną jako 4 cyfry szesnastkowe.
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)
Reguły długości
- Minimalny wiersz danych:
0005(ładunek 1 bajt) - Maksymalny wiersz danych:
fff0(ładunek 65516 bajtów) - Pakiet opróżniający:
0000 - Pakiet separatora:
0001 - Koniec odpowiedzi:
0002(protokół v2; obsługiwany przezProtocolV2::decode(), natomiastPktLine::decode()go odrzuca)
PktLine::MAX_PAYLOAD; // 65516 (65520 - 4)
Wykrywanie refów
Klasa RefDiscovery analizuje ogłoszenie refów serwera.
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
Format ogłoszenia refów
Pierwszy wiersz ogłoszenia zawiera możliwości:
<hash> HEAD\0<capability list>\n
<hash> refs/heads/main\n
<hash> refs/tags/v1.0\n
0000
Możliwości
Klasa Capability analizuje możliwości protokołu i zarządza nimi.
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']
Typowe możliwości
| Możliwość | Znaczenie |
|---|---|
multi_ack |
Serwer obsługuje negocjacje multi-ack |
thin-pack |
Serwer wysyła cienkie packi (mogą pomijać obiekty bazowe). Pitmaster żąda tej możliwości, ale nie potrafi jeszcze indeksować cienkich packów (#16) |
side-band |
Serwer używa side-band dla danych packa (limit 1000 bajtów) |
side-band-64k |
Serwer używa side-band 64K dla danych packa |
ofs-delta |
Pack może używać obiektów OFS_DELTA |
shallow |
Serwer obsługuje płytkie klonowanie |
no-done |
Serwer nie wymaga wiersza „done” |
symref=HEAD:refs/heads/main |
Symboliczny cel HEAD |
agent=git/2.45.0 |
Wersja git serwera |
Demultipleksowanie side-band
Gdy serwer używa side-band, dane packa są multipleksowane z komunikatami o postępie i błędach. Pierwszy bajt ładunku każdego wiersza pkt-line wskazuje kanał:
| Kanał | Znaczenie |
|---|---|
| 1 | Dane packa |
| 2 | Komunikaty o postępie (stderr) |
| 3 | Komunikaty o błędach krytycznych |
PitmasterUploadPackClient usuwa bajty kanału i składa dane packa ponownie.
Wersje protokołu
v1 (domyślna)
Tradycyjny protokół. Wykrywanie refów przesyła wszystkie refy naraz. Pitmaster negocjuje w jednej rundzie: wysyła swoje wanty, każdy lokalny czubek refa jako have oraz done.
v2
Prostszy protokół o lepszej wydajności.
Najważniejsze różnice względem v1:
- Wykrywanie refów to osobne polecenie
ls-refs(może filtrować refy) - Fetch to pojedyncze polecenie
fetchz uporządkowanymi parametrami - Używa pakietów separatora (
0001) do oddzielania sekcji
clone() i fetch() zawsze używają v1. v2 jest dostępne, gdy wywołasz je jawnie:
$discovery = $http->discoverRefsV2($url); // ls-refs
$packData = (new UploadPackClient($http))->fetchV2($url, $wants, $haves);
Przebieg żądania HTTP
Klonowanie/pobieranie
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)
Wysyłanie zmian
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
Obsługa błędów
Błędy sieciowe powodują wyjątek ProtocolException. Jeśli wymiana smart HTTP dla clone() lub fetch() nie powiedzie się, Pitmaster ponawia próbę przez dumb HTTP, a gdy ta również zawiedzie, ponownie zgłasza pierwotny błąd. Inne niepowodzenia (nieznany zdalny serwer, uchwyt bez procesu, pack, którego nie można indeksować) powodują inne typy wyjątków:
use Pitmaster\Exceptions\ProtocolException;
try {
$repo->fetch();
} catch (ProtocolException $e) {
echo "Network error: {$e->getMessage()}\n";
}
Typowe błędy:
- Serwer nieosiągalny (przekroczono limit czasu)
- Wymagane uwierzytelnienie lub nie znaleziono repozytorium (zgłaszane jako
Unexpected HTTP status 401,403lub404) - Nieoczekiwany
Content-Typew odpowiedzi - Nieprawidłowe dane pkt-line
- Komunikat z kanału błędów side-band