我和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/repo
和git 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
命令。
如果这些问题很愚蠢,我很抱歉,但我只是不明白如何把这些文档中的片段放在一起。
5条答案
按热度按时间kuuvgm7e1#
git传输协议的另一个方面是它的数据包管理,包括请求“HAVE”时的ACK:
在Git 2之前。27(2020年第二季度),服务于“
git clone
”和“git fetch
”的v2协议的服务器端没有准备好在意想不到的地方看到delim数据包,这导致了崩溃。参见commit cacae43(2020年3月29日)和commit 4845b77,commit 88124ab(2020年3月27日)。
(由Junio C Hamano --
gitster
--合并至commit 5ee5788,2020年4月22日)upload-pack
:处理意外的delim包签字人:杰夫·金
当处理v2**
ls-refs
**或fetch
命令的参数列表时,我们像这样循环:读取和处理数据包直到我们看到刷新。这里隐藏的假设是,除了
PACKET_READ_FLUSH
之外的任何东西都将为我们提供有效的数据包数据以供读取。但事实并非如此PACKET_READ_DELIM
或PACKET_READ_EOF
会将>packet->line
保留为NULL
,我们将segfault尝试查看它。相反,我们应该遵循在客户端演示的更仔细的模型(例如:例如,在
process_capabilities_v2)
中:只要我们得到正常的数据包,就保持循环,然后确保我们由于真实的的刷新而打破了循环。修复segfault并正确诊断来自客户端的任何意外输入。在Git 2之前。27(2020年第二季度),upload-pack协议v2在找到共同祖先之前过早放弃,导致从项目的分支中进行浪费的获取。
这已被纠正,以匹配v0协议的行为。
参见commit 2f0a093,commit 4fa3f00,commit 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 dd4b732、commit 9da69a6、commit acaaca7、commit cd8402e、commit fd194dd、commit 8d5d2a3、commit 8e6adb6、commit eb05349、commit 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()
和传输机制以支持多个。保存文件。实施说明:
builtin/fetch-pack.c
通常不生成.keep
文件,因此不受此更改或未来更改的影响。但是,它有一个未记录的“
--lock-pack
”特性,由remote-curl.c
在实现“fetch
”远程助手命令时使用。为了与远程助手协议保持一致,只会写入一行“
lock
”;其余的将导致对stderr的警告。然而,在实践中,永远不会写入警告,因为
remote-curl.c
“fetch
“仅用于协议v0/v1(不会生成多个.keep
文件)。(协议v2使用“stateless-connect”命令,而不是“fetch
”命令。)参见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体。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。如果此时客户端已挂断,请正常退出。
inkz8wg92#
在Git 2.30(2021年第一季度)中,传输层被教导在获取/推送事务期间可以选择交换
trace2
子系统分配的会话ID。请参阅commit a2a066d、commit 8c48700、commit 8295946、commit 1e905bb、commit 23bf486、commit 6b5b6e4、commit 8073d75、commit 791e1ad、commit e97e1cf、commit 81bd549、commit 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>
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 2.31(2021年第一季度)中,commit-graph学会了使用正确的提交日期而不是生成编号来帮助拓扑修订遍历,以将自己与协议v1区分开来。
请参阅commit 5a3b130、commit 8d00d7c、commit 1fdc383、commit e8b6300、commit c1a0911、commit d7f9278、commit 72a2bfc、commit c0ef139、commit f90fca6、commit 2f9bbb6、commit 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历史记录测试溢出相关代码:
其中:
观察到的最大偏移量是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成员(用于提交者日期)。
因为这已经导致了问题(如859fdc0(
commit-graph
:defineGIT_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 bc50d6c、commit fde55b0、commit 9c2c0a8、commit 448a39e(2021年2月2日)和commit 90cb1c4、commit 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和相关请求信息的列表,每个由单个空格分隔。响应
git push
(man)请求的“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的人都会很快意识到这个点需要更新。
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 a4d78ce、commit 0f1dc53、commit 237ffed、commit 884e586、commit 8ff14ed、commit 49e85e9、commit f08a5d4、commit f1f4d8a、commit edc9caf、commit 176e85c、commit b1c2edf、commit 1aa8dde、commit 40fc51e、commit f7e2050、commit 685fbd3、commit 3145ea9、commit 5b872ff、commit 230d7dd、commit b4be741,commit 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命令,用于显式请求某些引用
在协议v2中,通信是面向命令的。
当第一次联系服务器时,将通告功能列表。这些能力中的一些将是客户端可以请求执行的命令。一旦命令完成,客户端就可以重新使用连接并请求执行其他命令。
info/refs
仍然是客户端要查询的服务器端点,如HTTP传输部分所述:当使用
http://
或https://
传输时,客户端发出一个“智能”info/refs
请求,如http-protocol.txt
中所述,并通过在Git-Protocol
报头中提供“version=2
”来请求使用v2。v2服务器会回复:
随后的请求将直接发送到服务
$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 5e3548e,commit ff47322,commit 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 ba95710、commit 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 ec06283、commit d093bc7、commit d30fe89、commit af1c90d、commit 21bcf6e(2018年6月14日)和commit af00855、commit 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 6a139cd,commit 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
”部分的内容来确定是否继续阅读,而不是根据是否阅读了DELIM
或FLUSH
。与协议兼容的服务器没有问题,但在与提供意外附加部分的服务器通信时,可能会导致令人困惑的错误消息。考虑一个在“
acknowledgments
“之后发送“new-section
“的服务器:DELIM
new-section
”,并向最终用户报告“预期的‘确认’,收到‘new-section
’”对于调试所涉及的Git实现的人来说,错误消息是令人困惑的,因为“
new-section
”不是响应最新的请求,而是响应第一个请求。一个解决方案是总是在
DELIM
之后继续阅读,但在这种情况下,我们可以做得更好。我们从协议中得知:
DELIM
)并且: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)。见:许多脚手架“死”占位符被删除,但我们还没有得到周围取包。
这里对“fetch refs from cmdline”的测试非常少。当在WIP
GIT_TEST_PROTOCOL_VERSION=2
mode下运行整个测试套件时,有更好的覆盖率,理想情况下,我们应该有更好的覆盖率,而不需要调用特殊的测试模式。Git 2.22(Q2 2019)添加了:“
git clone
”在通过协议版本2进行交谈时学习了新的--server-option
选项。参见commit 6e98305,commit 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命令,作者是布兰登威廉姆斯:
serve
:git-serve
简介介绍
git-serve
,协议版本2的基础服务器。协议版本2旨在取代Git当前的Wire协议。
其目的是,它将是一个更简单,更少浪费的协议,可以随着时间的推移而发展。
协议版本2通过消除初始ref通告而对版本1进行了改进。
在其位置,服务器将在能力通告中导出其支持的能力和命令的列表。
客户端然后可以通过提供多个能力和命令特定参数来请求执行特定命令。
在命令完成时,客户端可以请求执行另一命令,或者可以通过发送刷新分组来终止连接。
但是... Git 2.22确实修正了这一点,通过Johannes Schindelin的commit b7ce24d:
将
git serve
转换为测试助手git serve
内置于ed10cb9(serve
:介绍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/refs
和objects/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数据中删除名称空间由于7171d8c(
upload-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
)。所以客户端的一半代码,来自a45b5f0(connect
:在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请求并只发送一个刷新分组。这可以用以下内容来证明:
第二个请求的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 d6509da,commit a7fbf12,commit 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),同一文件中的先前测试包含
在许多常见的 shell (e。例如,bash作为“
sh
”运行时),将GIT_TEST_PROTOCOL_VERSION
设置为空字符串的持续时间超过了trace_fetch
调用的预期持续时间。这会导致它覆盖在测试脚本的剩余部分中传递到测试中的
GIT_TEST_PROTOCOL_VERSION
设置,因此t5552.5
永远不会在这些shell上使用协议v2运行,而不管环境中的GIT_TEST_PROTOCOL_VERSION
设置如何。633a53179e修复了这个问题,显示了失败的测试。
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客户端在必要时运行。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
命令。