Git的传输协议是如何工作的

时间:2017-03-23 13:17:34

标签: git

我和Git一起工作了一年多,现在我必须向我们小组的其他人解释。这就是为什么我需要更多的背景。 去年我去了Git Book的大部分内容,最近我继续第10章。在10.6章我完全被卡住了:

  

让我们按照simplegit库的http-fetch过程进行操作:

$ git clone http://server/simplegit-progit.git
     

此命令执行的第一件事是下拉info / refs文件。   该文件由update-server-info命令编写,这就是原因   你需要启用它作为HTTP的后接收挂钩   运输正常:

=> GET info/refs
ca82a6dff817ec66f44342007202690a93763949     refs/heads/master

我有一个小型测试回购https://github.com/to_my/repogit clone效果很好。但

  • 文件夹info/refs在哪里?我只在/.git/info/exclude ...
  • 找到了clone
  • 我应该如何使用update-server-info命令?它是某种方式的git clone的一部分吗?
  • 我很遗憾地失去了" ...这就是为什么你需要启用它作为后接收钩子"虽然我理解钩子(我认为)并使用预提交钩子来自动增加包版本。
  • 我无法在git bash work中获得命令GET info/refs

很抱歉,如果问题很愚蠢,但我不理解如何将这些文章放在一起。

4 个答案:

答案 0 :(得分:1)

嗯,你正在进入管道细节;即使你必须向一个同事团队解释Git,我也会惊讶于需要这种程度的细节......

无论如何,info/refs文件只存在于远程服务器上的HTTP访问中。您可能在本地仓库中找不到它(并且不需要它)。 (这个场景中的遥控器可能是一个简单的仓库,顺便说一句,所以info将位于repo根目录,因为裸存储库没有工作树并且放置你曾经看过的文件{{ 1}}在根处。)

如果我们的遥控器有类似github,tfs等的东西......那么你就不用担心这一点,因为服务器会管理好的东西。我想如果你把repo作为静态内容从一个普通的旧Web服务器提供,那么这就很重要了,你必须设置钩子。

大多数用户永远不会使用或看到.git命令;顾名思义,它是用于服务器方面的存储 - 遥控器 - 以弥补缺乏git感知的HTTP服务器。

接收推送后调用post-receive钩子;所以在一个愚蠢的服务器场景中,你在遥控器上设置这个钩子,这样当你按下它时,它会通过更新某些信息(如refs文件)来响应。

您正在查看的update-server-info命令是一个HTTP命令,当您进行提取时,git客户端会在必要时运行。

答案 1 :(得分:1)

  

文件夹info / refs在哪里?我只找到一个/.git/info/exclude克隆...

没有这样的文件夹(它不是目录),但是 - .git/info/refs - 将是文件的位置,如果有的话在那里存档。

  

我应该如何使用update-server-info命令?它是某种方式的git clone的一部分吗?

一般来说,你应该使用它:它只适用于“哑”运输。 “智能”(双向对话)传输不需要它。

  

我很失败,因为“...这就是为什么你需要启用它作为后接收钩子”虽然我理解钩子(我想)并使用预提交钩子来自动增加包版本。 / p>

如果由于某种原因,您想要启用哑传输,则需要在每次需要创建或更新时运行某些内容来创建或更新多个文件。每当引用更改时,info/refs文件都需要更新,因此运行“某事”的好地方是在post-receive钩子中。 “某事”是命令git update-server-info

请注意,如果您没有在服务器上运行只推送裸存储库,那么运行git update-server-info后接收脚本是不够的,因为可以通过其他方式添加提交和其他对象(手动{例如{1}}。在这种情况下,您可以使用例如cron作业来创建或更新基于时钟驱动的哑传输信息。

  

我无法在git bash工作中获得命令git commit

如果文件存在,您可以通过HTTP获取,例如,从浏览器或使用GET info/refs命令。

答案 2 :(得分:1)

git传输协议的另一方面在于其数据包管理,包括请求“ HAVE”时的ACK:

在Git 2.27(2020年第二季度)之前,服务于“ git clone”和“ git fetch”的v2协议的服务器端尚未准备好在意外的地方看到delim数据包,这导致崩溃。

请参见commit cacae43前的commit 4845b77(2020年3月29日)和commit 88124abJeff King (peff)(2020年3月27日)。
(由Junio C Hamano -- gitster --commit 5ee5788中合并,2020年4月22日)

upload-pack:处理意外的delim数据包

签名人:杰夫·金

在处理v2 ls-refs fetch命令的参数列表时,我们像这样循环:

while (packet_reader_read(request) != PACKET_READ_FLUSH) {
        const char *arg = request->line;
 ...handle arg...
}

读取和处理数据包,直到看到刷新。这里隐藏的假设是,PACKET_READ_FLUSH以外的任何东西都将给我们有效的数据包数据以供读取。但这不是事实。 PACKET_READ_DELIMPACKET_READ_EOF>packet->line保留为NULL,我们将进行段错误尝试

相反,我们应该遵循客户端上展示的更为谨慎的模型(例如,在process_capabilities_v2)中:只要我们收到正常的数据包,就保持循环,然后确保由于以下原因而退出循环)真正的同花顺。 这样可以修复段错误并正确诊断来自客户端的任何意外输入。


在Git 2.27(2020年第二季度)之前,upload-pack协议v2在找到共同祖先之前就放弃了太早,导致从项目的分支中获取浪费。

此问题已得到纠正,以符合v0协议的行为。

请参见commit 2f0a093commit 4fa3f00commit d1185aaJonathan Tan (jhowtan)(2020年4月28日)。
(由Junio C Hamano -- gitster --commit 0b07eec中合并,2020年5月1日)

fetch-pack:在协议v2中,in_vain仅在ACK之后

签名人:Jonathan Tan
评论人:乔纳森·尼德

获取时,如果Git发送了至少MAX_IN_VAIN(即256条)“有”行而没有任何ACK版本,则Git停止协商。
但这应该仅在第一个ACK之后触发,如pack-protocol.txt所说:

但是,如果我们在上一轮中收到至少一个“ ACK%scontinue”,则在规范的客户端实现中将打开256个限制(仅 )。这有助于确保在我们完全放弃之前找到至少一个共同祖先。

协议v0的代码路径遵循此规则,但协议v2则不遵循此规则,从而导致协商周期缩短,但打包文件明显增大。
仅在收到至少一个ACK之后,教协议v2的代码路径检查此标准。


由于使用了2.27(其中v2不是默认设置),因此v2仍是默认设置,为2.28。

请参见commit 3697caf

config:让feature.experimental暗示protocol.version=2

Git 2.26使用协议v2作为其默认协议,但是在发布后不久,用户注意到协议v2协商代码在从远比其他遥远的某些远程进行抓取时容易失败(例如linux-next.git与Linus的linux.git)。
该问题已由0b07eec(合并分支“ jt/v2-fetch-nego-fix”, 2020-05-01,Git v2.27.0-rc0),但请谨慎使用,我们将协议v0作为2.27中的默认值,以便为解决其他未预期的问题花一些时间 表面。

为此,让我们确保用户使用feature.experimental标志 do 来请求最新消息。
这样,我们可以在新协议版本的更广泛受众中获得经验,并更有信心在将来某个将来的Git版本中默认为所有用户启用该协议。

实施说明:feature.experimental中的其余repo-settings.c选项不适用,因为它们与存储库对象相关,而此代码路径用于诸如“ {{1} }”,不需要存储库。


在Git 2.28(2020年第三季度)中,“获取/克隆”协议已更新,以允许服务器指示客户端获取预打包的打包文件以及打包对象数据。电线

请参见commit cae2ee1Ramsay Jones (``)(2020年6月15日)。
参见commit dd4b732commit 9da69a6commit acaaca7commit cd8402ecommit fd194ddcommit 8d5d2a3commit 8e6adb6commit eb05349,{ {3}}(2020年6月10日)作者:commit 9cb3cab
(由Jonathan Tan (jhowtan)Junio C Hamano -- gitster --中合并,2020年6月25日)

commit 34e849b:支持多个包锁文件

签名人:Jonathan Tan

每当获取导致下载包文件时,都会生成一个.keep文件,以便可以保留该包文件(例如,从正在运行的“ git repack”),直到写入引用该包文件内容的引用为止

在随后的补丁程序中,使用协议v2成功提取可能会导致生成多个.keep文件。因此,请教git ls-remote和传输机制来支持多个.keep文件。

实施说明:

  • fetch-pack通常不会生成fetch_pack()文件,因此不受此更改或将来更改的影响。
    但是,它具有未公开的“ .keep”功能,builtin/fetch-pack.c在实现“ --lock-pack”远程帮助程序命令时使用。
    为了与远程帮助程序协议保持一致,将只写入一行“ fetch”;其余的将对stderr发出警告。
    但是,实际上,永远不会写警告,因为remote-curl.clock”仅用于协议v0 / v1(不会生成多个fetch文件)。 (协议v2使用“无状态连接”命令,而不是“ .keep”命令。)

  • remote-curl.c的优化之处在于,如果目标对象位于已知是自包含且已连接的包中,则无需对ref进行连接检查。如果有多个packfile,则无法再执行此优化。

Cf。 connected.c

此功能允许服务器将其packfile响应的一部分作为URI提供。 这允许服务器设计提高带宽和CPU使用率的可伸缩性 (例如,通过CDN提供一些数据),以及(将来)提供 对客户可恢复性的某种度量。

此功能仅在协议版本2中可用。


通过无状态RPC /智能HTTP传输,

Packfile URIs git fetch --depth=”在服务器端无法很好地处理来自客户端的EOF。

作为传输协议的一部分,此问题已在Git 2.30(Q1 2021)中得到修复。

请参见mancommit fb3d1a0(2020年10月30日)。
(由Daniel Duvall (marxarelli)Junio C Hamano -- gitster --中合并,2020年11月18日)

commit d1169be:在fetch

之前允许无状态客户端EOF

签名人:Daniel Duvall

在进行深度协商的无状态包文件协商过程中,无状态RPC客户端(例如haves)将发送多个git-remote-curl请求,第一个请求仅包含want / shallows / deepens / filters,随后的包含拥有/完成。

upload-pack处理此类请求时,在协商循环期间输入upload-pack而不检查客户端是否挂断,可能会导致意外的EOF,并导致带有消息“ {{1}”的get_common_commits }”。

现实世界的影响包括:

  • 客户端通过服务器与die()进行对话,而该服务器不检查CGI的退出代码(例如fatal: the remote end hung up unexpectedly不知道也不关心致命事件。它会继续处理反应体正常。
  • 与服务器对话的客户端会检查出口代码并返回错误的HTTP状态,结果将失败,并显示消息“ git-http-backend
  • 运行出现故障的服务器的管理员必须通过修补处理mod_cgi)执行的代码来解决此问题,以忽略退出代码或采用其他启发式方法。
  • 即使退出代码未显示为HTTP服务器端错误状态,管理员也可能不得不处理与失败相关的“ error: RPC failed; HTTP 500 curl 22 The requested URL returned error: 500.”日志垃圾邮件。

为避免这些与EOF相关的致命事故,请git-http-backend在发送浅/浅线(随后为刷新)和读取客户端hung up unexpectedly之间轻轻观察一下EOF。
如果客户此时挂断了电话,请正常退出。

答案 3 :(得分:1)

在Git 2.30(Q1 2021)中,教导传输层在获取/推送事务期间有选择地交换trace2子系统分配的会话ID。

请参见commit a2a066dcommit 8c48700commit 8295946commit 1e905bbcommit 23bf486commit 6b5b6e4commit 8073d75,{{3} },commit 791e1adcommit e97e1cfcommit 81bd549(2020年11月11日)作者:commit f5cdbe4
(由Josh Steadmon (steadmon)Junio C Hamano -- gitster --中合并,2020年12月8日)

commit 01b8886:在v2功能中发布会话ID

签名人:Josh Steadmon

当transfer.advertiseSID为true时,通过新的session-id功能公布所有协议v2连接的服务器的会话ID。

并且:

serve:发布会话ID的新功能

签名人:Josh Steadmon

在以后的补丁中,我们将为Git服务器和客户端添加通过协议功能发布唯一会话ID的功能。当客户端和服务器日志均可用时,这可以简化调试。

technical/protocol-capabilities现在包含在其docs中:

session-id=<session id>


服务器可能会发布可用于标识此过程的会话ID 跨多个请求。客户端可以将自己的会话ID播回给 服务器也是如此。

会话ID对于给定的进程应该是唯一的。它们必须适合 分组行,并且不得包含不可打印或空格字符。

technical/protocol-v2现在包含在其man page中:

session-id=<session id>


The server may advertise a session ID that can be used to identify this process
across multiple requests. The client may advertise its own session ID back to
the server as well.

Session IDs should be unique to a given process. They must fit within a
packet-line, and must not contain non-printable or whitespace characters. 

Git 2.30的其他新修复程序(2021年第一季度):

fetch-pack”在看到无效的文件名时可以将NULL指针传递给unlink。错误检查已得到加强,以使其无法实现。

请参见man pagecommit 6031af3(2020年11月30日)。
(由René Scharfe (rscharfe)Junio C Hamano -- gitster --中合并,2020年12月8日)

commit eae47db:忽略无效的包锁定文件

签名人:RenéScharfe
评论人:泰勒·布劳

fetch-pack(“ fetch-pack:支持多个压缩包文件”,2020-06-10,Git v2.28.0-rc0-9da69a6539merge中列出)开始将string_list用作包锁定文件名,而不是单个字符串指针。
它还从NULL中删除了transport_unlock_pack()支票,该功能最终会删除这些锁定文件并释放其名称字符串。

index_pack_lockfile()如果不喜欢从传递给它的文件描述符中读取的内容,则可以返回NULL
声明unlink(2)不接受NULL指针(至少使用glibc)。
Undefined Behavior Sanitizer与Address Sanitizer一起检测到{10}(NULL)中transport_unlock_pack()的锁定文件名由make SANITIZE=address,undefined; cd t; ./t1060-object-corruption.sh传递给unlink(2)的情况。

恢复NULL检查以避免未定义的行为,但应将其放在源头,以便string_list中的项目数量反映有效锁定文件的数量。