协议
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。
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 行。
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。
use Pitmaster\Protocol\DumbHttpClient;
$client = new DumbHttpClient();
Dumb HTTP 的限制:
- 不进行协商(客户端必须下载所有内容)
- 传输过程中不进行 delta 压缩
- 比 smart HTTP 慢得多
- 某些服务器(GitHub、GitLab)不支持
Dumb 协议会获取:
GET /info/refs(纯文本,不使用 pkt-line)GET /HEAD(默认分支)GET /objects/info/packs(可用 pack 列表)GET /objects/pack/pack-<hash>.pack和.idx(每个列出的 pack 及其索引)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。
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 路由到此协议,因此请直接使用客户端。
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 文件,可在没有网络连接时传输仓库数据。
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 支持浅克隆,只获取有限深度的提交历史。
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)。