在 Git 2.25(2020 年第一季度)中,稀疏签出工作树的管理获得了专用的"sparse-checkout" command。
首先,这是一个扩展示例,以fast clone using a --filter option 开头:
git clone --filter=blob:none --no-checkout https://github.com/git/git
cd git
git sparse-checkout init --cone
# that sets git config core.sparseCheckoutCone true
git read-tree -mu HEAD
使用锥形选项(详细/记录如下)意味着您的 .git\info\sparse-checkout 将包含以下开头的模式:
/*
!/*/
含义:只有顶级文件,没有子文件夹。
如果你不想要top file,你需要避免圆锥模式:
# Disablecone mode in .git/config.worktree
git config core.sparseCheckoutCone false
# remove .git\info\sparse-checkout
git sparse-checkout disable
# Add the expected pattern, to include just a subfolder without top files:
git sparse-checkout set /mySubFolder/
# populate working-tree with only the right files:
git read-tree -mu HEAD
详细说明:
(在“Bring your monorepo down to size with sparse-checkout”查看更多信息,来自
Derrick Stolee)
因此,不仅排除子文件夹确实有效,而且在稀疏结帐的“锥形”模式下(使用 Git 2.25)会更快工作。
见commit 761e3d2(2019 年 12 月 20 日)Ed Maste (emaste)。
见commit 190a65f(2019 年 12 月 13 日),commit cff4e91,commit 416adc8,commit f75a69f,commit fb10ca5,commit 99dfa6f,commit e091228,commit e9de487,commit eb42fec,@9876543336@,@987654 ,commit 96cc8ab,commit 879321e,commit 72918c1,commit 7bffca9,commit f6039a9,commit d89f09c,commit bab3c35,commit 94c0956(2019 年 11 月 21 日)Derrick Stolee (derrickstolee)。
请参阅 Jeff Hostetler (Jeff-Hostetler) 的 commit e6152e3(2019 年 11 月 21 日)。
(由 Junio C Hamano -- gitster -- 合并于 commit bd72a08,2019 年 12 月 25 日)
签字人:Derrick Stolee
随着索引中的模式数量和条目数量的增长,稀疏结帐功能可以具有二次性能。
如果有 1,000 个模式和 1,000,000 个条目,那么这个时间可能非常重要。
创建一个新的布尔配置选项 core.sparseCheckoutCone,以表明我们希望稀疏结帐文件包含一组更有限的模式。
这是与core.sparseCheckout 分开的配置设置,以避免通过引入三态选项来破坏旧客户端。
config man page 包括:
`core.sparseCheckoutCone`:
启用稀疏结帐功能的“锥形模式”。
当 sparse-checkout 文件包含一组有限的模式时,这种模式提供了显着的性能优势。
git sparse-checkout man page 详情:
锥形图案集
完整的模式集允许任意模式匹配和复杂的包含/排除规则。
这些可能导致更新索引时O(N*M) 模式匹配,其中N 是模式数,M 是索引中的路径数。为了解决这个性能问题,启用core.spareCheckoutCone 时允许使用更受限制的模式集。
圆锥模式集中接受的模式是:
-
递归:目录内的所有路径都包括在内。
-
父级: 直接位于目录中的所有文件都包括在内。
除了以上两种模式,我们还期望根目录下的所有文件都包含在内。如果添加递归模式,则所有前导目录都将添加为父模式。
默认情况下,在运行git sparse-checkout init 时,会添加根目录作为父模式。
此时,sparse-checkout 文件包含以下模式:
/*
!/*/
这表示“包含根目录中的所有内容,但根目录以下两级不包含任何内容。”
如果我们随后将文件夹 A/B/C 添加为递归模式,则文件夹 A 和 A/B 将添加为父模式。
生成的 sparse-checkout 文件现在是
/*
!/*/
/A/
!/A/*/
/A/B/
!/A/B/*/
/A/B/C/
在这里,顺序很重要,因此负面模式会被正面模式覆盖
在文件中显示在较低位置的模式。
如果是core.sparseCheckoutCone=true,则 Git 将解析预期这些类型的模式的 sparse-checkout 文件。
如果模式不匹配,Git 会发出警告。
如果模式确实匹配预期的格式,那么 Git 将使用更快的哈希 -
基于算法来计算 sparse-checkout 中的包含。
所以:
协助人:Eric Wong
协助人:Johannes Schindelin
签字人:Derrick Stolee
为了使锥形模式集易于使用,请更新“git sparse-checkout (init|set)”的行为。
将“--cone”标志添加到“git sparse-checkout init”以设置配置选项“core.sparseCheckoutCone=true”。
在锥形模式下运行“git sparse-checkout set”时,用户只需提供递归文件夹匹配列表。 Git 会自动为前导目录添加必要的父匹配项。
请注意,--cone 选项仅记录在 Git 2.26(2020 年第一季度)
(由 Junio C Hamano -- gitster -- 合并到 commit ea46d90,2020 年 2 月 5 日)
doc:sparse-checkout:提到--cone选项
签字人:Matheus Tavares
签字人:Derrick Stolee
在af09ce2 ("sparse-checkout: init and set in cone mode", 2019-11-21, Git v2.25.0-rc0 -- merge) 中,添加了 '--cone' 选项'git sparse-checkoutinit'。
记录在git sparse-checkout:
这包括:
当提供--cone 时,core.sparseCheckoutCone 设置也被设置,允许在有限的一组模式下获得更好的性能。
(上面显示的“模式集”,在此答案的“CONE PATTERN SET”部分)
这种新的“锥形”模式会快多少?
协助人:Eric Wong
协助人:Johannes Schindelin
签字人:Derrick Stolee
sparse-checkout 中“锥形模式”选项允许的父模式和递归模式具有足够的限制性,我们可以避免使用正则表达式解析。一切都基于前缀匹配,因此我们可以使用哈希集来存储稀疏签出文件中的前缀。在检查路径时,我们可以从路径中剥离路径条目并检查哈希集是否完全匹配。
作为测试,我为 Linux 存储库创建了一个锥形模式稀疏签出文件,该文件实际上包含每个文件。这是通过获取 Linux 存储库中的每个文件夹并在此处创建模式对来构建的:
/$folder/
!/$folder/*/
这导致了 8,296 个模式的稀疏签出文件。
在这个文件上运行 'git read-tree -mu HEAD' 有以下性能:
core.sparseCheckout=false: 0.21 s (0.00 s)
core.sparseCheckout=true : 3.75 s (3.50 s)
core.sparseCheckoutCone=true : 0.23 s (0.01 s)
根据trace2 性能跟踪,上面括号中的时间对应于第一次clear_ce_flags() 调用所花费的时间。
虽然此示例是人为设计的,但它演示了这些模式如何减慢稀疏结帐功能。
还有:
签字人:Derrick Stolee
当用户在锥形模式下使用稀疏检出功能时,他们使用“git sparse-checkout set <dir1> <dir2> ...”或使用“--stdin”添加模式,以通过标准输入逐行提供目录。
这种行为自然看起来很像用户输入“git add <dir1> <dir2> ...”的方式
如果启用了core.ignoreCase,则“git add”将使用不区分大小写的匹配来匹配输入。
对 sparse-checkout 功能执行相同操作。
在unpack_trees() 期间更新跳过工作树位时执行不区分大小写的检查。这是通过将哈希算法和哈希图比较方法更改为可选地使用不区分大小写的方法来完成的。
启用此选项后,散列算法的性能成本会很小。
为了找出最坏的情况,以下是在具有深层目录结构的 repo 上运行的:
git ls-tree -d -r --name-only HEAD |
git sparse-checkout set --stdin
“设置”命令在 core.ignoreCase 禁用或启用的情况下进行计时。
对于历史悠久的回购,数字是
core.ignoreCase=false: 62s
core.ignoreCase=true: 74s (+19.3%)
为了重现性,Linux 内核存储库上的等效测试具有以下数字:
core.ignoreCase=false: 3.1s
core.ignoreCase=true: 3.6s (+16%)
现在,这并不是一个完全公平的比较,因为大多数用户将使用更浅的目录来定义他们的稀疏锥体,以及 eb42feca97 的性能改进(“unpack-trees: hash less in cone mode”2019-11- 21、Git 2.25-rc0) 可以去除大部分的哈希开销。要进行更实际的测试,请从 ls-tree 命令中删除“-r”以仅存储第一级目录。
在这种情况下,Linux 内核存储库在每种情况下都需要 0.2-0.25 秒,而深层存储库在每种情况下都需要 1 秒,正负 0.05 秒。
因此,我们可以证明此更改的成本,但这对任何合理的稀疏结帐锥都不太重要。
在 Git 2.25(2020 年第一季度)中,“git sparse-checkout list”子命令学会了在“锥形”模式生效时以更简洁的形式给出其输出。
参见Derrick Stolee (derrickstolee) 的commit 4fd683b、commit de11951(2019 年 12 月 30 日)。
(由 Junio C Hamano -- gitster -- 合并到 commit c20d4fd,2020 年 1 月 6 日)
签字人:Derrick Stolee
启用core.sparseCheckoutCone 时,“git sparse-checkout set”命令将目录列表作为输入,然后创建稀疏签出模式的有序列表,以便递归包含这些目录以及父目录中的所有同级条目也包括在内。
列出模式不如目录本身那么用户友好。
在锥形模式下,只要模式与预期的锥形模式类型匹配,将“git sparse-checkout list”的输出更改为仅显示创建模式的目录。
通过此更改,以下管道命令不会更改工作目录:
git sparse-checkout list | git sparse-checkout set --stdin
这唯一不起作用的情况是core.sparseCheckoutCone 是true,但稀疏检出文件包含与锥形模式的预期模式类型不匹配的模式。
最近在此版本中添加的代码以稀疏圆锥模式移动到索引中同一目录中的条目之外的条目,没有计算错误跳过的条目数,已通过 Git 更正2.25.1(2020 年 2 月)。
见Junio C Hamano (gitster)commit 7210ca4(2020 年 1 月 27 日)。
请参阅 Derrick Stolee via GitGitGadget (``) 的 commit 4c6c797(2020 年 1 月 10 日)。
(由 Junio C Hamano -- gitster -- 合并于 commit 043426c,2020 年 1 月 30 日)
报告人:Johannes Schindelin
签字人:Derrick Stolee
clear_ce_flags_dir() 方法处理公共目录中的缓存条目。返回的int 是该目录处理的缓存条目数。
在锥形模式下使用稀疏签出功能时,我们可以跳过完全包含或完全排除目录中的条目的模式匹配。
eb42feca ("unpack-trees: hashless in cone mode", 2019-11-21, Git v2.25.0-rc0 -- merge 列在batch #0) 引入了这个性能特性。旧机制依赖于调用clear_ce_flags_1() 返回的计数,但新机制通过从“cache”中减去“cache_end”来计算行数,以找到范围的大小。
然而,等式是错误的,因为它除以sizeof(struct cache_entry *)。这不是指针运算的工作原理!
为 2.25.0 版本准备的适用于 Windows 的 Git 的覆盖构建发现此问题并带有警告:
Pointer differences, such as `cache_end` - cache, are automatically
scaled down by the size (8 bytes) of the pointed-to type (struct `cache_entry` *).
Most likely, the division by sizeof(struct `cache_entry` *) is extraneous
and should be eliminated.
这个警告是正确的。
这给我们留下了一个问题:“这甚至是如何工作的?”
这种不正确的指针算法出现的问题是一个性能错误,而且是一个非常轻微的错误。
由于clear_ce_flags_dir() 返回的条目计数减少了 8 倍,clear_ce_flags_1() 中的循环将重新处理这些目录中的条目。
通过将全局计数器插入unpack-tree.c 并使用trace2_data_intmax() 跟踪它们(在私人更改中,用于测试),我能够看到clear_ce_flags_1() 内部的循环处理了多少次以及处理了多少次clear_ce_flags_dir() 被调用。
随着当前的变化,这些中的每一个都至少减少了 8 倍。
重复多级目录时,会发生大于 8 的因数。
具体来说,在 Linux 内核 repo 中,命令
git sparse-checkout set LICENSES
将工作目录限制为仅位于根目录和 LICENSES 目录中的文件。
以下是测量的计数:
clear_ce_flags_1 循环块:
Before: 11,520
After: 1,621
clear_ce_flags_dir 来电:
Before: 7,048
After: 606
虽然这些数字非常惊人,但在每种情况下花费在clear_ce_flags_1() 上的时间都不到一毫秒,因此无法以端到端时间来衡量改进。
在 Git 2.26(2020 年第一季度)中,稀疏结账功能中的一些粗糙边缘,尤其是在锥形模式周围,已被清理干净。
见commit f998a3f、commit d2e65f4、commit e53ffe2、commit e55682e、commit bd64de4、commit d585f0e、commit 4f52c2c、commit 9abc60f(2020 年 1 月 31 日)和commit 9e6d3e6、@987@、@987 commit 47dbf10、commit 3c75406、commit d622c34、commit 522e641(2020 年 1 月 24 日)Derrick Stolee (derrickstolee)。
请参阅 Jeff King (peff) 的 commit 7aa9ef2(2020 年 1 月 24 日)。
(由 Junio C Hamano -- gitster -- 合并于 commit 433b8aa,2020 年 2 月 14 日)
报告人:Finn Bryant
签字人:Derrick Stolee
稀疏签出功能中特殊的“锥形模式”的目的是始终匹配相同的稀疏签出文件匹配的相同模式,就像禁用锥形模式时一样。
当在锥形模式下将文件路径赋予“git sparse-checkout set”时,锥形模式会不正确地将文件匹配为递归路径。
在设置 skip-worktree 位时,文件不期望 MATCHED_RECURSIVE 响应,因此这些被排除在匹配的锥体之外。
通过检查 MATCHED_RECURSIVE 和 MATCHED 来修复此错误,并添加一个防止回归的测试。
The documentation 现在包括:
启用core.sparseCheckoutCone 时,输入列表被视为
目录列表,而不是稀疏结帐模式。
该命令将模式写入稀疏签出文件,以包括这些目录(递归)中包含的所有文件以及祖先目录的兄弟文件。
输入格式与git ls-tree --name-only 的输出相匹配。这包括将以双引号 (") 开头的路径名解释为 C 风格的带引号的字符串。
在 Git 2.26(2020 年第一季度)中,“git sparse-checkout”学习了新的“add”子命令。
参见commit 6c11c6a(2020 年 2 月 20 日)和 commit ef07659、commit 2631dc8、commit 4bf0c06、commit 6fb705a(2020 年 2 月 11 日)Derrick Stolee (derrickstolee)。
(由 @987654417 合并@in commit f4d7dfc,2020 年 3 月 5 日)
签字人:Derrick Stolee
使用稀疏结帐功能时,用户可能希望逐步扩展其稀疏结帐模式集。
允许使用新的“添加”子命令添加模式。
这与 'set' 子命令没有太大区别,因为我们仍然希望允许 '--stdin' 选项,并在锥体模式和其他模式下将输入解释为目录。
在圆锥模式下,我们正在扩大圆锥。
当A/B 已经是锥体中的目录时,这实际上可能会减少添加目录A 时的模式集。测试不同的案例:兄弟姐妹、父母、祖先。
当不处于锥形模式时,我们只能假设模式应该附加到稀疏检出文件中。
还有:
签字人:Derrick Stolee
在使用 Windows 时,用户可以将“git sparse-checkoutset A\B\C' to add the Unix-style path A/B/C”运行到他们的稀疏结帐模式。
在我们将字符串“A/B/C”添加到递归哈希集之前,规范化输入路径会将反斜杠转换为斜杠。
sparse-checkout 模式一直被禁止排除所有路径,留下一棵空的工作树。
在 Git 2.27(2020 年第二季度)中,这一限制已被解除。
参见Derrick Stolee (derrickstolee) 的commit ace224a(2020 年 5 月 4 日)。
(由 Junio C Hamano -- gitster -- 合并于 commit e9acbd6,2020 年 5 月 8 日)
报告人:Lars Schneider
签字人:Derrick Stolee
删除更新稀疏签出时的错误条件留下一个空的工作目录。
在9e1afb167 中添加了此行为(“稀疏结帐:禁止空工作树”,2009-08-20,Git v1.7.0-rc0 -- merge)。
评论添加在a7bc906f2(“添加解释为什么我们不允许稀疏结帐到空工作树”,2011-09-22,Git v1.7.8-rc0 -- merge)响应84563a624 中的“可疑”评论(“[unpack-trees.c](https://github.com/git/git/blob/ace224ac5fb120e9cae894e31713ab60e91f141f/unpack-trees.c):化妆品修复”,2010- 12-22,Git v1.7.5-rc0 -- merge)。
使用最近的“cone mode”和“git sparse-checkout init [--cone]”命令,设置合理的sparse-checkout模式集是很常见的
/*
!/*/
仅匹配根目录下的文件。如果存储库没有这样的文件,那么他们的“git sparse-checkout init”命令将会失败。
既然我们希望这是一种常见的模式,我们不应该让命令在空的工作目录上失败。
如果结果令人困惑,则用户可以使用“git sparse-checkout disable”或“git sparse-checkout set”进行恢复。这在使用锥形模式时尤其简单。