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

k4emjkb1  于 2023-04-28  发布在  Git
关注(0)|答案(5)|浏览(149)

我和Git一起工作了一年多,现在我必须向团队中的其他人解释这一点。这就是为什么我需要更多的支持。去年我浏览了Git Book的大部分内容,最近我继续阅读第10章。在第10.6章中,我完全卡住了:
让我们遵循simpegit库的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在哪里?在clone之后我只找到一个/.git/info/exclude。..
  • 如何使用update-server-info命令?它是git clone的一部分吗?
  • 我完全迷失了。”..这就是为什么你需要启用它作为一个post-receive hook”,尽管我理解hook(我认为),并使用一个pre-commit hook来自动增加包版本。
  • 我无法在git bash work中获取GET info/refs命令。

如果这些问题很愚蠢,我很抱歉,但我只是不明白如何把这些文档中的片段放在一起。

kuuvgm7e

kuuvgm7e1#

git传输协议的另一个方面是它的数据包管理,包括请求“HAVE”时的ACK:
在Git 2之前。27(2020年第二季度),服务于“git clone”和“git fetch”的v2协议的服务器端没有准备好在意想不到的地方看到delim数据包,这导致了崩溃。
参见commit cacae43(2020年3月29日)和commit 4845b77commit 88124ab(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我们将segfault尝试查看它
相反,我们应该遵循在客户端演示的更仔细的模型(例如:例如,在process_capabilities_v2)中:只要我们得到正常的数据包,就保持循环,然后确保我们由于真实的的刷新而打破了循环。修复segfault并正确诊断来自客户端的任何意外输入。
在Git 2之前。27(2020年第二季度),upload-pack协议v2在找到共同祖先之前过早放弃,导致从项目的分支中进行浪费的获取。
这已被纠正,以匹配v0协议的行为。
参见commit 2f0a093commit 4fa3f00commit d1185aa(2020年4月28日)by Jonathan Tan ( jhowtan )
(由Junio C Hamano -- gitster --合并于commit 0b07eec,2020年5月1日)

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

签字人:陈宗泽
审核人:乔纳森·尼德
当获取时,Git在发送了至少MAX_IN_VAIN(即256)个“have”行而没有任何ACK时停止协商。
但是这应该只在第一个ACK之后触发,正如pack-protocol.txt所说:
然而,如果我们在前一轮中收到至少一个“ACK %s continue”,则在规范客户端实现中将启用256限制 only。这有助于确保在我们完全给予之前至少找到一个共同的祖先。
协议v0的代码路径遵守这一点,但协议v2没有遵守这一点,导致更短的协商回合,但包文件明显更大。
仅在接收到至少一个ACK后,才示教协议v2的代码路径检查此标准。
由于工作在2。27(其中v2 * 不是 * 默认值),v2再次默认为2。28.
参见commit 3697caf

config:让功能。实验暗示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-rc 0),但为了谨慎起见,我们在2中使用协议v0作为默认值。27为任何其他意想不到的问题浮出水面争取一些时间。
为此,让我们确保用户请求使用该功能的出血边缘。实验标记 do get协议v2。
通过这种方式,我们可以获得更广泛的受众对新协议版本的经验,并且在未来的Git版本中为所有用户默认启用它时更有信心。
实施说明:这不是repo-settings.c中的其余feature.experimental选项,因为这些选项与存储库对象绑定,而这个代码路径用于不需要存储库的操作,如“git ls-remote”。
在Git 2.28(2020年第三季度)中,“fetch/clone”协议已经更新,允许服务器指示客户端获取预先打包的packfile(s)以及通过网络传输的打包对象数据
参见commit cae2ee1(2020年6月15日),作者Ramsay Jones (``)
请参阅commit dd4b732commit 9da69a6commit acaaca7commit cd8402ecommit fd194ddcommit 8d5d2a3commit 8e6adb6commit eb05349commit 9cb3cab(2020年6月10日)by Jonathan Tan ( jhowtan )
(由Junio C Hamano -- gitster --合并于commit 34e849b,2020年6月25日)

fetch-pack:支持多个Pack锁定文件

签字人:陈宗泽
每当提取导致包文件被下载时,生成keep文件,以便可以保留packfile(例如,从正在运行的“git repack”),直到引用packfile的内容写入refs。
在随后的补丁中,使用协议v2的成功获取可能导致多于一个。保持正在生成的文件。因此,示教fetch_pack()和传输机制以支持多个。保存文件。
实施说明:

但是,它有一个未记录的“--lock-pack”特性,由remote-curl.c在实现“fetch”远程助手命令时使用。
为了与远程助手协议保持一致,只会写入一行“lock”;其余的将导致对stderr的警告。

然而,在实践中,永远不会写入警告,因为remote-curl.cfetch“仅用于协议v0/v1(不会生成多个.keep文件)。(协议v2使用“stateless-connect”命令,而不是“fetch”命令。)

  • X1 E34 F1 X的优化之处在于,如果目标对象在已知是自包含和连接的包中,则不需要对引用进行连接性检查。如果有多个包文件,则无法再进行此优化。

参见Packfile URIs
此功能允许服务器将其packfile响应的一部分作为URI提供。这允许服务器设计提高带宽和CPU使用的可扩展性(例如,通过CDN提供一些数据),并(在未来)为客户端提供一些可恢复性措施。
此功能仅在协议版本2中可用。
git fetch --depth=(man)”通过无状态RPC /智能HTTP传输在服务器端处理客户端的EOF很差。
作为传输协议的一部分,这在Git 2中是固定的。30(二零二一年第一季)。
请参阅commit fb3d1a0(2020年10月30日)通过Daniel Duvall ( marxarelli )
(由Junio C Hamano -- gitster --合并至commit d1169be,2020年11月18日)

upload-pack:允许haves之前的无状态客户端EOF

签字人:丹尼尔·杜瓦尔
在给定深度的无状态包文件协商期间,无状态RPC客户端(例如:例如git-remote-curl)将发送多个upload-pack请求,第一个请求仅包含wants/shallows/deepens/filters,随后包含haves/done。
upload-pack处理此类请求时,如果不检查客户端是否已挂起,则输入get_common_commits可能会导致协商循环期间出现意外的EOF,并导致die()出现消息“fatal: the remote end hung up unexpectedly”。
真实的世界效果包括:

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

为了避免这些EOF相关的致命事件,请让upload-pack在发送浅/非浅行(随后刷新)和阅读客户端haves之间轻轻地查看EOF。
如果此时客户端已挂断,请正常退出。

inkz8wg9

inkz8wg92#

在Git 2.30(2021年第一季度)中,传输层被教导在获取/推送事务期间可以选择交换trace2子系统分配的会话ID。
请参阅commit a2a066dcommit 8c48700commit 8295946commit 1e905bbcommit 23bf486commit 6b5b6e4commit 8073d75commit 791e1adcommit e97e1cfcommit 81bd549commit f5cdbe4(2020年11月11日)by commit 8c48700
(由Junio C Hamano -- gitster --合并于commit 01b8886,2020年12月8日)

serve:在v2功能中通告会话ID

签字人:乔什·斯蒂德蒙
转移时。advertisesSID为true,则通过新的会话ID功能为所有协议v2连接通告服务器的会话ID。
并且:

docs:通告会话ID的新功能

签字人:乔什·斯蒂德蒙
在未来的补丁中,我们将为Git服务器和客户端添加通过协议功能通告唯一会话ID的功能。这样,当客户端和服务器日志都可用时,调试就更容易了。
technical/protocol-capabilities现在在其手册页中包括:

session-id=<session id>

服务器可以通告会话ID,该会话ID可以用于跨多个请求标识该进程。客户端也可以将其自己的会话ID通告回服务器。
会话ID对于给定进程应该是唯一的。它们必须适合一个包行,并且不能包含不可打印或空白字符。
technical/protocol-v2现在在其手册页中包括:

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(二零二一年第一季度):
fetch-pack“可以在看到无效文件名时将NULL指针传递给unlink;错误检查已被加强以使这不可能。
请参阅commit 6031af3(2020年11月30日)René Scharfe ( rscharfe )
(由Junio C Hamano -- gitster --合并于commit eae47db,2020年12月8日)

fetch-pack:忽略无效的包锁定文件

签字人:勒内·沙尔费
审核人:泰勒·布劳
9da69a6539(“fetch-pack:支持多个包锁文件”,2020-06-10,Git v2。28.0-rc 0--merge列于batch #5)开始使用string_list作为包锁文件名,而不是单个字符串指针。
它还从transport_unlock_pack()中删除了一个NULL检查,这个函数最终删除了这些锁文件并释放了它们的名称字符串。
如果index_pack_lockfile()不喜欢从传递给它的文件描述符中读取的内容,它可以返回NULL
unlink(2)被声明为不接受NULL指针(至少对于glibc)。
Undefined Behavior Sanitizer与Address Sanitizer一起检测到transport_unlock_pack()在t1060(make SANITIZE=address,undefined; cd t; ./t1060-object-corruption.sh)中将NULL锁定文件名传递给unlink(2)的情况。
恢复NULL检查以避免未定义的行为,但将其放在源代码中,以便string_list中的项数反映有效锁文件的数量。
传输层v2可能与commit graph introduced originally in Git 2.18 (Q2 2018)(在单独的文件中进行祖先遍历以优化图遍历所需的预先计算的信息)不兼容。
Ævar Arnfjörð Bjarmason在this thread中描述了您将看到的错误消息:

$ git status
    error: graph version 2 does not match version 1
$ ~/g/git/git --exec-path=$PWD status
    error: commit-graph version 2 does not match version 1
    On branch master
    [...]

在Git 2.31(2021年第一季度)中,commit-graph学会了使用正确的提交日期而不是生成编号来帮助拓扑修订遍历,以将自己与协议v1区分开来。
请参阅commit 5a3b130commit 8d00d7ccommit 1fdc383commit e8b6300commit c1a0911commit d7f9278commit 72a2bfccommit c0ef139commit f90fca6commit 2f9bbb6commit e30c5ee(2021年1月16日)by Abhishek Kumar ( abhishekkumar2718 )
(由Junio C Hamano -- gitster --合并于commit 8b4701a,2021年2月17日)

commit-graph:实现生成数据块

签字人:阿布舍克·库马尔
审核人:泰勒·布劳
审核人:德里克·斯托利
正如埃瓦尔所发现的,我们不能通过增加图的版本来区分代数v1和v2。
因此,在实现世代号v2之前的先决条件之一是以向后兼容的方式区分图版本。
我们将引入一个新的块,称为Generation DATa块(或GDAT)。
GDAT将存储修正的提交者日期偏移,而CDAT仍将存储拓扑级别。
旧Git不理解GDAT块,会忽略它,从CDAT阅读拓扑级别。
新的Git可以解析GDAT并利用新的代号,当GDAT块丢失时返回拓扑级别(就像旧Git编写的提交图一样)。
为了最小化存储修正提交日期所需的空间,Git将修正提交日期偏移量存储到commit-graph文件中,而不是修正提交日期。
这样每次提交可以节省4个字节,将GDAT块大小减少一半,但是偏移量可能会溢出分配给存储的4个字节。
由于这样的溢出非常罕见,并且应该非常罕见,我们使用以下溢出管理方案:
我们引入了一个新的提交图块Generation Data OVerflow('GDOV'),以存储偏移量大于GENERATION_NUMBER_V2_OFFSET_MAX的提交更正的提交日期。
如果偏移量大于GENERATION_NUMBER_V2_OFFSET_MAX,,则设置偏移量的MSB,其他位存储GDOV块中的已校正提交日期的位置,类似于如何维护额外边缘列表。
我们用下面的repo历史记录测试溢出相关代码:

F - N - U
         /         \
U - N - U            N
         \          /
          N - F - N

其中:

  • 由U表示的提交具有自Unix纪元起零秒的提交者日期,
  • 由N表示的提交具有自Unix纪元以来1112354055秒的提交者日期(测试套件的默认提交者日期),并且
  • 由F表示的提交具有自Unix纪元(2 ^ 31 - 2)秒的提交日期。

观察到的最大偏移量是2 ^ 31,刚好大到足以溢出。
这与v1向后兼容,因为:

commit-graph:仅当整个链都使用时才使用第2代

签字人:德里克·斯托利
签字人:阿布舍克·库马尔
审核人:泰勒·布劳
审核人:德里克·斯托利
由于Git的发布版本理解commit-graph的CDAT块中的代号,但不理解GDAT块,因此可能出现以下情况:
1.“New”Git用GDAT块编写提交图。
1.“Old”Git在顶部写了一个分割的提交图,没有GDAT块。
如果分割的提交图的每一层都是独立处理的,就像这次提交之前的情况一样,Git只检查当前层的chunk_generation_data指针,那么下层的提交(使用GDAT的一个)应该已经更正了提交日期作为其代号,而上层的提交将具有拓扑层次作为其代号。
更正的提交日期通常具有比拓扑级别大得多的值。
这意味着如果我们有两个提交,一个来自上层,另一个来自下层,那么父代的生成小于子代的生成的预期将被违反。
在测试中很难暴露此问题。
由于我们从人为的低代数开始,任何优先考虑代数的提交walk都会在遍历低代数的提交之前遍历所有高代数的提交。
在我尝试的所有情况下,提交图层本身“保护”任何不正确的行为,因为下层的提交都无法到达上层的提交。
在这种情况下,这个问题会表现为一个性能问题,特别是像“git log --graph”(man)这样的东西,因为低代数会导致入度队列在允许拓扑顺序队列向输出写入任何内容之前遍历下层的所有提交(取决于上层的大小)。
因此,当在分割提交图中写入新层时,只有最顶层具有GDAT块时,我们才写入GDAT块。
这保证了如果层具有GDAT块,则所有较低层也必须具有GDAT块。
重写层遵循类似的方法:如果(在拆分的提交图链中)被重写的层的集合下面的最顶层存在,并且它不包含GDAT块,则重写的结果也不具有GDAT块。
什么是“更正提交日期”?

commit-graph:实现更正提交日期

签字人:阿布舍克·库马尔
审核人:泰勒·布劳
审核人:德里克·斯托利
大部分准备工作完成后,让我们实现更正的提交日期。
提交的修正提交日期定义为

  • 没有父母的提交(根提交)已将提交日期更正为等于其提交者日期。
  • 具有至少一个父提交的修正提交日期等于其提交日期的最大值,并且比其父提交中的最大修正提交日期多一个。

作为特殊情况,时间戳为零的根提交(01.01.1970 00:00:00 Z)已将提交日期更正为1,以便能够与GENERATION_NUMBER_ZERO(即未计算的更正提交日期)区分开来。
为了最小化存储修正的提交日期所需的空间,Git将修正的提交日期偏移量存储到commit-graph文件中。
提交的更正提交日期偏移量定义为其更正提交日期与实际提交日期之间的差值。
存储正确的提交日期需要sizeof(timestamp_t)字节,在大多数情况下是64位(uintmax_t)
但是,仅使用32位就可以安全地存储已纠正的提交日期偏移。
这将使GDAT块的大小减半,这将使提交图文件的大小减少约6%。
然而,如果提交格式错误但有效并且提交者日期为0 Unix时间,则使用偏移量是有问题的,因为偏移量将与校正的提交日期相同,因此需要64位来正确存储。
虽然Git在这个阶段不会写出偏移量,但Git会将更正后的提交日期存储在结构commit_graph_data的成员生成中。
它将开始写入提交日期偏移量,并引入生成数据块。
这提高了性能:

commit-reach:在paint_down_to_common()中使用更正的提交日期

签字人:阿布舍克·库马尔
审核人:泰勒·布劳
审核人:德里克·斯托利
091f4cf(“commit:如果不需要,不要使用代号”,2018-08-30,Git v2。19.0-rc 2--merge)更改了paint_down_to_common()以使用提交日期而不是生成编号v1(拓扑级别),因为在某些拓扑上性能会下降。

实现了代号v2(更正的提交日期)后,我们不再需要依赖提交日期,而是可以使用代号。
例如,命令git merge-base(man)v4。Linux存储库上的8 v4.9遍历167468次提交,占用0.提交日期为135秒,提交日期为167496次,取0。157 s分别用于更正提交者日期。
当使用修正的提交日期时,Git遍历的提交数量几乎与提交日期相同,这个过程比较慢,因为每次比较我们都必须访问一个commit-slab(用于修正提交者日期),而不是访问struct成员(用于提交者日期)。
因为这已经导致了问题(如859fdc0commit-graph:define GIT_TEST_COMMIT_GRAPH,2018-08-29,Git v2.20.0-rc 0--merge listed in batch #1),我们在t6404-recursive-merge中禁用commit graph。
然后,仍然使用Git 2。31(2021年第一季度),修复了提交图文件在更正的提交日期数据周围的增量更新。
参见commit bc50d6ccommit fde55b0commit 9c2c0a8commit 448a39e(2021年2月2日)和commit 90cb1c4commit c4cc083(2021年2月1日)by Derrick Stolee ( derrickstolee )
(由Junio C Hamano -- gitster --合并至commit 5bd0b21,2021年2月17日)

commit-graph:分别计算各代

签字人:德里克·斯托利
审核人:泰勒·布劳
compute_generation_numbers()方法由3258c66(“commit-graph:计算代数”,2018-05-01,Git v2.19.0-rc 0--merge列在batch #1中)来计算现在称为“拓扑层次”的东西。
为了兼容起见,这些仍然存储在commit-graph文件中,而c1a0911(“commit-graph:implement corrected commit date”,2021-01-16,Git v2.31.0 --batch #9中列出的merge)更新了方法,以计算新版本的代号:更正提交日期
这是有道理的,为什么这些是分组。
它们对必要的提交执行非常相似的遍历,并在每个父节点上计算相似的最大值。
然而,将这两者结合在一起会以一种难以分开的微妙方式将它们混为一谈。
特别是,topo_level slab用于存储所有情况下的拓扑级别,但commit_graph_data_at(c)->generation成员根据现有提交图文件的状态存储不同的值。* 如果现有的提交图文件有一个“GDAT”块,那么这些值表示正确的提交日期。* 如果现有的commit-graph文件没有“GDAT”块,那么这些值实际上是拓扑级别。
此问题仅在将现有提交图文件升级为具有“GDAT”块的文件时才会发生。
当前的更改并没有解决这个升级问题,但是将实现分成两部分有助于这个过程,这将在下一个更改中进行。
重要的是,这有助于处理num_generation_data_overflows错误递增的情况,从而触发溢出块的写入。
并且:

commit-graph:要特别小心混血儿

签字人:德里克·斯托利
审核人:泰勒·布劳
当从没有提交日期的提交图升级到有修正提交日期的提交图时,有几件事需要考虑。
当计算新的提交图文件的代号时,我们需要确保commit_graph_data结构体的“generation”成员对于这些提交被设置为零。
不幸的是,当修正的提交日期不可用时,使用拓扑级别作为生成号的回退方法在这里给我们造成了伤害:解析提交注意到read_generation_data为假,并且用拓扑级别填充“generation”。
解决方案是迭代提交,解析提交以填充初始值,然后将生成值重置为零以触发重新计算。
此循环仅在现有提交图数据没有正确的提交日期时发生。
还有:
在Git 2.32(2021年第二季度)中,线上协议学习了一种新的请求类型,以在给定对象名称列表的情况下请求对象大小。
参见commit a2ba162(2021年4月20日),作者Bruno Albuquerque ( brunoga2 )
(由Junio C Hamano -- gitster --合并到commit eede711,2021年5月14日)

object-info:支持检索对象信息

签字人:布鲁诺·阿尔伯克基
有时候,在不完全下载对象的情况下获取对象的信息是很有用的。
添加“object-info”功能,使客户机可以使用完整的十六进制对象名称来请求与对象相关的信息。
目前只退回尺码。
technical/protocol-v2现在在其手册页中包括:

object-info

object-info是检索一个或多个对象信息的命令。它的主要目的是允许客户端基于此信息做出决策,而不必完全获取对象。对象大小是当前支持的唯一信息。
object-info请求接受以下参数:

  • size

请求为每个列出的对象id返回大小信息。

  • oid <oid>

向服务器指示客户端要获取其信息的对象。

object-info的响应是请求的对象id和相关请求信息的列表,每个由单个空格分隔。

output = info flush-pkt

info = PKT-LINE(attrs) LF)
*PKT-LINE(obj-info LF)

attrs = attr | attrs SP attrs

attr = "size"

obj-info = obj-id SP obj-size

响应git pushman)请求的“git receive-pack“(man)在中途被杀死时无法清除过时的锁文件,已在Git 2中更正。41(2023年第二季度)。
参见commit c55c306(2023年3月10日),作者Patrick Steinhardt ( pks-t )
(由Junio C Hamano -- gitster --合并于commit ea09dff,2023年3月21日)

receive-pack:修复失效时的包文件锁

导演:Jeff King
签字人:帕特里克·斯坦哈特
当接受git-receive-pack中的包文件时,我们将该包文件馈送到git-index-pack中以生成包文件索引。
由于包文件通常只包含无法访问的对象,直到引用被更新,因此并发运行的垃圾收集可能会试图立即删除包文件,从而导致损坏。
为了解决这个问题,我们要求git-index-pack在将packfile移动到位之前创建一个.keep文件,一旦处理完所有引用更新,该文件将再次被删除。
现在,在生产系统中,我们已经观察到这些.keep文件有时并没有像预期的那样被删除,结果是存储库往往会随着时间的推移而增长从未被删除的包文件。
这似乎是由于在我们将保留的包文件从隔离目录迁移到主对象数据库后,git-receive-pack被杀死时的竞争引起的。
虽然这个竞赛窗口通常很小,但可以通过安装proc-receive钩子来扩展它。
通过将锁文件注册为临时文件来修复此争用,以便在退出或接收信号时自动删除它。
以下是另一个用例/修复,说明了协议的工作方式:
不支持协议v2的传输在某些情况下无法正确回退到协议v0,这已经在Git 2中得到了纠正。41(2023年第二季度)。
参见commit eaa0fd6(2023年3月17日),作者Jeff King ( peff )
(由Junio C Hamano -- gitster --合并于commit f879501,2023年3月28日)

git_connect():修复将v2降级到v0时的边角情况

签字人:杰夫·金
git_connect()中有一段代码可以检查我们是否正在使用protocol_v2,进行推送,如果是,则将我们转到protocol_v0(因为我们只知道如何使用v2进行获取)。
但它忽略了一些角落的情况:
1.它检查“prog”变量,该变量实际上是远程端接收包的路径。
默认情况下,这只是“git-receive-pack”,但它可以是任意字符串(如“/path/to/git receive-pack等)。
在这种情况下,我们会意外地停留在v2模式。
1.除了“receive-pack“和“upload-pack“之外,还有一个我们期望的值:“upload-archive“用于处理“git archive --remote“(man)"。
receive-pack一样,它不理解v2,应该使用v0协议。
实际上,到目前为止,这两种方法都没有在真实的世界中引起bug。
我们确实向服务器发送了一个“我们理解v2”的探测,但是由于除了upload-pack之外没有服务器实现v2,所以它被忽略了。
但是如果我们为这些端点实现v2,这最终会成为一个问题,因为旧的客户端会错误地声称理解它,导致他们无法解析服务器响应。
我们可以通过传入程序路径和操作的“名称”来修复(1)。
我在这里将名称视为字符串,因为这是在transport_connect()中设置的模式,transport_connect()是我们的调用者之一(之前我们只是简单地丢弃了“name”值)。
我们可以通过只允许已知的v2协议(“upload-pack”)而不是阻止未知的协议(“receive-pack”和“upload-archive”)来修复(2)。
这意味着无论谁最终实现了v2推送,都必须调整这个列表,但这是合理的。
我们将在默认情况下做安全、保守的事情(坚持使用v0),任何使用v2的人都会很快意识到这个点需要更新。

2skhul33

2skhul333#

注意:启动Git 2。18(2018年第二季度),git传输协议随着v2的实现而发展。
使用Git 2.26(2020年第一季度),it is the default。这是“不”在2。27(2020年第2季度,见本答案的结尾,以及follow-up answer)。又是在2。28(2020年第三季度)
参见commit a4d78cecommit 0f1dc53commit 237ffedcommit 884e586commit 8ff14edcommit 49e85e9commit f08a5d4commit f1f4d8acommit edc9cafcommit 176e85ccommit b1c2edfcommit 1aa8ddecommit 40fc51ecommit f7e2050commit 685fbd3commit 3145ea9commit 5b872ffcommit 230d7ddcommit b4be741commit 1af8ae1(15 Mar 2018)by Brandon Williams ( mbrandonw ) .
(由Junio C Hamano -- gitster --合并于commit 9bfa0f9,2018年5月8日)
完整的规范在Documentation/technical/protocol-v2.txt中:
协议v2将在以下方面改进v1:

  • 一个服务将支持多个命令,而不是多个服务名称
  • 可轻松扩展,因为功能被移动到协议中它们自己的部分,不再隐藏在NUL字节后面,也不再受到pkt-line大小的限制。
  • 分离出隐藏在NUL字节后面的其他信息(例如:例如,代理字符串作为功能,symrefs可以使用'ls-refs'请求
  • 除非明确要求,否则将省略参考广告
    *ls-refs命令,用于显式请求某些引用
  • 设计时考虑了http和stateless-rpc。使用清晰的flush语义,http远程帮助器可以简单地充当代理
    在协议v2中,通信是面向命令的

当第一次联系服务器时,将通告功能列表。这些能力中的一些将是客户端可以请求执行的命令。一旦命令完成,客户端就可以重新使用连接并请求执行其他命令。
info/refs仍然是客户端要查询的服务器端点,如HTTP传输部分所述:
当使用http://https://传输时,客户端发出一个“智能”info/refs请求,如http-protocol.txt中所述,并通过在Git-Protocol报头中提供“version=2”来请求使用v2。

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

v2服务器会回复:

S: 200 OK
   S: <Some headers>
   S: ...
   S:
   S: 000eversion 2\n
   S: <capability-advertisement>

随后的请求将直接发送到服务$GIT_URL/git-upload-pack。(这对git-receive-pack也是一样的)。
我们的目标是拥有更多的能力:
有两种不同类型的功能:

  • 正常功能,可用于传递信息或更改请求行为,以及
    *命令,这是客户端想要执行的核心操作(fetch、push等)。
    协议版本2默认无状态

这意味着所有命令必须只持续一轮,并且从服务器端的Angular 来看是无状态的,除非客户端请求了指示状态应由服务器维护的能力。
客户端必须不需要服务器端的状态管理来正常工作。
这允许在服务器端进行简单的循环负载平衡,而无需担心状态管理。
最后:
ls-refs是用于在v2中请求引用通告的命令。
与当前的引用通告不同,ls-refs接受参数,这些参数可用于限制从服务器发送的引用。
并且:
fetch是v2中用于获取packfile的命令。
它可以被看作是v1 fetch的修改版本,其中ref-advertisement被剥离(因为ls-refs命令填充了该角色),消息格式被调整以消除冗余并允许轻松添加未来的扩展。
自那次提交(5月10日)以来,协议V2已在Google博客文章“Introducing Git protocol version 2”中由Brandon Williams正式宣布(5月28日)。
在这两种情况下:
基本命令中不支持的其他功能将以空格分隔的功能列表的形式作为功能通告中的命令值进行通告:“<command>=<feature 1> <feature 2>
请参阅commit 5e3548ecommit ff47322commit ecc3e53(2018年4月23日)by Brandon Williams ( mbrandonw )
(由Junio C Hamano -- gitster --合并到commit 41267e9,2018年5月23日)

serve:介绍server-option功能

在协议版本2中引入“server-option”功能。
这使将来的客户端能够在使用协议版本2时在命令请求中发送服务器特定选项。

fetch:使用协议v2时发送服务器选项

通过在cmdline上通过'-o'或'--server-option'指定服务器选项,教fetch选择接受服务器选项。
当使用协议版本2执行fetch通信时,这些服务器选项被发送到远程端。
如果使用v2以外的协议进行通信,则忽略提供的选项,并且不将其发送到远程端。
对于git ls-remote也是如此。
传输协议v2学习支持Dec. 2017 with Git 2.16中的部分克隆。
参见Jonathan Tan ( jhowtan )commit ba95710commit 5459268(2018年5月3日)和commit 7cc6ed2(2018年5月2日)。
(由Junio C Hamano -- gitster --合并至commit 54db5c0,2018年5月30日)

{fetch,upload}-pack:在协议v2中支持过滤器

fetch-pack/upload-pack协议v2是独立于过滤器参数(用于部分获取)开发的,因此它不包括对过滤器参数的支持。添加对筛选器参数的支持。

与传统协议一样,只有配置了uploadpack.allowfilter,服务器才会通告并支持“filter”。
与传统协议一样,如果指定了“--filter”,客户端将继续发出警告,但服务器不会通告它。
Git 2.19(Q3 2018)改进了git传输协议v2的fetch部分:
参见commit ec06283commit d093bc7commit d30fe89commit af1c90dcommit 21bcf6e(2018年6月14日)和commit af00855commit 34c2903(2018年6月6日)by Jonathan Tan ( jhowtan )
(由Junio C Hamano -- gitster --合并至commit af8ac73,2018年8月2日)

fetch-pack:介绍协商器API

引入新文件fetch-negotiator.{h,c},它包含一个API,在该API的背后抽象了协商的细节

fetch-pack:使用参考修剪“已”发送

在使用协议v2的协商中,获取包有时不充分利用在ref通告中获得的信息:具体地,如果服务器通告客户端也具有的提交,则客户端永远不需要通知服务器它具有提交的父提交,因为它可以仅告诉服务器它具有所通告的提交并且它知道服务器可以并且将推断其余提交。
Git 2.20(Q4 2018)修复了git ls-remotes
参见commit 6a139cdcommit 631f0f8(2018年10月31日)by Jeff King ( peff )
(由Junio C Hamano -- gitster --合并至commit 81c365b,2018年11月13日)
git ls-remote $there foo被最近的协议v2更新破坏,并停止显示与不是refs/{heads,tags}/foo的'foo'匹配的引用,这已被修复。
Git 2.20修复了git fetch,当通过协议v2进行对话时,它在解析来自另一端的响应时有点松散。
参见commit 5400b2a(2018年10月19日)by Jonathan Tan ( jhowtan )
(由Junio C Hamano -- gitster --合并至commit 67cf2fa,2018年11月13日)

fetch-pack:更精确地解析v2响应

协议v2响应中的每个部分后面都有一个DELIM数据包(表示后面还有更多部分)或一个FLUSH数据包(表示后面没有部分)。
但是当解析“acknowledgments”部分时,do_fetch_pack_v2()可以接受这两个部分,但是仅根据“acknowledgments”部分的内容来确定是否继续阅读,而不是根据是否阅读了DELIMFLUSH
与协议兼容的服务器没有问题,但在与提供意外附加部分的服务器通信时,可能会导致令人困惑的错误消息。考虑一个在“acknowledgments“之后发送“new-section“的服务器:

  • 客户端写入请求
  • 客户端读取不包含“ready”的“acknowledgments”部分,则DELIM
  • 由于没有“就绪”,客户端需要继续协商,并写入请求
  • 客户端读取“new-section”,并向最终用户报告“预期的‘确认’,收到‘new-section’”

对于调试所涉及的Git实现的人来说,错误消息是令人困惑的,因为“new-section”不是响应最新的请求,而是响应第一个请求。
一个解决方案是总是在DELIM之后继续阅读,但在这种情况下,我们可以做得更好。
我们从协议中得知:

  • “ready”表示至少packfile部分即将到来(因此,DELIM)并且:
  • no“ready”意味着没有后续的部分(因此,FLUSH)。

因此,教process_acks()执行此操作。
Git 2.21将为fetch pack带来V2协议的实际官方支持:
参见commit e20b419(2018年12月18日),作者Jeff King ( peff )
(由Junio C Hamano -- gitster --合并至commit d3b0178,2019年1月29日)

fetch-pack:支持协议版本2

当最初在8f6982b(“protocol:引入enum protocol_version value protocol_v2”,2018-03-14,Git v2。18)。见:

git log -p -G'support for protocol v2 not implemented yet' --full-diff --reverse v2.17.0..v2.20.0

许多脚手架“死”占位符被删除,但我们还没有得到周围取包。
这里对“fetch refs from cmdline”的测试非常少。当在WIP GIT_TEST_PROTOCOL_VERSION=2 mode下运行整个测试套件时,有更好的覆盖率,理想情况下,我们应该有更好的覆盖率,而不需要调用特殊的测试模式。
Git 2.22(Q2 2019)添加了:“git clone”在通过协议版本2进行交谈时学习了新的--server-option选项。
参见commit 6e98305commit 35eb824(2019年4月12日)by Jonathan Tan ( jhowtan )
(由Junio C Hamano -- gitster --合并至commit 6d3df8e,2019年5月8日)

clone:使用协议v2时发送服务器选项

Commit 5e3548e(“fetch:使用协议v2时发送服务器选项”,2018-04-24,Git v2.18.0-rc 0)教导了“fetch“在使用协议v2时发送服务器选项的能力,但没有教导“clone“。
此能力由“-o”或“--server-option”触发。
示教“clone”相同的能力,但因为“clone”已经有“-o”用于其他参数,所以仅示教“克隆”以接收“--server-option”。
在文档中解释,对于clone和fetch,服务器选项的服务器处理是特定于服务器的。
这类似于receive-pack对推送选项的处理--目前,它们只是被发送到钩子上,以便按照它们认为合适的方式进行解释。
注意:Git 2.12在commit ed10cb9中引入了一个git serve命令,作者是布兰登威廉姆斯

servegit-serve简介

介绍git-serve,协议版本2的基础服务器。

协议版本2旨在取代Git当前的Wire协议。
其目的是,它将是一个更简单,更少浪费的协议,可以随着时间的推移而发展。
协议版本2通过消除初始ref通告而对版本1进行了改进。
在其位置,服务器将在能力通告中导出其支持的能力和命令的列表。
客户端然后可以通过提供多个能力和命令特定参数来请求执行特定命令。
在命令完成时,客户端可以请求执行另一命令,或者可以通过发送刷新分组来终止连接。
但是... Git 2.22确实修正了这一点,通过Johannes Schindelin的commit b7ce24d

git serve转换为测试助手

git serve内置于ed10cb9serve:介绍git-serve,2018-03-15,Git v2。18.0-rc 0)作为后端服务于Git协议v2,可能最初打算由git upload-pack产生。
然而,在协议v2补丁使其成为核心Git的版本中,git upload-pack直接调用serve()函数,而不是派生git serve;git serve作为内置命令存在的唯一原因是提供了一种测试协议v2功能的方法。
这意味着它甚至不必是一个内置的,安装在面向最终用户的Git安装,但它可以是一个测试助手。
让我们这样做。
Git 2.23(2019年第二季度)将使update-server-info更高效,因为它学会了不要用相同的内容重写文件。
参见commit f4f476b(2019年5月13日),作者Eric Wong ( ele828 )
(由Junio C Hamano -- gitster --合并于commit 813a3a2,2019年6月13日)

update-server-info:避免不必要的覆盖

如果现有的info/refsobjects/info/packs文件与文件系统上的现有内容匹配,则不要更改它们。
这是为了保留mtime,并使愚蠢的HTTP轮询器更容易依赖If-Modified-Since头。
结合stdio和内核缓冲;内核应该能够避免块层写入并减少小文件的损耗。
因此,不再需要--force选项。
因此,停止编写文档,但为了兼容性(以及调试,如果必要的话)而保留它。
还有Git 2.221还将修复服务器端对“git fetch”的支持,该支持用于在使用命名空间功能时显示HEAD符号引用的错误值。
请参阅commit 533e088(2019年5月23日)通过Jeff King ( peff )
(由Junio C Hamano -- gitster --合并于commit 5ca0db3,2019年7月25日)

upload-pack:从symref数据中删除名称空间

由于7171d8cupload-pack:send symbolic ref information as capability,2013-09-17,Git v1.8.4.3),我们已经向克隆和获取客户端发送了关于HEAD指向哪个分支的特殊信息,这样他们就不必根据匹配的提交ID来猜测。
然而,此功能从未与GIT_NAMESPACE功能正常工作。因为upload-pack使用head_ref_namespaced(find_symref),所以我们会查找并报告refs/namespaces/foo/HEAD,而不是repo的实际HEAD
这是有意义的,因为顶级HEAD所指向的分支可能根本不会被通告。
但我们做错了两件事:
1.我们报告全名refs/namespaces/foo/HEAD,而不仅仅是HEAD。
这意味着没有客户端会费心对symref做任何事情,因为我们不会以其他方式宣传它。
1.我们使用symref的全名(e.例如,refs/namespaces/foo/refs/heads/master)。这对客户端同样没用,他只在广告中看到“refs/heads/master”。

我们应该剥离名称空间前缀的两个地方(本补丁修复)

很可能没有人注意到,因为我们倾向于做正确的事情。
Bug(1)意味着我们没有提到HEAD(只是refs/namespace/foo/HEAD)。所以客户端的一半代码,来自a45b5f0connect:在get_remote_head(),2013-09-17,Git v1中使用引用的symref信息注解引用。8.4.3),没有注解HEAD,我们使用guess_remote_head()中的回退,通过对象id匹配refs。
这通常是对的。它只在模棱两可的情况下福尔斯,比如包含的测试中列出的情况。
这也意味着当我们修复bug(2)时,我们不必担心破坏任何将预先剥离的名称放入其名称空间symrefs的人。
由于bug(1),没有人会使用我们一开始宣传的symref(更不用说那些symref对于任何非命名空间的访问来说都是坏的)。
请注意,我们在这里对v0和v2协议进行了单独的修复。
在v2中,symref通告被移动为ls-refs命令的一部分。
这实际上是正确的,因为symref注解搭载了现有的ref广告,该广告被正确地剥离。
但它仍然需要修复部分(2)。
Git 2.251(2020年2月),减少了在无状态RPC机制上运行“ls-remote”时不必要的往返。
参见讨论:
一位同事(Jon Simons)今天指出了git ls-remote在协议v2中的一个有趣行为:它发出第二个POST请求并只发送一个刷新分组。

这可以用以下内容来证明:

GIT_CURL_VERBOSE=1 git -c protocol.version=2 ls-remote origin

第二个请求的Content-Length报头将正好是4个字节。
参见commit 4d8cab9(2020年1月8日)by Jeff King ( peff )
(由Junio C Hamano -- gitster --合并至commit 45f47ff,2020年1月22日)

transport:当断开stateless-rpc帮助程序时不刷新

签字人:杰夫·金
ba227857d2(“Reduce the number of connects when fetching”,2008-02-04,Git v1.5.5-rc 0--merge),当我们断开git传输时,我们发送最后一个flush包。
这清楚地告诉另一端我们已经完成了,并避免另一端抱怨“远程端意外挂起”(尽管我们只看到沿着服务器stderr传递的传输,如ssh或local-host)。
但是,当我们通过传输帮助程序启动v2无状态连接会话时,发送这个刷新包就没有意义了。我们执行的每个操作都是自包含的,另一端可以在操作之间挂起。
但更糟糕的是,通过发送flush数据包,我们可能会导致helper发出一个全新的请求_just_来发送flush数据包。因此,我们可以引发一个额外的网络请求,只是说“顺便说一下,我们没有更多的发送”。
让我们放弃这个额外的刷新包。正如测试所示,这将通过http的v2 ls-remote所需的POST数量从2减少到1。
在Git 2.26(2020年第一季度)中,test-lint机器知道检查“VAR=瓦尔shell_function"构造,但没有检查”VAR= shell_function",,这已经得到纠正。
参见commit d6509dacommit a7fbf12commit c7973f2(2019年12月26日)by Jonathan Nieder ( artagnon )
(由Junio C Hamano -- gitster --合并至commit c7372c9,2020年1月30日)

fetch test:“跳过”标记测试仅为v0

签字人:乔纳森·尼德
633a53179e起(fetch test:避免在shell函数中使用“VAR= cmd”,2019-12-26),t5552。5(不发送“have”与服务器确认的提交的祖先)在使用GIT_TEST_PROTOCOL_VERSION=2运行时失败。
原因:
在协商中发送的“have“的进程取决于我们使用的是基于无状态RPC的传输还是有状态双向传输(例如参见44d8dc54e7,“Fix potential local deadlock during fetch-pack”,2011-03-29,Git v1.7.5-rc0)。
在协议v2中,所有传输都是无状态传输,而在协议v0中,本地访问和SSH等传输是有状态的。
在有状态传输中,要发送的“have”的数量每轮乘以2,直到达到PIPESAFE_FLUSH(即32),然后每轮增加PIPESAFE_FLUSH
在无状态传输中,计数每轮乘以2,直到达到LARGE_FLUSH(即16384),然后乘以1。每轮1次之后。
此外,在有状态传输中,正如fetch-pack.c解释的那样:
我们保持一个窗口在另一边的“前面”,只在下一个窗口上等待ACK
这会影响t5552.5,因为它会从第二个窗口中出现的协商器中查找“have”。
对于协议版本2,第二个窗口永远不会到达,并且测试失败。
直到633a53179e(2019-12-26),同一文件中的先前测试包含

GIT_TEST_PROTOCOL_VERSION= trace_fetch client origin to_fetch

在许多常见的 shell (e。例如,bash作为“sh”运行时),将GIT_TEST_PROTOCOL_VERSION设置为空字符串的持续时间超过了trace_fetch调用的预期持续时间。
这会导致它覆盖在测试脚本的剩余部分中传递到测试中的GIT_TEST_PROTOCOL_VERSION设置,因此t5552.5永远不会在这些shell上使用协议v2运行,而不管环境中的GIT_TEST_PROTOCOL_VERSION设置如何。
633a53179e修复了这个问题,显示了失败的测试。

iyr7buue

iyr7buue4#

嗯,你正在进入管道的细节;即使你不得不向一群同事解释Git,我也会对这种细节的需求感到惊讶。..
无论如何,info/refs文件只存在于一个远程服务器上,该远程服务器将通过HTTP访问。你可能不会在你的本地存储库中找到它(也不需要它)。(这个场景中的远程可能是一个裸仓库,顺便说一句,所以info将位于仓库根目录,因为裸仓库没有工作树,而是将您习惯于在.git中看到的文件放在根目录。)
如果我们的远程是在像github,tfs等。..那么你就不需要担心这些了,因为服务器会很好地管理这些事情。我想如果你从一个普通的旧web服务器中将repo作为静态内容提供,那么这就很重要了,你必须设置钩子。
大多数用户永远不会使用或看到update-server-info命令;正如它的名字所暗示的那样,它是用于 * 服务器 * 端的repos- remotes -以弥补缺乏git感知的HTTP服务器。
post-receive钩子在接收到推送后被调用;所以在一个哑服务器场景中,你在远程设置这个钩子,这样当你推到它时,它会通过更新某些信息(比如refs文件)来响应。
您正在查看的GET命令是一个HTTP命令,在进行fetch时由git客户端在必要时运行。

vkc1a9a2

vkc1a9a25#

文件夹info/refs在哪里?我只找到一个/。git/info/exclude后面的克隆。..
没有这样的文件夹(它不是一个 * 目录 *),但是-.git/info/refs-应该是 * 文件 * 所在的位置,如果那里有一个文件的话。
如何使用update-server-info命令?它是git clone的一部分吗?
一般来说,你不应该使用它:它只适用于“哑巴”运输机。“智能”(双向对话)传输不需要它。
我完全迷失了。”..这就是为什么你需要启用它作为一个post-receive hook”,尽管我理解hook(我认为),并使用一个pre-commit hook来自动增加包版本。
如果出于某种原因,您希望启用哑传输,则需要在每次需要创建或更新文件时运行一些东西来创建或更新几个文件。info/refs文件需要在引用更改时更新,因此运行“something”的好地方是post-receive钩子。“something”是命令git update-server-info
请注意,如果您没有在服务器上运行仅推送的裸存储库,那么让post-receive脚本运行git update-server-info是不够的,因为可以通过其他方式添加提交和其他对象(例如手动git commit)。在这种情况下,你可以使用E。例如,在时钟驱动的基础上创建或更新哑传输信息的Cron作业。
我无法在git bash中获取GET info/refs命令。
如果该文件存在,您将通过HTTP获得它,e.例如,从浏览器或使用curl命令。

相关问题