delphij's Chaos

选择chaos这个词是因为~~实在很难找到一个更合适的词来形容这儿了……

23 May 2021

本地 git 的 partial clone

partial clone 是 git 的一项旨在减少空间和网络带宽占用的特性。 它会跳过下载那些可能不会用到的 git 对象,而是仅仅在需要时才去下载。对于网络延迟较低且带宽不愁的用户来说, 这样做往往会节省掉不少不必要的磁盘空间占用,而代价是可能失去离线访问的能力。除此之外,有些操作, 例如 git blame 或者 git log -p 很可能会需要与服务器交互,从而会变得略慢一些。

比较有用的场景是在使用某些历史比较久,或是对文件整体替换较多,而大部分情况下只关注最新版本的代码库。 与较早的 --depth 1 相比,partial clone的优点在于想要访问历史时仍然可以像正常的clone一样访问。

今天闲来无事打算把机房的服务器稍微升级一下,于是顺手弄了一下 partial clone,稍微记一笔作弊条。

服务器

要使用此特性,服务器必须支持 Git wire protocol v2。 这个新版的协议是 几年前 Brandon Williams 在 Google 时设计并实现的,现时的 git 版本都应该已经支持了。

其次是服务器必须允许客户端发起 filter (此特性是在 git 2.19 时引入的)请求。默认情况下,git 服务器并不允许这样做, 因为带 filter 的请求会产生比不带 filter 的请求消耗更多 CPU。

启用 filter 的支持的方法是在服务器端将对应的库的 uploadpack.allowFilter 设置为 true,例如:

$ git config uploadpack.allowFilter true

客户端

客户端比较简单,只要在 git clone 时指定 --filter=blob:none 就可以了。

效果

以 FreeBSD 的源代码为例,使用 --filter=blob:none 时, .git 需要占用 553MB 的空间,而不使用的话则需要 1.3GB。

其他替代方案

限制复制代码库时的历史深度(depth)

使用 --depth 1 可以达到节约空间的效果。以同一代码库为例,其 .git 只需 261MB 的空间,但需要说明的是这样一来历史是无法访问的, 假如未来需要从其他分支去 cherry-pick 的话,需要做一次 fetch --unshallow

使用worktree

如果本地已经有一份 clone,那么用 worktree 可能是比较理想的方案。 不过,worktree 要求 owner 是同一个人,并且对分支的使用有一些限制。 假如本地的另外一份 clone 是只读的话,这个会比较不方便。 我个人在本地的开发通常会使用 worktree。