背景
近期需要对一个项目进行一些小更改,仓库本身是很小的,但是在 git clone
项目的时候却等待了很久。如图:
不禁产生了疑惑,10M体积左右的仓库为何会产生200多M的数据?
原因分析
通过执行命令 du -d 1 -h
可以分析出当前项目各个文件的体积:
可以看出是 .git
文件占了主要的占比,进入目录详情查找发现,是有一个 .pack
文件特别大。
通过查找相关资料发现:
- 当使用
git add
和git commit
命令的过程中,Git
不知不觉就会创建出来blob
文件对象,然后更新index
索引,再然后创建tree
对象,最后创建出commit
对象,这些commit
对象指向顶层tree
对象以及先前的commit
对象。 - 而上述创建出来的对象,都以文件的方式保存在
.git/objects
目录下。所以,当在使用的过程中,提交了一个体积特别大的文件,就会被Git
追踪记录在.git/objects
文件夹下面。 - 此时,如果再次删除这个体积特别大的文件,其实 Git 只会记录我们删除的这个操作,但并不会把文件从 .git 文件夹下面真正的删除,即 .git 文件夹完全不会变小。
解决方案
- 识别出体积最大的几个文件(demo数量为5)
shell
git rev-list --objects --all | grep "$(git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -5 | awk '{print$1}')"
我们发现基本都是些字体、图片等静态文件。可以和当前目录文件进行对比,看哪些是已经废弃的,可以进行删除操作。
- 将该文件从历史记录的所有 tree 中移除
shell
git filter-branch --force --index-filter 'git rm -rf --cached --ignore-unmatch 你的大文件名' --prune-empty --tag-name-filter cat -- --all
以 public/font/PingFangSC-Regular.otf
为例,删除前后对比如下:
- 重复2的操作即可,确保删除的文件是已失效的
- 提交修改
shell
# 真正删除
rm -rf .git/refs/original/
git reflog expire --expire=now --all
# 清理无效文件
git gc --prune=now
git gc --aggressive --prune=now
git push --force
# 让远程仓库变小
git remote prune origin
总结
避免项目中直接存放大文件,使用对象存储/CDN的方式引用这些静态文件。