Git
英语 ▾ 主题 ▾ 最新版本 ▾ gitprotocol-http 最后更新于 2.44.0

名称

gitprotocol-http - Git 基于 HTTP 的协议

概要

<over-the-wire-protocol>

描述

Git 支持两种基于 HTTP 的传输协议。一种是“哑”协议,它只需要连接服务器端的标准 HTTP 服务器,另一种是“智能”协议,它需要一个 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 中描述的基本身份验证。服务器应该支持基本身份验证,方法是依赖于放置在 Git 服务器软件前面的 HTTP 服务器。

服务器不应该为了身份验证或访问控制而要求 HTTP cookie。

客户端和服务器可以支持其他常见的基于 HTTP 的身份验证形式,例如摘要身份验证。

SSL

客户端和服务器应该支持 SSL,特别是为了在依赖基本 HTTP 身份验证时保护密码。

会话状态

从 HTTP 服务器端的角度来看,Git 通过 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 客户端都必须通过发现远程存储库上的可用引用来开始获取或推送交换。

哑客户端

仅支持“哑”协议的 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,但也可能是任何内容类型。客户端不应尝试验证返回的 Content-Type。哑服务器不应返回以 application/x-git- 开头的返回类型。

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

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

返回的内容是一个 UNIX 格式的文本文件,描述每个引用及其已知值。该文件应该按照 C 本地排序规则按名称排序。该文件不应该包含名为 HEAD 的默认引用。

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

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

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

哑服务器响应

哑服务器必须以哑服务器回复格式响应。

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

智能服务器响应

如果服务器不识别请求的服务名称,或者服务器管理员已禁用请求的服务名称,服务器必须返回 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 执行 ref 发现。

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" 命令。客户端不能引用 "want" 命令中的一个 ID,除非该 ID 出现在通过 ref 发现获得的响应中,除非服务器广告功能 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)

待办事项:进一步记录此内容。

协商算法

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

初始化步骤

C:使用 ref 发现来获取已广告的 refs。

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

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

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

C:启动一个队列 c_pending,按提交时间排序(从最新的开始弹出)。添加所有客户端 refs。当从队列中弹出提交时,其父级应该自动重新插入。提交只能进入队列一次。

一个计算步骤

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 刷新(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 中的所有对象是否可以直接从 refs 访问。

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

如果没有收到 "want" 对象,则发送错误:待办事项:如果未请求 "want" 行,则定义错误。

如果任何 "want" 对象不可访问,则发送错误:待办事项:如果请求无效的 "want",则定义错误。

创建一个空列表 s_common

如果发送了 "have"

循环遍历客户端提供的顺序中的对象。

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

S:发送 git-upload-pack 响应

如果服务器已找到一个要打包的封闭对象集或请求以 "done" 结束,则它将回复打包结果。待办事项:记录基于打包的响应

S: PACK...

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

此处,"封闭对象集"的定义是:从每个 "want" 到至少一个 "common" 对象至少有一条路径。

如果服务器需要更多信息,则它将回复一个状态继续响应:待办事项:记录非打包响应

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

执行另一个计算步骤。

智能服务 git-receive-pack

此服务从 $GIT_URL 指向的存储库读取数据。

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

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 标头以防止响应被缓存。

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

客户端必须在请求正文中发送至少一个命令。在请求正文的命令部分,客户端应该发送通过 ref 发现获得的 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] 套件的一部分

scroll-to-top