The future of large files in Git is Git

Tyler Cipriani

Git 大文件的未来,依然是 Git

原文由 Tyler Cipriani 发布,订阅该博客

如果说 Git 有什么天敌,那一定是大文件。

大文件会撑爆 Git 的存储,拖慢 git clone 的速度,还会让 Git 托管平台不堪重负。

2015 年,GitHub 发布了 Git LFS——一个用来绕过大文件问题的 Git 扩展。但 Git LFS 也带来了新的复杂性和存储成本。

与此同时,Git 项目一直在悄悄攻关大文件问题。虽然 LFS 还没死,但最新版 Git 已经指明了一条道路——在不远的将来,LFS 终将被淘汰。

今天你就能做的事:用 Git 部分克隆替代 Git LFS

Git LFS 的原理是将大文件存储在仓库之外。

通过 LFS 克隆项目时,你会拿到仓库的历史记录和小文件,但会跳过大文件。取而代之,Git LFS 只会下载你当前工作区所需的大文件。

2017 年,Git 项目引入了部分克隆,它能提供与 Git LFS 相同的好处:

部分克隆让我们可以在 clone 和 fetch 期间避免预先下载[大型二进制资源],从而缩短下载时间、减少磁盘占用。

—— 部分克隆设计说明,git-scm.com

Git 的部分克隆和 LFS 都能带来:

  1. 更小的检出——克隆时,你拿到的是大文件的最新一份拷贝,而不是每一份历史拷贝。
  2. 更快的克隆——由于无需下载大文件,每次克隆都很快。
  3. 快速就绪——与浅克隆不同,你仍然拥有项目的完整历史,可以马上投入工作。

什么是部分克隆?

Git 部分克隆就是带 --filter 参数的克隆。

例如,要避免下载大于 100KB 的文件,可以这样操作:

git clone --filter='blobs:size=100k' <repo>

之后,Git 会在你检出需要时,再按需惰性下载超过 100KB 的文件。

默认情况下,如果我用 git clone 克隆一个包含某个烦人的 25MB PNG 文件众多版本的仓库,克隆会很慢,检出的体积也会大得离谱:

$ time git clone https://github.com/thcipriani/noise-over-git
Cloning into '/tmp/noise-over-git'...
...
Receiving objects: 100% (153/153), 1.19 GiB

real    3m49.052s

检出单个 25MB 文件竟然要将近四分钟!

$ du --max-depth=0 --human-readable noise-over-git/.
1.3G    noise-over-git/.
$ ^ 🤬

而这一个 25MB 文件的 50 个版本就占掉了 1.3GB 空间。

但部分克隆可以绕开这些问题:

$ git config --global alias.pclone 'clone --filter=blob:limit=100k'
$ time git pclone https://github.com/thcipriani/noise-over-git
Cloning into '/tmp/noise-over-git'...
...
Receiving objects: 100% (1/1), 24.03 MiB

real    0m6.132s
$ du --max-depth=0 --human-readable noise-over-git/.
49M     noise-over-git/
$ ^ 😻 (the same size as a git lfs checkout)

我的过滤条件让克隆速度提升了 97%(3m 49s → 6s),检出体积也缩小了 96%(1.3GB → 49M)!

不过,这里还是有一些注意事项。

如果你运行的命令需要用到被过滤掉的数据,Git 就得去服务器上拉取。因此,像 git diffgit blamegit checkout 这类命令,执行时都需要向 Git 托管平台请求数据。

但对于大文件来说,这和 Git LFS 的行为是一样的。

再说了,我可不记得自己上次对 PNG 文件跑 git blame 是什么时候了 🙃。

何必这么折腾?Git LFS 有什么问题?

Git LFS 把 Git 处理大文件的问题转嫁给了用户。

而且问题还不小:

  • 🖕 高度的厂商锁定——GitHub 编写 Git LFS 时,其他大文件系统——Git Fat、Git Annex 和 Git Media——在服务端上都是中立的。但 GitHub 却把用户锁定在了自家的私有服务端实现上,还要为此收费。1
  • 💸 成本高昂——GitHub 能胜出,是因为它让用户可以免费托管仓库。但 Git LFS 一开始就是付费产品。如今虽然有了免费额度,定价却完全取决于 GitHub 的心情。如今在 GitHub 上存放一个 50GB 的仓库,存储费就要 40 美元/年,而在 Amazon S3 标准存储上存 50GB 只要 13 美元/年。
  • 😰 难以撤销——一旦迁移到 Git LFS,不重写历史就无法撤销。
  • 🌀 持续的配置成本——所有协作者都得安装 Git LFS。没装的话,他们拿到的就不是预期的大文件,而是让人一头雾水的、塞满元数据的文本文件。

未来:Git 大对象承诺者

大文件也会给 Git 托管平台带来麻烦。

GitHub 和 GitLab 都对文件大小做了限制2,因为大文件的托管成本更高。Git LFS 通过把大文件卸载到 CDN 来降低服务端的成本。

但 Git 项目已经有了新的解决方案。

今年早些时候,Git 合并了一项新功能:大对象承诺者。大对象承诺者旨在提供与 LFS 相同的服务端收益,同时免去给用户带来的麻烦。

这项工作尤其旨在改善服务端的情况,特别是针对那些已经以二进制格式压缩过的大型 blob。

这项工作旨在提供 Git LFS 的替代方案

—— 大对象承诺者,git-scm.com

什么是大对象承诺者?

大对象承诺者是只存放大文件的特殊 Git 远端。

在那个光明美好的未来,大对象承诺者将这样工作:

  1. 你把大文件推送到 Git 托管平台。
  2. 后台,Git 托管平台会把这个大文件卸载到大对象承诺者上。
  3. 当你克隆时,Git 托管平台会把承诺者的信息告诉你的 Git 客户端。
  4. 你的客户端会从 Git 托管平台克隆,并自动魔法般地从承诺者远端获取大文件。

但距离那个光明美好的未来,我们还有一段路要走。

Git 大对象承诺者仍在开发中。相关代码已在2025 年 3 月合并到 Git 中。但还有更多工作要做,也有不少悬而未决的问题有待解答。

所以就目前而言,处理超大文件你还是得靠 Git LFS。但一旦大对象承诺者得到广泛采用,也许 GitHub 就会允许你推送大于 100MB 的文件了。

Git 中大文件的未来,依然是 Git。

Git 项目正在为大文件问题绞尽脑汁,这样你就不用操心了。

今天,我们还得靠 Git LFS。

但很快,在 Git 里存放大文件的唯一阻碍,就只剩下你那隐隐约约、不祥的直觉——把整个 MP3 曲库塞进 Git 似乎不是什么好主意。


Refactoring English 编辑


  1. 后来,其他 Git 托管平台也做了自己的 LFS 服务端。如今,你可以推送到多个 Git 托管平台或使用 LFS 传输代理,但这些都会让协作者的配置变得更复杂。除非你额外下功夫去解锁,否则基本还是被锁定的状态。↩︎

  2. 文件大小限制:GitHub 100MBGitLab.com 100MB↩︎

本文章由 muse-spark-1.2-contributor 进行翻译

评论