Post

GitHub 的 Git LFS 奇遇

本月 21 号晚上,我的邮箱收到一封了由 GitHub 发送,标题为「At 80% of Git LFS data quota for cotes2020」的邮件,内容是说我账号的 Git LFS 用量使用了 96% 的额度。所以马上登陆 GitHub 一探究竟,原来是一个开源仓库消耗了这些空间:

lfs-quota

repo size 看到这幅景象,开始思考问题,首先我平常在公开项目的 commit 从来没有使用 Git LFS,其次上述开源项目的体积仅有 2.64 MB, 就算全部存储在 LFS,也不应该有 0.96 GB 这么夸张的数字。

所以有两个疑问:

  1. 为何会使用了 Git LFS?
  2. 为何 LFS 用量达到 0.96 GB?

翻阅 GitHub Docs 的这篇文章,里面说删除云端的 LFS 文件需要联系 GitHub Support。于是给客服发了邮件,并很快得到了回复,说有 4 个文件通过我的账号上传到 LFS 的空间,体积大概 28 KB,而且还说了一个更加惊人的事情:所有 fork 的下游仓库在 pull 以及存储都会消耗根仓库的 LFS 的空间和带宽额度1,所以这 0.96 GB 的空间是近 2k 的 fork 产生并累计到我的账号上的,这个答复解决了 疑问 2

移除 Git LFS 文件

虽然 GitHub Support 帮忙在后台清零云端的存储,但这仅仅是治标,从根本解决问题是找出占用 LFS 的文件,并把它们从 LFS 迁移到常规的 Git Object。

首先是定位 LFS 文件:

1
2
3
$ cd REPO
$ git lfs ls-files -a -l
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 * .nojekyll

接着,根据 GitHub Support 的建议,把占用 LFS 的文件从历史中剔除,方法有很多种:

git-lfs-migrate 对我的情况不适用,因为我并没有真正使用 Git LFS,下文会细述。

我选用了 BFG,在每次剔除一个文件之后,再次用 git lfs ls-files -a -l 检查仓库,以免出现漏网之鱼。前后共找出了两个目标文件:

1
2
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 * .nojekyll
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 * ._scripts/py/utils/__init__.py

其实过程中还出现很多在未合并 PR 里面提交的文件,因为它们都不曾出现在根项目的提交历史中,所以可以忽略不做处理。

恢复 Git Object

接着,是在仓库恢复上述被剔除的必要文件:touch <FILE_NAME>,这样就产生了一个空白的文件。但奇怪的是,用 git lfs ls-files 检查,居然又被 LFS 跟踪了!为了排除是本地 git-lfs 的影响的嫌疑,我使用 GitHub 网页的 Add file 在远端创建一个空文件,然后 pull 到本地,却可以通过 git lfs ls-files 的检测。

这两种方式创建空文件的差异成了新的疑问,可惜一时间找不到答案。隔了一段时间,无意间使用 ls -l 时发现了一个有趣的现象:touch 创建的文件体积是 0 byte,而 GitHub 创建的空文件是 1 byte。所以是 git-lfs 会自动跟踪 0 字节文件。再上网搜索相关的问题,在 git-lfs/issue/4660 找到了这个问题的解释。

看来 GitHub 远端使用 Git LFS 的原因(疑问 1)弄清了,要避免「空文件」被存入 LFS,就要让「空文件」的体积大于 0,用 ddecho 都可以创建 1 byte 最小体积的文件:

1
2
$ dd if=/dev/zero of=emptyfile bs=1 count=1
$ echo "" > emptyfile

相比 dd,使用 echo 的效果会更佳,因为输出的内容是一个换行符,生成的文件可以被文本编辑器打开,这也是 GitHub 网页的 Add file 新建文件的效果。

事件总结

源码的空白文件体积必须大于 0 byte,不然在推送到 GitHub 之后,会被存储在 Git LFS,即便本地没有安装 git-lfs 也会是这种结果,因为 GitHub 云端是有 git-lfs 的。其次,GitHub 没有 Git LFS 文件列表的管理页面,找 GitHub Support 也没权限查看用户文件,真的很不方便(隔壁 Bitbucket 就有这个功能),这是不希望用户迁出 LFS 空间,出售 5 刀每月的数据包服务吗?最后,剔除历史文件将会重写所有 commit 历史,造成的影响是所有的 fork 都会炸锅,没法合并。

引用

This post is licensed under CC BY 4.0 by the author.

Comments powered by Disqus.