我一直使用 Git 仓库来管理我的 Markdown 笔记,但是因为定时提交,没多久就产生了非常多的提交历史,并且因为频繁的提交和导入了一些比较大的 PDF 文件和图片文件,所以导致 .git
目录的体积已经超过了所有笔记的大小,笔记内容也就 300+M,但是整个仓库有近 1G 大小。
所以便想着能不能给 Git 仓库进行一下瘦身,最开始想要实现的方向是能不能压缩一下提交历史,然后把历史记录中的大文件剔除。所以查询方案的时候就先往这两个方向上靠。
git gc
最先想到的就是在仓库执行 git gc
, (garbage collection),这条命令会对 Git 仓库中不需要的文件进行删除,然后将其他文件压缩:
git gc --aggressive
然后再执行:
git prune
git-prune
命令会删除在 object database 中不可达的 objects。不过通常在执行 git gc
的时候会自动调用该命令。
我的仓库中执行这两条命令后效果并不是很明显。
删除 Git 提交的大文件
可以使用 git count-objects -v
来查看 git 仓库占用的空间大小。
count: 391
size: 13968
in-pack: 41519
packs: 2
size-pack: 493311
prune-packable: 0
garbage: 10
size-garbage: 0
在输出的结果中 size-pack
就是包文件的大小,单位是 KB,可以看到我本地的包文件在 493MB 左右。
使用如下的命令查看仓库中的大文件:
git rev-list --objects --all | grep -E `git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -10 | awk '{print$1}' | sed ':a;N;$!ba;s/\n/|/g'`
或者:
git rev-list --objects --all | grep "$(git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -15 | awk '{print$1}')"
解释:在 git gc
命令执行之后,所有的对象会被放到一个打包的文件中,存放在 .git/objects/pack/*.idx
,可以通过 git verify-pack
命令,对输出的第三列(文件大小)进行排序,从而找出这个大文件。
然后使用 git rev-list
命令使用 --objects
参数来找出相关的 SHA-1,对象的 SHA-1 和相关联的文件路径。
然后可以使用 git filter-branch
来改写历史,移除大文件
# 下面的命令请谨慎执行,在多人合作的仓库中小心执行
git filter-branch --tree-filter 'rm -f path/to/large/files' --tag-name-filter cat -- --all
git push origin --tags --force
git push origin --all --force
说明:--tree-filter
这里的路径别搞错。
bfg
在搜寻的过程中发现了 git filter-branch
的代替工具 bfg
可以比 filter-branch 命令更快。
reference
In my case, I pushed several big (> 100Mb) files and then proceeded to remove them. But they were still in the history of my repo, so I had to remove them from it as well.
What did the trick was:
bfg -b 100M # To remove all blobs from history, whose size is superior to 100Mb
git reflog expire --expire=now --all
git gc --prune=now --aggressive
Then, you need to push force on your branch:
git push origin <your_branch_name> --force
bfg
bfg 可以使用 brew 来安装:
brew install bfg
# 清除垃圾文件(大量无用的mp3文件)
git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch *.mp3' --prune-empty --tag-name-filter cat -- --all
# 提交到远程仓库(如GitHub, 我再次从git clone GitHub代码库会变小为1.3M)
git push origin --force --all
# 必须回收垃圾,本地仓库才变小
git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin
git reflog expire --expire=now --all
git gc --prune=now
rm -rf .git/refs/original
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now