跳转到正文

协议

Pitmaster 支持多种网络传输协议,用于与 git 服务器通信。Smart HTTP 是主要传输方式,也提供其他协议以提高兼容性。

Smart HTTP(主要协议)

推荐使用的传输方式。通过 HTTPS 工作,也是部署最广泛的协议。没有用于自定义标头或凭据的 API;凭据只能通过 URL(https://user:token@host/repo.git)传递,PHP 会将其作为 Basic 身份验证发送。clone() 会将包含凭据的 URL 作为 remote.origin.url 存储在 .git/config 中。请求使用 PHP 的流包装器,因此必须启用 allow_url_fopen。

PHP
use Pitmaster\Protocol\SmartHttpClient;

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

详情请参阅Smart HTTP。

协议 v1

传统的 smart HTTP 协议。发现阶段会发送所有 refs,协商时使用 want/have/done 行。

PHP
use Pitmaster\Protocol\ProtocolV1;

协议 v2

较新的协议采用结构化命令(ls-refs、fetch)。对于 refs 众多的仓库,该协议效率更高,因为客户端可以筛选 refs 列表。

clone() 和 fetch() 始终使用协议 v1。协议 v2 可通过 SmartHttpClient::discoverRefsV2() 和 UploadPackClient::fetchV2() 经由 smart HTTP 显式使用。

哑HTTP

用于不支持 smart HTTP 协议的服务器的回退方案。Dumb 协议通过普通 HTTP GET 请求逐个获取对象和 pack。

PHP
use Pitmaster\Protocol\DumbHttpClient;

$client = new DumbHttpClient();

Dumb HTTP 的限制:

  • 不进行协商(客户端必须下载所有内容)
  • 传输过程中不进行 delta 压缩
  • 比 smart HTTP 慢得多
  • 某些服务器(GitHub、GitLab)不支持

Dumb 协议会获取:

  1. GET /info/refs(纯文本,不使用 pkt-line)
  2. GET /HEAD(默认分支)
  3. GET /objects/info/packs(可用 pack 列表)
  4. GET /objects/pack/pack-<hash>.pack 和 .idx(每个列出的 pack 及其索引)
  5. GET /objects/<xx>/<rest>(从 ref 顶端遍历获取单个松散对象)

仅当服务器未列出 pack 时才会执行第 5 步。若服务器列出了 pack,则不会下载仅以松散文件形式存在于服务器上的对象(#18)。Dumb 客户端仅接受 40 个字符的(SHA-1)对象 ID。smart HTTP 交换失败时,clone() 和 fetch() 会回退到 dumb HTTP。

SSH

SSH 传输方式在已加载时使用 ssh2 PHP 扩展。否则会通过 proc_open() 启动系统 ssh 客户端。无论哪种情况,都会在远程端运行 git-upload-pack 或 git-receive-pack。

PHP
use Pitmaster\Protocol\SshClient;

$client = new SshClient();

SSH URL:

代码
git@github.com:user/repo.git
ssh://git@github.com/user/repo.git

无需扩展。两种路径的行为有所不同:

系统 ssh 客户端(ssh2 未加载时使用)。Pitmaster 会运行 ssh -o BatchMode=yes -o ConnectTimeout=30 -p <port> <user>@<host> <command>,因此身份验证和主机密钥检查遵循你的 SSH 配置及代理。以下环境变量可调整命令:

变量 作用
PITMASTER_SSH_COMMAND 替代 ssh 运行的可执行文件
PITMASTER_SSH_IDENTITY_FILE 作为 -i <file> 传递
PITMASTER_SSH_KNOWN_HOSTS 作为 -o UserKnownHostsFile=<file> 传递
PITMASTER_SSH_STRICT_HOST_KEY_CHECKING 作为 -o StrictHostKeyChecking=<value> 传递

此路径会启动进程,因此不能用于通过 ['processes' => false] 打开的句柄。

ssh2 扩展(加载后始终优先使用)。Pitmaster 使用 ~/.ssh/id_rsa 进行身份验证;如果 RSA 密钥存在但被拒绝,则改用 ~/.ssh/id_ed25519;否则使用 SSH 代理。此路径会忽略 PITMASTER_SSH_* 变量,且不会验证服务器的主机密钥(#22)。

SSH 传输方式会将 git-upload-pack 或 git-receive-pack 作为远程命令运行,并通过 SSH 通道通信,使用与 smart HTTP 相同的 pkt-line 协议。

Git 协议(TCP)

原生 git 协议通过 TCP 端口 9418 使用 PHP 套接字通信。GitProtocolClient 实现了 refs 发现和 upload-pack(获取),但不支持推送。clone() 和 fetch() 不会将 git:// URL 路由到此协议,因此请直接使用客户端。

PHP
use Pitmaster\Protocol\GitProtocolClient;

$client = new GitProtocolClient();

Git URL:

代码
git://github.com/user/repo.git

特性:

  • 无身份验证(实际只能读取)
  • 不加密
  • 大多数托管服务提供商已弃用
  • 使用与 smart HTTP 相同的 pkt-line 分帧方式

git 协议会在初始请求中发送服务名称和仓库路径,随后使用标准 pkt-line 进行 refs 发现和 pack 协商。

Bundle

Git bundle 是包含 ref 信息的离线 pack 文件,可在没有网络连接时传输仓库数据。

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 可读取 v2 和 v3 bundle 标头。没有 Repository 方法可导入 bundle:将 packData() 写入 .git/objects/pack/,使用 PackIndexer::writeIndex() 建立索引,然后自行更新 refs。

Bundle 格式:

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

Bundle 适用于:

  • 通过 USB 驱动器等方式离线传输
  • 增量备份
  • 在气隙隔离环境中为克隆预置数据

浅克隆

Pitmaster 支持浅克隆,只获取有限深度的提交历史。

PHP
use Pitmaster\Protocol\ShallowClone;

向 Pitmaster::clone($url, $path, $depth) 或 $repo->fetch($remote, $depth) 传入深度。请求会添加 shallow 能力和一行 deepen:

代码
want <hash>
deepen <depth>

服务器会返回 shallow <hash> 行,指出历史记录被截断的位置。Pitmaster 根据深度本身计算浅克隆边界;如果计算结果为空,则使用服务器的 shallow 行;结果存储在 .git/shallow 中。ShallowClone::readShallow()、writeShallow() 和 isShallow() 会直接处理该文件。

协议比较

协议 身份验证 加密 推送 典型用途
Smart HTTP 凭据位于 URL 中 HTTPS 是 主要传输方式
Dumb HTTP 凭据位于 URL 中 HTTPS 否 旧版回退方式
SSH SSH 密钥或代理 SSH 是 私有仓库、CI
Git (TCP) 无 无 否 只读镜像
Bundle 不适用 不适用 不适用 离线传输

传输方式选择

Pitmaster::clone()、$repo->fetch() 和 $repo->push() 根据 URL 选择传输方式:

URL 模式 传输方式
ssh://... SSH
user@host:path SSH
其他情况(https://...、http://...) Smart HTTP;clone 和 fetch 会回退到 dumb HTTP

git:// URL 和本地路径不受这些方法支持:它们会被发送给 smart HTTP 客户端并失败。对于 git://,请直接使用 GitProtocolClient。目前,包含用户名和端口的 HTTPS URL(https://user@host:8443/repo.git)会被误认为是 SSH URL(问题 #17)。