问题: 如何在部分克隆的 git 存储库中删除/修剪/gc 未引用的 blob?
详情:我正在评估Git Partial Clone是否可以成为Git LFS的替代品,现在GitLab和GitHub似乎都已经实现了对它的全面支持(我找不到beta标签)。由于大型二进制文件/blob 仅在使用
--filter=blob:none
结帐时获取,因此磁盘上的 git 存储库和获取性能似乎都相当快。
话虽这么说,我正在努力如何清理/修剪类似于 git LFS prune 的斑点。当随着时间的推移使用合理大小的存储库时,您最终仍然会得到随着时间的推移积累的所有 blob,而这些 blob 不再被 HEAD 引用。
我尝试过的:我希望
git gc --prune=today --aggressive
能够理解活动过滤器(或者我可以通过过滤器)来转换当前结账未引用的所有blob/树/提交,到承诺对象。不幸的是,在使用了相当长一段时间后,我找不到减少部分克隆存储库大小的方法
编辑:我使用的是 git 版本 2.36.0
我希望
能够理解活动过滤器(或者我可以通过过滤器)将当前结帐未引用的所有blob/树/提交转换为promisor对象。git gc --prune=today --aggressive
现在应该可以使用 Git 2.48(2025 年第 1 季度),第 10 批:
git gc
"(man) 会丢弃 Promisor 包中的对象引用的 Promisor 包之外的任何对象,并且我们不会在运行时从 Promisor 中重新获取它们,从而导致无法使用的存储库。请参阅 commit c08589e、commit d9e24ce、commit 78995ff、commit da80429(2024 年 11 月 1 日),作者:Jonathan Tan (
jhowtan
)。gitster
-- 合并于 commit 0c11ef1,2024 年 11 月 20 日)
:将本地链接重新打包到承诺包中index-pack
签署人:Jonathan Tan
教导index-pack在处理CLI上指定的
包中的对象时,将这些对象引用的本地对象(以及它们递归引用的本地对象)重新打包到promisor包中。--promisor
这可以防止出现这样的情况:当从承诺者远程获取时,我们最终得到承诺者对象(新获取的)引用非承诺者对象(在获取之前本地创建的)。
例如,如果客户端之前已将对象推送到远程,则可能会出现这种情况。
在这种情况下出现的一个问题是,如果非promisor对象变得无法访问,除非通过promisor对象(例如,如果指向它们的分支已移动到指向引用它们的promisor对象),那么GC将产生垃圾收集它们。
还有其他方法可以解决这个问题,但最简单的似乎是强制执行不变量,即我们没有引用非承诺对象的承诺对象。此重新打包是从索引包完成的,以最大限度地减少性能影响。
在获取期间,大多数对象在内存中完全膨胀的唯一时间是计算其对象 ID 时,因此我们也会在此期间扫描对象(以查看它们引用了哪些对象)。此外,为了最大限度地减少性能影响,如果对象是松散对象或存在于非承诺包中,则该对象将被计算为本地对象。
(如果它也在承诺者包中或由承诺者包中的对象引用,那么从技术上讲,它已经是承诺者对象。
但是,将承诺者对象错误识别为非承诺者对象在这里相对良性 - 因此,我们将将该承诺者对象重新打包到承诺者包中,并将其复制到对象存储中,但不存在正确性问题,只是效率低下的问题。 )
git index-pack
现在包含在其 手册页中:
此外,如果给定包中存在引用非承诺者的对象 对象(在存储库中),将那些非承诺者对象重新打包到承诺者中 盒。这避免了存储库具有可通过承诺者对象访问的非承诺者对象的情况。
测试:“获取非承诺者提交的后代后,gc 工作”
# Setup
git init full &&
git -C full config uploadpack.allowfilter 1 &&
git -C full config uploadpack.allowanysha1inwant 1 &&
touch full/foo &&
git -C full add foo &&
git -C full commit -m "commit 1" &&
git -C full checkout --detach &&
# Partial clone and push commit to remote
git clone "file://$(pwd)/full" --filter=blob:none partial &&
echo "hello" > partial/foo &&
git -C partial commit -a -m "commit 2" &&
git -C partial push &&
# gc in partial repo
git -C partial gc --prune=now &&
# Create another commit in normal repo
git -C full checkout main &&
echo " world" >> full/foo &&
git -C full commit -a -m "commit 3" &&
# Pull from remote in partial repo, and run gc again
git -C partial pull &&
git -C partial gc --prune=now