Skip to content

Protocols

Pitmaster supports multiple network transport protocols for communicating with git servers. Smart HTTP is the primary transport. Additional protocols are available for broader compatibility.

Smart HTTP (primary)

The recommended transport. Works over HTTPS and is the most widely deployed protocol. There is no API for custom headers or credentials; credentials can only be passed in the URL (https://user:token@host/repo.git), which PHP sends as Basic authentication. clone() stores the URL, credentials included, as remote.origin.url in .git/config. Requests use PHP's stream wrappers, so allow_url_fopen must be enabled.

PHP
use Pitmaster\Protocol\SmartHttpClient;

$http = new SmartHttpClient(timeout: 30);
$discovery = $http->discoverRefs('https://github.com/user/repo.git');

See Smart HTTP for full details.

Protocol v1

The traditional smart HTTP protocol. All refs are sent during discovery, and negotiation uses want/have/done lines.

PHP
use Pitmaster\Protocol\ProtocolV1;

Protocol v2

The newer protocol with structured commands (ls-refs, fetch). More efficient for repositories with many refs because the client can filter the ref list.

clone() and fetch() always use protocol v1. Protocol v2 is available explicitly over smart HTTP through SmartHttpClient::discoverRefsV2() and UploadPackClient::fetchV2().

Dumb HTTP

Fallback for servers that do not support the smart HTTP protocol. The dumb protocol fetches objects and packs individually via plain HTTP GET requests.

PHP
use Pitmaster\Protocol\DumbHttpClient;

$client = new DumbHttpClient();

Dumb HTTP limitations:

  • No negotiation (client must download everything)
  • No delta compression during transfer
  • Significantly slower than smart HTTP
  • Some servers (GitHub, GitLab) do not support it

The dumb protocol fetches:

  1. GET /info/refs (plain text, no pkt-line)
  2. GET /HEAD (default branch)
  3. GET /objects/info/packs (list of available packs)
  4. GET /objects/pack/pack-<hash>.pack and .idx (each listed pack and its index)
  5. GET /objects/<xx>/<rest> (individual loose objects, walked from the ref tips)

Step 5 runs only when the server lists no packs. When it lists packs, objects that exist only as loose files on the server are not downloaded (#18). The dumb client accepts 40-character (SHA-1) object IDs only. clone() and fetch() fall back to dumb HTTP when the smart HTTP exchange fails.

SSH

SSH transport uses the ssh2 PHP extension when it is loaded. Otherwise it starts the system ssh client through proc_open(). Either way it runs git-upload-pack or git-receive-pack on the remote.

PHP
use Pitmaster\Protocol\SshClient;

$client = new SshClient();

SSH URLs:

Code
git@github.com:user/repo.git
ssh://git@github.com/user/repo.git

No extension is required. The two paths behave differently:

System ssh client (used when ssh2 is not loaded). Pitmaster runs ssh -o BatchMode=yes -o ConnectTimeout=30 -p <port> <user>@<host> <command>, so authentication and host key checking follow your SSH configuration and agent. These environment variables adjust the command:

Variable Effect
PITMASTER_SSH_COMMAND Executable to run instead of ssh
PITMASTER_SSH_IDENTITY_FILE Passed as -i <file>
PITMASTER_SSH_KNOWN_HOSTS Passed as -o UserKnownHostsFile=<file>
PITMASTER_SSH_STRICT_HOST_KEY_CHECKING Passed as -o StrictHostKeyChecking=<value>

This path starts a process, so it is not available on handles opened with ['processes' => false].

ssh2 extension (always preferred when loaded). Pitmaster authenticates with ~/.ssh/id_rsa, then ~/.ssh/id_ed25519 if the RSA key is present but rejected, and otherwise with the SSH agent. The PITMASTER_SSH_* variables are ignored, and the server's host key is not verified on this path (#22).

The SSH transport runs git-upload-pack or git-receive-pack as a remote command and communicates over the SSH channel using the same pkt-line protocol as smart HTTP.

Git protocol (TCP)

The native git protocol communicates over TCP port 9418 using PHP sockets. GitProtocolClient implements ref discovery and upload-pack (fetching); it cannot push. clone() and fetch() do not route git:// URLs to it, so use the client directly.

PHP
use Pitmaster\Protocol\GitProtocolClient;

$client = new GitProtocolClient();

Git URLs:

Code
git://github.com/user/repo.git

Characteristics:

  • No authentication (read-only in practice)
  • No encryption
  • Deprecated by most hosting providers
  • Uses the same pkt-line framing as smart HTTP

The git protocol sends the service name and repository path as the initial request, then proceeds with standard pkt-line ref discovery and pack negotiation.

Bundles

Git bundles are offline pack files with ref information. They allow transferring repository data without a network connection.

PHP
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 reads v2 and v3 bundle headers. There is no Repository method to import a bundle: write packData() to .git/objects/pack/, build its index with PackIndexer::writeIndex(), and update the refs yourself.

Bundle format:

Code
# v2 git bundle\n
-<prerequisite hash>\n    (zero or more)
<hash> <refname>\n        (one or more)
\n
<pack data>               (standard pack file format)

Bundles are useful for:

  • Sneakernet transfers (USB drives)
  • Incremental backups
  • Seeding clones in air-gapped environments

Shallow clones

Pitmaster supports shallow clones, where only a limited depth of commit history is fetched.

PHP
use Pitmaster\Protocol\ShallowClone;

Pass a depth to Pitmaster::clone($url, $path, $depth) or $repo->fetch($remote, $depth). The request adds the shallow capability and a deepen line:

Code
want <hash>
deepen <depth>

The server responds with shallow <hash> lines indicating where the history was truncated. Pitmaster computes the shallow boundary from the depth itself and uses the server's shallow lines when that computation finds nothing; the result is stored in .git/shallow. ShallowClone::readShallow(), writeShallow(), and isShallow() work with that file directly.

Protocol comparison

Protocol Auth Encryption Push Typical use
Smart HTTP Credentials in the URL HTTPS Yes Primary transport
Dumb HTTP Credentials in the URL HTTPS No Legacy fallback
SSH SSH keys or agent SSH Yes Private repos, CI
Git (TCP) None None No Read-only mirrors
Bundle N/A N/A N/A Offline transfer

Transport selection

Pitmaster::clone(), $repo->fetch(), and $repo->push() select the transport from the URL:

URL pattern Transport
ssh://... SSH
user@host:path SSH
Anything else (https://..., http://...) Smart HTTP; clone and fetch fall back to dumb HTTP

git:// URLs and local paths are not supported by these methods: they are sent to the smart HTTP client and fail. Use GitProtocolClient directly for git://. An HTTPS URL that contains both a user and a port (https://user@host:8443/repo.git) is currently mistaken for an SSH URL (#17).