Git 仓库随着 commit 次数的增加变得越来越大,尤其是当 push 了大文件之后,即使后面把该文件删除了,也会被 Git 记录下来,以便能够随时回滚到指定提交。
以笔者的某个 Git 仓库为例,整个目录有 3GB,其中 .git 目录本身就占了 2.7GB,Git 仓库过大的话会有如下问题:
- 占用电脑的存储空间
- 别人 clone 耗时太长
- 切换分支占用内存
如果之前提交的大文件后面不再使用了,可以考虑对仓库进行一次「瘦身」。先说结果吧,瘦身后的仓库只有 80M,整整优化了 97% 的体积!那是如何操作的呢?
这里推荐一个工具叫 BFG, 可以快速帮助我们清理 Git 仓库,非常令人放心的是,该工具不会删除最后一次提交时仓库里面的任何文件,只是对历史记录进行清理。话不多说,直接看操作步骤:
第一步:安装 BFG 命令行工具
BFG 依赖 Java 环境,所以确保电脑上已经安装了 Java。然后去官网下载 jar 包,例如 bfg-1.14.0.jar。然后在命令行创建别名:
alias bfg='java -jar /xxxx/bfg-1.14.0.jar'
如果是 Mac 的话,可以直接用 brew 来安装:
brew install bfg
第二步:下载镜像仓库
命令是:
git clone --mirror git://example.com/your-repo.git
git clone 命令实现版本库克隆,主要有如下三种用法:
git clone <repository> <directory>
git clone --bare <repository> <directory.git>
git clone --mirror <repository> <directory.git>
这里采用第三种,即带 --mirror
参数的。你可能会问,这三种有什么区别呢?
- 用法1将
<repository>
指向的版本库创建一个克隆到<directory>
目录。目录<directory>
相当于克隆版本库的工作区,文件都会检出,版本库位于工作区下的.git
目录中。 - 用法2和用法3创建的克隆版本库都不含工作区,直接就是版本库的内容,这样的版本库称为裸版本库。一般约定俗成裸版本库的目录名以
.git
为后缀,所以上面示例中将克隆出来的裸版本库目录名写做<directory.git>
。 - 用法3区别于用法2之处在于用法3克隆出来的裸版本对上游版本库进行了注册,这样可以在裸版本库中使用 git fetch 命令和上游版本库进行持续同步。
如果仓库较大,建议下载完了之后,先用下面的命令备份一下,方便等会操作失误重做。
cp -r your-repo.git your-repo-backup.git
第三步:执行清理命令
根据需要,运行 bfg 命令,例如清理所有超过 10M 的文件:
bfg --strip-blobs-bigger-than 10M your-repo.git
清理前 100 大的文件:
bfg --strip-biggest-blobs 100 your-repo.git
指定文件或按照通配符清理文件:
bfg --delete-files "node.exe" your-repo.git
bfg --delete-files "*.zip" your-repo.git
保护指定分支(默认是master),这个命令非常有用,表示哪些分支的最新提交后的文件不要删除:
bfg --protect-blobs-from master,feat/latest --delete-files "*.zip" your-repo.git
bfg --protect-blobs-from master,dev,stage --strip-biggest-blobs 100 your-repo.git
当然,如果你不想在任何分支中看到这个文件的话,可以这么写:
bfg --delete-files "*.zip" --no-blob-protection your-repo.git
bfg 非常棒的地方是,每次命令都会生成日志,放在跟 your-repo.git 仓库平级的 your-repo.git.bfg-report 目录下,有个 deleted-files.txt 可以清晰的看到哪些文件被删除了,例如:
d2c18a31fef1d60f47 8316352 dog.png e00f5efc61fc18a37c 8205751 cat.png c8c27e0758df6d9795 9878146 wix.exe
第四步:更新 Git 历史
确定第三步想要清理的命令都执行完了,运行下面的命令对仓库历史进行改写,运行速度会比较慢:
cd your-repo.git
git reflog expire --expire=now --all && git gc --prune=now --aggressive
第五步:提交到远程仓库
到目前为止,所有的操作还只是本地的,需要把瘦身过的仓库提交到远程:
cd your-repo.git
git push --mirror
这个时候,仓库的历史才被真正改写了,重新 clone 之后,将获得瘦身后的仓库。
不过在多人协作的时候,你要告诉所有协作者删除本地旧仓库,重新 clone 新仓库,如果某些小伙伴一不小心又把本地旧仓库 push 上去,你的工作都白做了...
所以为了彻底防止上述情况发生,也可以重新建一个新仓库,用下面的命令改写 origin 地址,push 到新仓库,通知所有人下载这个新仓库即可:
cd your-repo.git
git remote set-url origin your-new-repo.git
git push --mirror