Έξυπνο HTTP
Το Pitmaster επικοινωνεί με διακομιστές git μέσω HTTPS χρησιμοποιώντας το έξυπνο πρωτόκολλο 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, με τις δυνατότητες στην πρώτη γραμμή.
Αποστολή pack (fetch)
$response = $http->uploadPack($url, $requestBody);
Στέλνει ένα POST στο <url>/git-upload-pack με το σώμα διαπραγμάτευσης want/have. Επιστρέφει το ακατέργαστο σώμα απόκρισης (pkt-lines, NAK και δεδομένα side-band). Το UploadPackClient δημιουργεί το αίτημα και εξάγει το pack:
use Pitmaster\Protocol\UploadPackClient;
$client = new UploadPackClient($http);
$packData = $client->fetch($url, $wants, $haves); // ObjectId[] wants and haves
Λήψη pack (push)
$response = $http->receivePack($url, $requestBody);
Στέλνει ένα POST στο <url>/git-receive-pack με τις εντολές ενημέρωσης αναφορών και τα δεδομένα pack.
Κωδικοποίηση PktLine
Το πρωτόκολλο git χρησιμοποιεί πλαισίωση pkt-line για όλη την επικοινωνία. Κάθε γραμμή έχει ως πρόθεμα το συνολικό μήκος της (μαζί με το πρόθεμα μήκους των 4 byte), κωδικοποιημένο ως 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 byte) - Μέγιστη γραμμή δεδομένων:
fff0(ωφέλιμο φορτίο 65516 byte) - Πακέτο εκκαθάρισης:
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 |
Ο διακομιστής στέλνει thin packs (ενδέχεται να παραλείπονται βασικά αντικείμενα). Το Pitmaster το ζητά, αλλά δεν μπορεί ακόμη να δημιουργήσει ευρετήριο για thin packs (#16) |
side-band |
Ο διακομιστής χρησιμοποιεί side-band για δεδομένα pack (με όριο τα 1000 byte) |
side-band-64k |
Ο διακομιστής χρησιμοποιεί side-band 64K για δεδομένα pack |
ofs-delta |
Το pack μπορεί να χρησιμοποιεί αντικείμενα OFS_DELTA |
shallow |
Ο διακομιστής υποστηρίζει shallow clone |
no-done |
Ο διακομιστής δεν απαιτεί γραμμή "done" |
symref=HEAD:refs/heads/main |
Συμβολικός στόχος του HEAD |
agent=git/2.45.0 |
Έκδοση git του διακομιστή |
Διαχωρισμός καναλιών side-band
Όταν ο διακομιστής χρησιμοποιεί side-band, τα δεδομένα pack πολυπλέκονται με μηνύματα προόδου και σφάλματος. Το πρώτο byte του ωφέλιμου φορτίου κάθε pkt-line υποδεικνύει το κανάλι:
| Κανάλι | Σημασία |
|---|---|
| 1 | Δεδομένα pack |
| 2 | Μηνύματα προόδου (stderr) |
| 3 | Μηνύματα μοιραίου σφάλματος |
Το Pitmaster του UploadPackClient αφαιρεί τα byte καναλιού και ανασυνθέτει τα δεδομένα pack.
Εκδόσεις πρωτοκόλλου
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. Αν αποτύχει η ανταλλαγή έξυπνου HTTP για clone() ή fetch(), το Pitmaster επιχειρεί ξανά μέσω απλού HTTP και, αν αποτύχει κι αυτό, επαναφέρει το αρχικό σφάλμα. Άλλες αποτυχίες (άγνωστος απομακρυσμένος διακομιστής, λαβή χωρίς διεργασία, pack που δεν μπορεί να καταχωριστεί σε ευρετήριο) προκαλούν άλλους τύπους εξαιρέσεων:
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