Smart HTTP
Pitmaster se comunica com servidores git por HTTPS usando o protocolo smart HTTP. Este é o transporte de rede principal. Ele usa as funções nativas de stream de PHP (fopen com um contexto de stream) para solicitações HTTP, portanto allow_url_fopen precisa estar habilitado.
SmartHttpClient
use Pitmaster\Protocol\SmartHttpClient;
$http = new SmartHttpClient();
// Or with custom timeout:
$http = new SmartHttpClient(timeout: 60);
O tempo limite usa por padrão a constante PITMASTER_HTTP_TIMEOUT (um inteiro) ou 30 segundos quando ela não está definida.
Descobrir refs
$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() faz o mesmo para git-receive-pack. A solicitação de descoberta de refs é enviada para:
GET <url>/info/refs?service=git-upload-pack
A resposta é uma lista de refs codificada em pkt-line, com recursos na primeira linha.
Upload pack (fetch)
$response = $http->uploadPack($url, $requestBody);
Envia um POST para <url>/git-upload-pack com o corpo da negociação want/have. Retorna o corpo bruto da resposta (pkt-lines, NAK e dados side-band). UploadPackClient cria a solicitação e extrai o pack:
use Pitmaster\Protocol\UploadPackClient;
$client = new UploadPackClient($http);
$packData = $client->fetch($url, $wants, $haves); // ObjectId[] wants and haves
Receive pack (push)
$response = $http->receivePack($url, $requestBody);
Envia um POST para <url>/git-receive-pack com os comandos de atualização de refs e os dados do pack.
Codificação PktLine
O protocolo git usa enquadramento pkt-line em toda a comunicação. Cada linha tem como prefixo seu comprimento total (incluindo o prefixo de comprimento de 4 bytes), codificado como 4 dígitos hexadecimais.
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)
Regras de comprimento
- Linha de dados mínima:
0005(carga útil de 1 byte) - Linha de dados máxima:
fff0(carga útil de 65516 bytes) - Pacote de flush:
0000 - Pacote delimitador:
0001 - Fim da resposta:
0002(protocolo v2; tratado porProtocolV2::decode(), enquantoPktLine::decode()o rejeita)
PktLine::MAX_PAYLOAD; // 65516 (65520 - 4)
Descoberta de refs
A classe RefDiscovery analisa o anúncio de refs do servidor.
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
Formato do anúncio de refs
A primeira linha do anúncio inclui recursos:
<hash> HEAD\0<capability list>\n
<hash> refs/heads/main\n
<hash> refs/tags/v1.0\n
0000
Recursos
A classe Capability analisa e gerencia os recursos do protocolo.
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']
Recursos comuns
| Recurso | Significado |
|---|---|
multi_ack |
O servidor oferece suporte à negociação multi-ack |
thin-pack |
O servidor envia packs finos (objetos-base podem ser omitidos). Pitmaster solicita esse recurso, mas ainda não pode indexar packs finos (#16) |
side-band |
O servidor usa side-band para dados do pack (limitado a 1000 bytes) |
side-band-64k |
O servidor usa side-band de 64K para dados do pack |
ofs-delta |
O pack pode usar objetos OFS_DELTA |
shallow |
O servidor oferece suporte a clone raso |
no-done |
O servidor não exige uma linha "done" |
symref=HEAD:refs/heads/main |
Destino simbólico de HEAD |
agent=git/2.45.0 |
Versão git do servidor |
Demultiplexação side-band
Quando o servidor usa side-band, os dados do pack são multiplexados com mensagens de progresso e de erro. O primeiro byte da carga útil de cada pkt-line indica o canal:
| Canal | Significado |
|---|---|
| 1 | Dados do pack |
| 2 | Mensagens de progresso (stderr) |
| 3 | Mensagens de erro fatal |
Pitmaster de UploadPackClient remove os bytes do canal e remonta os dados do pack.
Versões do protocolo
v1 (padrão)
O protocolo tradicional. A descoberta de refs envia todas as refs de uma vez. Pitmaster negocia em uma única rodada: envia suas solicitações, cada ponta de ref local como have e done.
v2
Um protocolo mais simples e com melhor desempenho.
Principais diferenças em relação a v1:
- A descoberta de refs é um comando
ls-refsseparado (pode filtrar refs) - Fetch é um único comando
fetchcom parâmetros estruturados - Usa pacotes delimitadores (
0001) para separar seções
clone() e fetch() sempre usam v1. v2 está disponível quando chamado explicitamente:
$discovery = $http->discoverRefsV2($url); // ls-refs
$packData = (new UploadPackClient($http))->fetchV2($url, $wants, $haves);
Fluxo de solicitação HTTP
Clone/Fetch
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)
Push
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
Tratamento de erros
Erros de rede lançam ProtocolException. Quando a troca smart HTTP de clone() ou fetch() falha, Pitmaster tenta novamente usando dumb HTTP e relança o erro original se isso também falhar. Outras falhas (remoto desconhecido, handle sem processo, pack que não pode ser indexado) lançam outros tipos de exceção:
use Pitmaster\Exceptions\ProtocolException;
try {
$repo->fetch();
} catch (ProtocolException $e) {
echo "Network error: {$e->getMessage()}\n";
}
Erros comuns:
- Servidor inacessível (tempo limite)
- Autenticação necessária ou repositório não encontrado (reportado como
Unexpected HTTP status 401,403ou404) Content-Typeinesperado na resposta- Dados pkt-line inválidos
- Mensagem de erro do canal side-band