Git 中大文件的未来就是 Git 本身
如果 Git 有什么天敌,那一定是大文件。
大文件会让 Git 的存储膨胀,拖慢 git clone,还会给 Git 托管平台带来严重麻烦。
2015 年,GitHub 发布了 Git LFS——一个绕过大文件问题的 Git 扩展。但 Git LFS 又带来了新的复杂性和存储成本。
与此同时,Git 项目一直在默默改进对大文件的处理。虽然 LFS 还没死,但最新的 Git 版本已经展现出一条通往未来的道路——在那样的未来里,LFS 终将过时。
今天就能做的事:用 Git partial clone 取代 Git LFS
Git LFS 的工作原理是把大文件存放在仓库之外。
当你通过 LFS 克隆项目时,你会得到仓库的历史记录和小文件,但会跳过大文件。取而代之的是,Git LFS 只下载你的工作副本所需的大文件。
2017 年,Git 项目引入了 partial clone(部分克隆),它能提供与 Git LFS 相同的好处:
Partial clone 让我们可以在克隆和拉取操作中避免提前下载[大型二进制资产],从而减少下载时间和磁盘占用。
– Partial Clone Design Notes,git-scm.com
Git 的 partial clone 和 LFS 都能做到:
- 更小的检出 – 克隆时,你得到的是大文件的最新副本,而不是每一份副本。
- 更快的克隆 – 因为避免了下载大文件,每次克隆都很快。
- 快速上手 – 与浅克隆不同,你能获得项目的完整历史——可以立刻开始工作。
什么是 partial clone?
Git partial clone 就是带 --filter 参数的克隆。
例如,要避免下载大于 100KB 的文件,你可以使用:
git clone --filter='blobs:size=100k' <repo>之后,Git 会按需懒加载检出所需的任何超过 100KB 的文件。
默认情况下,如果我 git clone 一个包含某个烦人的 25 MB 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 的空间。
但 partial clone 可以轻松避开这些问题:
$ 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/
$ ^ 😻 (和 git lfs 检出的大小一样)我的过滤器让克隆快了 97%(3 分 49 秒 → 6 秒),检出体积缩小了 96%(1.3GB → 49M)!
不过这里仍有一些注意事项。
如果你运行的命令需要被过滤掉的数据,Git 就得去服务器上取回它。因此,像 git diff、git blame 和 git 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 LFS,协作者得到的将是充满元数据的令人困惑的文本文件,而不是他们期望的大文件。
未来:Git large object promisors
大文件同样会给 Git 托管平台带来问题。
GitHub 和 GitLab 对文件大小设有限制2,因为托管大文件的费用更高。Git LFS 通过把大文件卸载到 CDN 来降低服务器端成本。
但 Git 项目有了一个新的解决方案。
今年早些时候,Git 合入了一个新特性:large object promisors(大对象承诺者)。Large object promisors 的目标是提供与 LFS 相同的服务器端好处,同时免去用户的麻烦。
这项工作旨在特别改善服务器端的状况,尤其是针对那些已经是二进制压缩格式的大型 blob。
这项工作旨在提供 Git LFS 的替代方案
– Large Object Promisors,git-scm.com
什么是 large object promisor?
Large object promisor 是一种只存放大文件的特殊 Git 远程仓库。
在光明美好的未来,large object promisor 将这样运作:
- 你把一个大文件推送到你的 Git 托管服务。
- 在后台,你的 Git 托管服务把这个大文件卸载到一个 large object promisor 上。
- 当你克隆时,Git 托管服务会把 promisor 的信息告知你的 Git 客户端。
- 你的客户端从 Git 托管服务克隆,并自动从 promisor 远程仓库获取大文件。
但我们距离那个光明美好的未来还有一段路要走。
Git large object promisors 仍在开发之中。其中一部分已于 2025 年 3 月合入 Git。但仍有更多工作要做,还有悬而未决的问题有待解答。
所以,就目前而言,处理超大文件你还是离不开 Git LFS。不过一旦 large object promisors 得到广泛采用,也许 GitHub 会允许你推送大于 100MB 的文件。
Git 中大文件的未来就是 Git 本身。
Git 项目正在认真思考大文件问题,好让你不必操心。
今天,我们还得忍受 Git LFS。
但很快,Git 中大文件的唯一障碍,将只剩下你脑海中那个模糊而不祥的预感:把 MP3 音乐库塞进 Git 里恐怕不是个好主意。
随机一篇博客