简体中文 ▾ 主题 ▾ 最新版本 ▾ gitprotocol-http 上次更新于 2.52.0

名称

gitprotocol-http - Git 基于 HTTP 的协议

概要

<over-the-wire-protocol>

描述

Git 支持两种基于 HTTP 的传输协议。一种“愚蠢”(dumb)协议,只需要服务器端有标准的 HTTP 服务器,以及一种“智能”(smart)协议,需要一个 Git 感知的 CGI(或服务器模块)。本文档描述了这两种协议。

作为一项设计特性,智能客户端可以自动将“愚蠢”协议的 URL 升级为智能 URL。这允许所有用户拥有相同的已发布 URL,并且对等方会自动选择对他们可用的最高效的传输方式。

URL 格式

通过 HTTP 访问的 Git 仓库的 URL 使用 RFC 1738 中定义的标准 HTTP URL 语法,因此它们的形式为:

http://<host>:<port>/<path>?<searchpart>

在此文档中,占位符 $GIT_URL 将代表用户输入的 http:// 仓库 URL。

服务器应处理匹配 $GIT_URL 位置的所有请求,因为 Git 使用的“智能”和“愚蠢” HTTP 协议都通过在用户提供的 $GIT_URL 字符串的末尾附加额外的路径组件来工作。

一个愚蠢客户端请求一个松散对象的示例

$GIT_URL:     http://example.com:8080/git/repo.git
URL request:  http://example.com:8080/git/repo.git/objects/d0/49f6c27a2244e12041955e262a404c7faba355

一个智能请求到达通配网关的示例

$GIT_URL:     http://example.com/daemon.cgi?svc=git&q=
URL request:  http://example.com/daemon.cgi?svc=git&q=/info/refs&service=git-receive-pack

一个请求到子模块的示例

$GIT_URL:     http://example.com/git/repo.git/path/submodule.git
URL request:  http://example.com/git/repo.git/path/submodule.git/info/refs

客户端必须去除用户提供的 $GIT_URL 字符串末尾的 /(如果存在),以防止在发送到服务器的任何 URL 中出现空路径标记(//)。兼容的客户端必须将 $GIT_URL/info/refs 展开为 foo/info/refs,而不是 foo//info/refs

认证

如果需要认证才能访问仓库,则使用标准的 HTTP 认证,并且可以由 HTTP 服务器软件进行配置和强制执行。

由于 Git 仓库是通过标准路径组件访问的,服务器管理员可以使用其 HTTP 服务器内的目录权限来控制仓库访问。

客户端应支持 RFC 2617 中描述的 Basic 认证。服务器应通过依赖位于 Git 服务器软件前面的 HTTP 服务器来支持 Basic 认证。

服务器不应要求 HTTP cookie 用于认证或访问控制目的。

客户端和服务器可以支持其他常见的基于 HTTP 的认证形式,例如 Digest 认证。

SSL

客户端和服务器应支持 SSL,尤其是在依赖 Basic HTTP 认证时保护密码。

会话状态

Git over HTTP 协议(与 HTTP 本身非常相似)在 HTTP 服务器端视角来看是无状态的。所有状态必须由客户端进程保留和管理。这允许在服务器端进行简单的轮循负载均衡,而无需担心状态管理。

客户端为了正确运行,不得要求服务器端进行状态管理。

服务器为了正确运行,不得要求 HTTP cookie。客户端可以在请求处理过程中存储和转发 HTTP cookie,如 RFC 2616 (HTTP/1.1) 所述。服务器应忽略客户端发送的任何 cookie。

通用请求处理

除非另有说明,客户端和服务器都应假定所有标准的 HTTP 行为。这包括(但不限于)

如果 $GIT_URL 处没有仓库,或者匹配 $GIT_URL 的位置指向的资源不存在,服务器不得响应 200 OK。服务器应响应 404 Not Found410 Gone 或任何其他不暗示资源按请求存在的合适 HTTP 状态码。

如果 $GIT_URL 处有仓库,但当前不允许访问,服务器必须响应 403 Forbidden HTTP 状态码。

服务器应同时支持 HTTP 1.0 和 HTTP 1.1。服务器应支持请求和响应体的分块编码。

客户端应同时支持 HTTP 1.0 和 HTTP 1.1。客户端应支持请求和响应体的分块编码。

服务器可以返回 ETag 和/或 Last-Modified 标头。

客户端可以通过包含 If-Modified-Since 和/或 If-None-Match 请求标头来重新验证缓存的实体。

如果请求中出现了相关标头且实体未更改,服务器可以返回 304 Not Modified。客户端必须将 304 Not Modified200 OK 视为相同,并重用缓存的实体。

如果 Cache-Control 和/或 Expires 标头允许缓存,客户端可以不经重新验证就重用缓存的实体。客户端和服务器必须遵循 RFC 2616 来处理缓存控制。

发现引用

所有 HTTP 客户端在进行 fetch 或 push 交换时,都必须先发现远程仓库中可用的引用。

愚蠢客户端

仅支持“愚蠢”协议的 HTTP 客户端必须通过请求仓库的特殊 info/refs 文件来发现引用。

愚蠢 HTTP 客户端必须向 $GIT_URL/info/refs 发起 GET 请求,不带任何搜索/查询参数。

C: GET $GIT_URL/info/refs HTTP/1.0
S: 200 OK
S:
S: 95dcfa3633004da0049d3d0fa03f80589cbcaf31	refs/heads/maint
S: d049f6c27a2244e12041955e262a404c7faba355	refs/heads/master
S: 2cb58b79488a98d2721cea644875a8dd0026b115	refs/tags/v1.0
S: a3c2e2402b99163d1d59756e5f207ae21cccba4c	refs/tags/v1.0^{}

返回的 info/refs 实体的 Content-Type 应为 text/plain; charset=utf-8,但可以是任何内容类型。客户端不得尝试验证返回的内容类型。愚蠢服务器不得返回以 application/x-git- 开头的返回类型。

可以返回 Cache-Control 标头来禁用对返回实体的缓存。

检查响应时,客户端应只检查 HTTP 状态码。有效的响应是 200 OK304 Not Modified

返回的内容是描述每个 ref 及其已知值的 UNIX 格式文本文件。文件应按 C 语言环境排序。文件不应包含名为 HEAD 的默认 ref。

info_refs   =  *( ref_record )
ref_record  =  any_ref / peeled_ref
any_ref     =  obj-id HTAB refname LF
peeled_ref  =  obj-id HTAB refname LF
 obj-id HTAB refname "^{}" LF

智能客户端

支持“智能”协议(或同时支持“智能”和“愚蠢”协议)的 HTTP 客户端必须通过请求仓库的带参数的 info/refs 文件来发现引用。

请求必须只包含一个查询参数,service=$servicename,其中 $servicename 必须是客户端希望联系以完成操作的服务名称。请求不得包含其他查询参数。

C: GET $GIT_URL/info/refs?service=git-upload-pack HTTP/1.0

愚蠢服务器回复

S: 200 OK
S:
S: 95dcfa3633004da0049d3d0fa03f80589cbcaf31	refs/heads/maint
S: d049f6c27a2244e12041955e262a404c7faba355	refs/heads/master
S: 2cb58b79488a98d2721cea644875a8dd0026b115	refs/tags/v1.0
S: a3c2e2402b99163d1d59756e5f207ae21cccba4c	refs/tags/v1.0^{}

智能服务器回复

S: 200 OK
S: Content-Type: application/x-git-upload-pack-advertisement
S: Cache-Control: no-cache
S:
S: 001e# service=git-upload-pack\n
S: 0000
S: 004895dcfa3633004da0049d3d0fa03f80589cbcaf31 refs/heads/maint\0multi_ack\n
S: 003fd049f6c27a2244e12041955e262a404c7faba355 refs/heads/master\n
S: 003c2cb58b79488a98d2721cea644875a8dd0026b115 refs/tags/v1.0\n
S: 003fa3c2e2402b99163d1d59756e5f207ae21cccba4c refs/tags/v1.0^{}\n
S: 0000

客户端可以在 Git-Protocol HTTP 标头中以冒号分隔的字符串形式发送额外参数(参见 gitprotocol-pack[5])。

使用 git-upload-pack[1]--http-backend-info-refs 选项。

愚蠢服务器响应

愚蠢服务器必须以愚蠢服务器响应格式进行响应。

有关愚蠢服务器响应的更详细描述,请参阅前面愚蠢客户端下的部分。

智能服务器响应

如果服务器不识别请求的服务名称,或者请求的服务名称已被服务器管理员禁用,则服务器必须响应 403 Forbidden HTTP 状态码。

否则,智能服务器必须为请求的服务名称响应智能服务器响应格式。

应使用 Cache-Control 标头来禁用对返回实体的缓存。

Content-Type 必须是 application/x-$servicename-advertisement。如果返回了其他内容类型,客户端应回退到愚蠢协议。回退到愚蠢协议时,客户端不应向 $GIT_URL/info/refs 发起额外请求,而应使用已有的响应。如果不支持愚蠢协议,客户端不得继续。

客户端必须验证状态码是 200 OK 还是 304 Not Modified

客户端必须验证响应实体的开头五个字节匹配正则表达式 ^[0-9a-f]{4}#。如果此测试失败,客户端不得继续。

客户端必须将整个响应解析为一系列 pkt-line 记录。

客户端必须验证第一个 pkt-line 是 # service=$servicename。服务器必须将 $servicename 设置为请求参数值。服务器应在此行末尾包含一个 LF。客户端应忽略行末的 LF。

服务器必须以魔术 0000 结束 pkt-line 标记符来终止响应。

返回的响应是一个 pkt-line 流,描述每个 ref 及其已知值。流应按 C 语言环境排序。流应包含名为 HEAD 的默认 ref 作为第一个 ref。流必须在第一个 ref 后面通过 NUL 包含功能声明。

如果“version=1”作为额外参数发送,“version 1”将包含在返回的响应中。

smart_reply     =  PKT-LINE("# service=$servicename" LF)
     "0000"
     *1("version 1")
     ref_list
     "0000"
ref_list        =  empty_list / non_empty_list
empty_list      =  PKT-LINE(zero-id SP "capabilities^{}" NUL cap-list LF)
non_empty_list  =  PKT-LINE(obj-id SP name NUL cap_list LF)
     *ref_record
cap-list        =  capability *(SP capability)
capability      =  1*(LC_ALPHA / DIGIT / "-" / "_")
LC_ALPHA        =  %x61-7A
ref_record      =  any_ref / peeled_ref
any_ref         =  PKT-LINE(obj-id SP name LF)
peeled_ref      =  PKT-LINE(obj-id SP name LF)
     PKT-LINE(obj-id SP name "^{}" LF

智能服务 git-upload-pack

此服务从 $GIT_URL 指向的仓库读取。

客户端必须首先使用 $GIT_URL/info/refs?service=git-upload-pack 进行引用发现。

C: POST $GIT_URL/git-upload-pack HTTP/1.0
C: Content-Type: application/x-git-upload-pack-request
C:
C: 0032want 0a53e9ddeaddad63ad106860237bbf53411d11a7\n
C: 0032have 441b40d833fdfa93eb2908e52742248faf0ee993\n
C: 0000
S: 200 OK
S: Content-Type: application/x-git-upload-pack-result
S: Cache-Control: no-cache
S:
S: ....ACK %s, continue
S: ....NAK

客户端不得重用或重新验证缓存的响应。服务器必须包含足够的 Cache-Control 标头以防止响应被缓存。

服务器应支持此处定义的所有功能。

客户端必须在请求正文中至少发送一个“want”命令。客户端不得引用在引用发现过程中未出现在响应中的 ID,除非服务器声明了 allow-tip-sha1-in-wantallow-reachable-sha1-in-want 功能。

compute_request   =  want_list
       have_list
       request_end
request_end       =  "0000" / "done"
want_list         =  PKT-LINE(want SP cap_list LF)
       *(want_pkt)
want_pkt          =  PKT-LINE(want LF)
want              =  "want" SP id
cap_list          =  capability *(SP capability)
have_list         =  *PKT-LINE("have" SP id LF)

待办:进一步记录。

协商算法

选择最小 pack 的计算过程如下(C = 客户端,S = 服务器)

初始化步骤

C:使用引用发现获取广告中的引用。

C:将看到的任何对象放入集合 advertised

C:构建一个空集合 common,用于存放稍后确定在两端都存在的对象。

C:构建一个集合 want,包含客户端希望获取的 advertised 中的对象,基于引用发现期间看到的内容。

C:启动一个队列 c_pending,按提交时间排序(最新的先弹出)。添加所有客户端引用。当一个提交从队列中弹出时,其父提交应自动插入回队列。提交必须只进入队列一次。

一次计算步骤

C:发送一个 $GIT_URL/git-upload-pack 请求

C: 0032want <want-#1>...............................
C: 0032want <want-#2>...............................
....
C: 0032have <common-#1>.............................
C: 0032have <common-#2>.............................
....
C: 0032have <have-#1>...............................
C: 0032have <have-#2>...............................
....
C: 0000

流被组织成“命令”,每个命令单独出现在一个 pkt-line 中。在命令行中,直到第一个空格的文本是命令名,行中到第一个 LF 的其余部分是值。命令行以 LF 作为 pkt-line 值的最后一个字节终止。

命令必须按以下顺序出现,如果它们出现在请求流中

  • “want”

  • “have”

流以 pkt-line flush ( 0000 ) 终止。

一个“want”或“have”命令必须有一个十六进制格式的对象名称作为其值。多个对象名称必须通过发送多个命令来发送。对象名称必须使用通过 object-format 功能协商的对象格式(默认为 SHA-1)给出。

have 列表是通过从 c_pending 中弹出前 32 个提交来创建的。如果 c_pending 为空,则可以提供更少的提交。

如果客户端已发送 256 个“have”提交,并且尚未收到来自 s_common 的其中一个,或者客户端已清空 c_pending,则它应包含一个“done”命令,告知服务器它不会继续。

C: 0009done

S:解析 git-upload-pack 请求

验证 want 中的所有对象是否可以直接从 ref 访问。

服务器可以向后遍历历史记录或通过 reflog 来允许稍微过时的请求。

如果没有收到“want”对象,则发送错误:待办:定义未请求“want”的错误。

如果任何“want”对象不可访问,则发送错误:待办:定义无效“want”的错误。

创建一个空列表 s_common

如果发送了“have”

按客户端提供的顺序遍历对象。

对于每个对象,如果服务器有可从 ref 访问的对象,则将其添加到 s_common。如果将提交添加到 s_common,则不添加任何祖先,即使它们也出现在 have 中。

S:发送 git-upload-pack 响应

如果服务器找到了要打包的封闭对象集,或者请求以“done”结束,则响应 pack。待办:记录基于 pack 的响应。

S: PACK...

返回的流是 git-upload-pack 服务支持的 side-band-64k 协议,pack 嵌入在流 1 中。来自服务器端的进度消息可以出现在流 2 中。

这里的“封闭对象集”被定义为至少存在一条从每个“want”到至少一个“common”对象的路径。

如果服务器需要更多信息,它会响应一个状态继续响应:待办:记录非 pack 响应。

C:解析 upload-pack 响应:待办:记录解析响应。

进行另一次计算步骤。

智能服务 git-receive-pack

此服务从 $GIT_URL 指向的仓库读取。

客户端必须首先使用 $GIT_URL/info/refs?service=git-receive-pack 进行引用发现。

C: POST $GIT_URL/git-receive-pack HTTP/1.0
C: Content-Type: application/x-git-receive-pack-request
C:
C: ....0a53e9ddeaddad63ad106860237bbf53411d11a7 441b40d833fdfa93eb2908e52742248faf0ee993 refs/heads/maint\0 report-status
C: 0000
C: PACK....
S: 200 OK
S: Content-Type: application/x-git-receive-pack-result
S: Cache-Control: no-cache
S:
S: ....

客户端不得重用或重新验证缓存的响应。服务器必须包含足够的 Cache-Control 标头以防止响应被缓存。

服务器应支持此处定义的所有功能。

客户端必须在请求正文中至少发送一个命令。在请求正体的命令部分,客户端应将通过引用发现获得的 ID 作为 old_id 发送。

update_request  =  command_list
     "PACK" <binary-data>
command_list    =  PKT-LINE(command NUL cap_list LF)
     *(command_pkt)
command_pkt     =  PKT-LINE(command LF)
cap_list        =  *(SP capability) SP
command         =  create / delete / update
create          =  zero-id SP new_id SP name
delete          =  old_id SP zero-id SP name
update          =  old_id SP new_id SP name

待办:进一步记录。

GIT

Git[1] 套件的一部分