【问题标题】:Shallow AND Sparse GIT Repository Clone浅层和稀疏 GIT 存储库克隆
【发布时间】:2019-03-02 18:02:42
【问题描述】:

我有一个超过 1 GB 的浅克隆 git 存储库。我对所需的文件/目录使用稀疏签出。

如何将存储库克隆减少为仅稀疏的签出文件/目录?

最初,我能够通过在克隆时禁用签出来将克隆的存储库限制为仅稀疏签出。然后在进行初始结帐之前设置稀疏结帐。这将存储库限制为仅约 200 MB。更易于管理。但是,在将来的某个时间更新远程分支信息会导致其余文件和目录包含在存储库克隆中。将 repo 克隆大小发送回超过 1 GB,我不知道如何只处理稀疏的签出文件和目录。

简而言之,我想要的是一个浅层 AND 稀疏存储库克隆。不仅仅是浅层回购克隆的稀疏结帐。完整的 repo 会浪费空间,并且会影响某些任务的性能。

希望有人可以分享解决方案。谢谢。

【问题讨论】:

  • 这是用 Git 2.25(2020 年第一季度)完全实现的:参见my answer below 末尾的示例。

标签: git repository clone sparse-checkout


【解决方案1】:

Shallow and sparse 表示“部分”或“狭窄”。

部分克隆(或“狭义克隆”)在理论上是可行的,并于 2017 年 12 月首次使用 Git 2.16 实现,名称为 seen here
但是:

这在 Git 2.20(2018 年第四季度)中得到了进一步优化,因为在将从原始存储库中延迟补充的部分克隆中,我们通常希望避免“这个对象是否存在(本地)?”在我们故意省略的对象上 当我们创建(部分/稀疏)克隆时。
然而,缓存树代码路径(用于从索引中写入树对象)坚持该对象存在,即使对于部分签出区域之外的路径也是如此。
代码已更新以避免此类检查。

参见Jonathan Tan (jhowtan)commit 2f215ff(2018 年 10 月 9 日)。
(由 Junio C Hamano -- gitster -- 合并到 commit a08b1d6,2018 年 10 月 19 日)

cache-tree: 在部分克隆中跳过一些 blob 检查

在部分克隆中,每当发生稀疏检出时,都会验证索引中是否存在所有 blob,无论它们是否被 .git/info/sparse-checkout 规范包含或排除。
这会显着降低性能,因为任何时候都会发生延迟提取 检查是否存在丢失的 blob。


在 Git 2.24(2019 年第四季度)中,cache-tree 代码在尝试查看其计算的树对象是否已经存在时不那么激进 存储库。

参见 Jonathan Tan (jhowtan)commit f981ec1(2019 年 9 月 3 日)。
(由 Junio C Hamano -- gitster -- 合并到 commit ae203ba,2019 年 10 月 7 日)

cache-tree: 不要懒惰获取试探树

cache-tree 数据结构用于加速 HEAD 和索引之间的比较,当索引被樱桃挑选更新时(例如),一个树对象会表示目录中索引中的路径是在内核中构建的,以查看对象存储中是否已经存在这样的树对象。

当引入惰性获取机制时,我们转换了“树存在吗?”错误地检查“如果没有,如果我们懒惰地克隆,看看远程是否有它”调用。
由于这个检查的重点是通过机会性地记录一个已经存在的树对象来修复缓存树,我们甚至不应该尝试从远程获取一个。

传递OBJECT_INFO_SKIP_FETCH_OBJECT 标志以确保我们只检查本地对象存储中的存在而不触发延迟获取机制。


在 Git 2.25(2020 年第一季度)中,“git fetch”代码路径有一个很大的“当我询问是否存在某些东西时不要懒惰地获取丢失的对象”开关。

已通过标记“这件事存在吗?”得到纠正。带有“如果不是请不要懒惰地获取它”标志的调用。

参见Jonathan Tan (jhowtan)@commit 603960bcommit e362fad(2019 年 11 月 13 日)和commit 6462d5e(2019 年 11 月 5 日)。
(由 Junio C Hamano -- gitster -- 合并到 commit fce9e83,12 月 1 日2019)

clone:删除fetch_if_missing=0

签字人:Jonathan Tan

Commit 6462d5eb9a ("fetch: remove fetch_if_missing=0", 2019-11-08) 努力从获取机制中删除对 fetch_if_missing=0 的需求,因此尝试从克隆中删除 fetch_if_missing=0 也是合理的。但是这样做会暴露一个错误——当服务器没有发送一个直接指向的对象时,这应该是一个错误,而不是延迟获取的触发器。(获取机制中的这种情况被使用“ git clone”,而不是“git fetch”,这就是上述提交没有发现错误的原因。)

可以通过在连接检查期间抑制延迟获取来修复该错误。修复此错误,并从克隆中删除 fetch_if_missing

还有:

promisor-remote:删除fetch_if_missing=0

签字人:Jonathan Tan

Commit 6462d5eb9a ("fetch: remove fetch_if_missing=0", 2019-11-08) 努力从获取机制中删除对 fetch_if_missing=0 的需求,因此尝试从惰性获取中删除 fetch_if_missing=0 是合理的promisor-remote 中的机制也是如此。

但是这样做会暴露一个错误——当服务器没有发送标签对象指向的对象时,就会发生无限循环:Git 尝试获取丢失的对象,这会导致所有 refs 的延迟(用于协商),这会导致对该丢失对象的延迟获取,依此类推。
这个错误是因为在延迟获取期间不必要地使用了 fetch negotiator - 它在初始化后没有使用,但它仍然被初始化(这会导致所有 refs 的取消引用)。

因此,当在获取期间不使用协商器时,请避免对其进行初始化。然后,从promisor-remote 中删除fetch_if_missing


通过 Derrick Stolee

的“Bring your monorepo down to size with sparse-checkout”查看更多信息

将 sparse-checkout 与 partial clone feature 配对可以进一步加快这些工作流程。
这种组合加快了数据传输过程,因为您不需要每个可访问的 Git 对象,而是可以只下载您需要的那些以填充工作目录的锥体

$ git clone --filter=blob:none --no-checkout https://github.com/derrickstolee/sparse-checkout-example
Cloning into 'sparse-checkout-example'...
Receiving objects: 100% (373/373), 75.98 KiB | 2.71 MiB/s, done.
Resolving deltas: 100% (23/23), done.
 
$ cd sparse-checkout-example/
 
$ git sparse-checkout init --cone
Receiving objects: 100% (3/3), 1.41 KiB | 1.41 MiB/s, done.
 
$ git sparse-checkout set client/android
Receiving objects: 100% (26/26), 985.91 KiB | 5.76 MiB/s, done.

在 Git 2.25.1(2020 年 2 月)之前,has_object_file() 表示“no”给定一个通过pretend_object_file() 注册到系统的对象,使其与read_object_file() 不一致,导致延迟获取尝试获取来自 Promisor 遥控器的空树。

See discussion.

我试图重现这个

empty_tree=$(git mktree </dev/null)
git init --bare x
git clone --filter=blob:none file://$(pwd)/x y
cd y
echo hi >README
git add README
git commit -m 'nonempty tree'
GIT_TRACE=1 git diff-tree "$empty_tree" HEAD

事实上,即使来自不包含它的存储库,Git 似乎也为空树提供服务。

参见Jonathan Tan (jhowtan)commit 9c8a294(2020 年 1 月 2 日)。
(由 Junio C Hamano -- gitster -- 合并到 commit e26bd14,2020 年 1 月 22 日)

sha1-file:删除OBJECT_INFO_SKIP_CACHED

签字人:Jonathan Tan

在部分克隆中,如果用户将空树的哈希值(“git mktree &lt;/dev/null” - 对于 SHA-1,这是 4b825d...)提供给需要解析该对象的命令,例如:

git diff-tree 4b825d <a non-empty tree>

然后 Git 将不必要地懒惰地获取空树,因为对该对象的解析会调用 repo_has_object_file(),这不会对空树进行特殊处理。

相反,教repo_has_object_file() 咨询find_cached_object()(处理空树),从而使其与object-store-accessing 的其余功能保持一致。
代价是 repo_has_object_file() 现在在每次调用时都需要 oideq,但这与文件系统查找或无论如何都需要的包索引搜索相比是微不足道的。 (如果 find_cached_object() 由于之前对 pretend_object_file() 的调用而需要做更多事情,那么在我们是否呈现缓存对象方面更有理由保持一致。)

作为历史记录,现在称为 repo_read_object_file() 的函数在 346245a1bb 中被教授空树(“硬编码空树对象”,2008-02-13,Git v1.5.5-rc0 -- merge),现在称为 oid_object_info() 的函数在 c4d9986f5f 中被教导了空树(“sha1_object_info: 检查 cached_object 也存储”,2011-02-07,Git v1.7.4.1) .

repo_has_object_file() 从未更新,可能是由于疏忽。
标志OBJECT_INFO_SKIP_CACHED, 稍后在dfdd4afcf9(“sha1_file:教sha1_object_info_extended更多标志”,2017-06-26,Git v2.14.0-rc0)中引入并用于e83e71c5e1(“sha1_file: refactor has_sha1_file_with_flags", 2017-06-26, Git v2.14.0-rc0) 被引入以保留空树处理中的这种差异,但现在可以将其删除。


Git 2.25.1 还会警告程序员 pretend_object_file() 允许代码暂时使用内核对象。

commit 60440d7(2020 年 1 月 4 日)Jonathan Nieder (artagnon)
(由 Junio C Hamano -- gitster -- 合并到 commit b486d2e,2020 年 2 月 12 日)

sha1-file:记录如何使用pretend_object_file

灵感来源:Junio C Hamano
签字人:Jonathan Nieder

像内存中的替代品一样,pretend_object_file 包含一个 陷阱 供粗心的人使用:粗心的调用者可以使用它来创建对磁盘上对象中不存在的对象的引用商店

添加注释,说明如何使用该功能而不冒此类问题的风险。

当前唯一的调用者是 blame,它使用 pretend_object_file 创建一个代表工作树状态的内存提交。在讨论如何在“git merge”之类的操作中安全地使用此功能时注意到,与责备不同,它不是只读的。

所以the comment is now:

/*
 * Add an object file to the in-memory object store, without writing it
 * to disk.
 *
 * Callers are responsible for calling write_object_file to record the
 * object in persistent storage before writing any other new objects
 * that reference it.
 */
int pretend_object_file(void *, unsigned long, enum object_type,
            struct object_id *oid);

Git 2.25.1(2020 年 2 月)包含一个 Futureproofing,以确保测试不依赖于当前的实现细节。

参见Jonathan Tan (jhowtan)commit b54128b(2020 年 1 月 13 日)。
(由 Junio C Hamano -- gitster -- 合并到 commit 3f7553a,2020 年 2 月 12 日)

t5616: 对 delta 基础变化具有鲁棒性

签字人:Jonathan Tan

Commit 6462d5eb9a ("fetch: remove fetch_if_missing=0", 2019-11-08) 包含一个依赖于必须延迟获取 blob 的 delta base 的测试,但假设要获取的树(作为test) 作为非增量对象发送。
这个假设在未来可能不成立;例如,对象哈希长度的变化可能会导致树被作为增量发送。

依赖于必须懒惰地获取树的 delta 基数,并且不对 blob 是以 delta 还是非 delta 形式发送的假设,从而使测试更加健壮。


Git 2.25.2(2020 年 3 月)修复了最近更改为将协议 v2 设为默认协议所揭示的错误。

参见Derrick Stolee (derrickstolee)commit 3e96c66commit d0badf8(2020 年 2 月 21 日)。
(由 Junio C Hamano -- gitster -- 合并到 commit 444cff6,2020 年 3 月 2 日)

partial-clone: 查找对象时避免获取

签字人:Derrick Stolee

在测试部分克隆时,我注意到一些奇怪的行为。我正在测试一种运行“git init”的方法,然后手动配置远程部分克隆,然后运行“git fetch”。
令人惊讶的是,我看到 'git fetch' 进程开始向服务器请求多轮包文件下载!稍微调整一下情况,我发现我可能会导致遥控器挂断错误。

添加两个测试来证明这两个问题。

在第一个测试中,我们发现当使用 blob 过滤器从之前没有任何标签的存储库中提取时,'git fetch --tags origin' 命令失败,因为服务器发送“多个过滤器规格无法组合”。这只发生在使用协议 v2 时。

在第二个测试中,我们看到带有多个 ref 更新的“git fetch origin”请求会导致多个包文件下载。
这一定是由于 Git 试图在 refs 指向的对象中出错。让这件事特别讨厌的是,这是通过do_oid_object_info_extended()方法,所以谈判中没有“有”。
这导致远程从每个新的 ref 发送每个可达的提交和树,提供二次数据传输!如果我们恢复6462d5eb9a(获取:删除fetch_if_missing=0, 2019-11-05,Git v2.25.0-rc0),则此测试已修复,但该恢复会导致其他测试失败。
真正的修复需要更多的关注。

修复:

当使用部分克隆时,builtin/fetch.c 中的find_non_local_tags() 会检查每个远程标签以查看其对象是否也在本地存在。不期望对象在本地存在,但是如果对象不存在,此函数仍然会触发延迟获取。当请求提交时,这可能会非常昂贵,因为我们完全从不存在的对象的上下文中删除,因此在请求中不提供任何“拥有”。

6462d5eb9a (fetch: remove fetch_if_missing=0, 2019-11-05, Git v2.25.0-rc0, , Git v2.25.0-rc0) 删除了一个阻止这些获取的全局变量,以支持位标志。但是,某些对象存在检查未更新为使用此标志。

更新find_non_local_tags() 以使用OBJECT_INFO_SKIP_FETCH_OBJECTOBJECT_INFO_QUICK
_QUICK 选项仅防止重新准备包文件结构。当我们预计由于更新的 refs 导致对象不存在时,我们需要非常小心地提供 _SKIP_FETCH_OBJECT

这解决了t5616-partial-clone.sh. 中的损坏测试


git clone --single-branch”自动跟随标签的逻辑不小心避免延迟获取不必要的标签,这已在 Git 2.27(2020 年第二季度)中得到纠正,

参见Jeff King (peff)commit 167a575(2020 年 4 月 1 日)。
(由 Junio C Hamano -- gitster -- 合并到 commit 3ea2b46,2020 年 4 月 22 日)

clone:在跟踪标签时使用“快速”查找

签字人:杰夫·金

当使用--single-branch 进行克隆时,我们实现了git fetch 通常的标签跟随行为,抓取指向我们本地对象的任何标签对象。

不过,当我们是部分克隆时,我们的 has_object_file() 检查实际上会延迟获取每个标签。

这不仅违背了--single-branch 的目的,而且执行起来非常缓慢,可能会为每个标签启动新的提取。
这对于浅克隆来说更糟糕,这意味着--single-branch,因为即使是彼此超集的标签也会被单独获取。

我们可以通过将OBJECT_INFO_SKIP_FETCH_OBJECT 传递给调用来解决此问题,这就是git fetch 在这种情况下所做的。

同样,让我们​​包含 OBJECT_INFO_QUICK,,因为这就是 git fetch 所做的。
基本原理在5827a03545 中进行了讨论(获取:使用“快速”has_sha1_file 进行标签跟踪,2016-10-13,Git v2.10.2),但这里的权衡将更加适用,因为克隆不太可能与另一个重新打包我们新创建的存储库的进程竞争。

即使在非部分情况下,这也可能提供非常小的加速,因为我们会避免为每个标签调用reprepare_packed_git()(尽管在实践中,我们只有一个包文件,所以 reprepare 应该是很便宜)。


在 Git 2.27(2020 年第二季度)之前,当客户端需要时,使用在线协议版本 2 通过“git://”和“ssh://”协议为“git fetch”客户端提供服务在服务器端存在错误向例如发出后续请求自动关注标签。

参见Christian Couder (chriscool)commit 08450ef(2020 年 5 月 8 日)。
(由 Junio C Hamano -- gitster -- 合并到 commit a012588,2020 年 5 月 13 日)

upload-pack:为每个 v2 获取命令清除 filter_options

帮助者:Derrick Stolee
帮助者:Jeff King
帮助者:Taylor Blau
签字人:Christian Couder

由于协议v2的请求/响应模型,upload_pack_v2()函数有时会在同一个进程中被调用两次,而'structlist_objects_filter_optionsfilter_options'在'upload-pack.c开头被声明为静态'。

这使得list_objects_filter_die_if_populated() 中的检查(由process_args() 调用)在第二次调用upload_pack_v2() 时失败,因为第一次已填充filter_options

为了解决这个问题,filter_options 不再是静态的。它现在由upload_pack() 直接拥有。它现在也是 'struct upload_pack_data' 的一部分,因此它被 upload_pack_v2() 间接拥有。

从长远来看,我们的目标是让upload_pack() 也使用'struct upload_pack_data',因此将filter_options 添加到此结构中比让upload_pack_v2() 直接拥有它更有意义。

这修复了 d0badf8797 记录的 2 个错误中的第一个(“partial-clone:在部分获取中演示错误”,2020-02-21,Git v2.26.0-rc0 -- merge 中列出的 @987654396 @)。


在 Git 2.29(2020 年第四季度)中,pretend-object 机制会在决定将数据保留在核心之前检查给定对象是否已存在于对象存储中,但该检查会触发从承诺人遥控器。

参见Jonathan Tan (jhowtan)commit a64d2aa(2020 年 7 月 21 日)。
(由 Junio C Hamano -- gitster -- 合并于 commit 5b137e8,2020 年 8 月 4 日)

sha1-file: 使pretend_object_file() 不预取

签字人:Jonathan Tan

pretend_object_file() 被一个不存在的对象调用时(这是典型的情况),不需要从promisor 远程获取任何东西,因为调用者已经知道该对象应该包含什么。因此,抑制取指。 (添加OBJECT_INFO_QUICK 标志也是出于同样的原因。)

当在未提交修改的文件上运行“blame”时,$DAYJOB 注意到了这一点。

【讨论】:

  • 看不到任何文档 re: "partial" "narrow" 或 "--filter" 选项。 git 版本 2.19.0.windows.1
  • @NOYB 你会在github.com/git/git/commit/…看到它
  • 显然 github 不支持部分克隆(--filter)。 “警告:服务器无法识别过滤,忽略” 整个 1.4 GB 存储库仍被克隆。 git clone --no-checkout --filter=blob:none "github.com/freebsd/freebsd-ports" pc1
  • @NOYB 确切地说:这仍在部署中,尚未得到主要 Git 存储库托管服务的支持。您需要使用最新的 Git 作为镜像运行自己的服务器,以便从该镜像克隆/推送/拉取。
  • 感谢您告知我即将推出的部分克隆功能。一旦它被 github 支持,它应该是非常有用的功能。但是现在需要一个本地完整克隆会破坏我的目的。关于 github 支持的大概时间框架有什么想法吗?谢谢
猜你喜欢
  • 1970-01-01
  • 2017-06-07
  • 1970-01-01
  • 2011-09-08
  • 2019-08-11
  • 1970-01-01
  • 2013-07-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多