git大文件清理这个需求,一般很少出现,我遇到这个问题是因为:
-
项目超过很多年
-
分支数上千
-
clone下来的代码超过30G了
这就是痛点了,对于服务器打包或者本地clone项目,都是难以接受
我们知道,git每次commit提交,都是对当前项目的一次快照,会生成改变文件的object对象,然后tree对象,然后生成commit对象。其中,object对象是每个文件的hash文件,对于一般的文本文件来说通常不大,但是对于音视频等大文件来说,也会直接添加到项目版本库中,随着项目迭代,这些文件也会别替换更新,但是这些历史文件虽然不需要再使用了,但是依然存在git仓库中,随着每次 git clone 都下载到本地
于是才有了,我公司项目超过30G了,太多历史文件了,这次git历史大文件清理的目的就是清楚历史文件。
要做这件事这里,我们提出两个:
1、历史记录中只删除视频,音频,图片,压缩包,安装包等文件(因为这些文件大)
2、保留文本文件(代码相关)
这里,我们提供两种方式:
1、bfg.jar 包,官网
-
git删除提交时本质上是逻辑删除,文件依然保存repo里,存放于.git
-
传统 git 提供的 「filter-branch」option 命令慢、操作复杂,且可能出现复杂的问题
-
git-filter-branch的另一种选择
-
核心功能是用来过滤删除大文件
-
比「filter-branch」更快:10-720x
-
Java实现
bfg.jar
使用方式是,将下列shell代码放到文件中,命名如 mybfg.sh
将 bfg.jar 包 和 mybfg.sh 放到一起,执行 sh mybfg.sh,后续每一步都会有提示,直接输入y就行
注意:这个会删掉所有出master的分支,另外所有开发需要重clone项目,不能使用之前的项目了不然历史又推上来了
#!/bin/bash
set e
# 清理git包
# 先确保gitlab已关闭master的保护分支 Setting -> Repository -> Protected branches ,先点Unprotected
# push后体积增大原因:https://github.com/rtyley/bfg-repo-cleaner/issues/68
# version 1.46
echo "开始执行"
git="$(git --version 2>&1 |awk 'NR==1{print $3}')"
if [ -z "$git" ];then
echo "【失败】请先安装git"
exit
else
echo "1、获取到git版本$git"
fi
java="$(java -version 2>&1 |awk 'NR==1{print $3}')"
if [ -z "$java" ];then
echo "【失败】请先安装JDK"
exit
else
echo "2、获取到java版本$java"
fi
bfg="$(find ./ -name bfg-* | head -n 1)"
if [ -z "$bfg" ];then
echo "【失败】请先下载bfg.jar包放到到当前目录"
exit
else
bfg="$(readlink -f $bfg)"
echo "3、获取到bfg.jar包$bfg"
fi
echo "4、【请输入】项目名,如static-page等"
read project
folder="$project$(date +%Y%m%d)_$(date +%H%M%S)"
echo "5、进入文件夹 $folder"
mkdir $folder
cd $folder
echo "5、【请输入】master是否更新提交 (y/n)"
read unewmaster
if [[ "$unewmaster" == "y" || "$unewmaster" == "Y" ]];then
echo "开始执行 master是否更新提交"
git clone http://skt.abcmoreonline.com:8088/a/kk-front-end/$project.git
cd $project
echo $(date) > .gitsmaller
git add ./.gitsmaller
git commit -m "feat: update .gitsmaller"
git push
cd ../
rm -rf $project
else
echo "跳过master更新提交,如果最终瘦身失败,请选y"
fi
echo "6、开始执行 git clone --mirror http://github.com/a/front-end/$project.git"
git clone --mirror http://github.com/a/front-end/$project.git
echo "7、开始执行 java -jar $bfg --delete-files '*.{png,jpg,jpeg,gif,svg,webp,mp4,zip,rar,svga}' $project.git"
# java -jar $bfg --strip-blobs-bigger-than 30K $project.git
java -jar $bfg --delete-files "*.{png,jpg,jpeg,gif,svg,webp,mp4,zip,rar,svga}" $project.git
# java -jar $bfg --delete-folders "{hth,leyu,nyb,qiusu,yabo,yb}" $project.git
echo "8、开始执行 cd $project.git && git reflog expire --expire=now --all"
cd $project.git
git reflog expire --expire=now --all
echo "9、开始执行 git gc --prune=now "
date
git gc --prune=now
date
echo "10、开始执行 git config --unset remote.origin.mirror"
git config --unset remote.origin.mirror
echo "11、【请输入】是否立即push到服务器 (y/n)"
read push
if [[ "$push" == "y" || "$push" == "Y" ]];then
# echo "开始执行 git push -u origin master -f"
# git push -u origin master -f
echo "开始执行 git push -f --all"
git push -f --all
git push origin --force 'refs/heads/*'
git push origin --force 'refs/tags/*'
git push origin --force 'refs/replace/*'
else
echo "跳过push"
fi
echo "12、执行完毕!!"
echo "注意检查push后输出Writing objects: 100% (2239/2239), 4.40 GiB | 380.19 MiB/s, done.类似表示成功。"
echo "注意检查push后所有人需要重新clone项目开发,绝对不可用之前项目"
git-filter方式
这是官方提供的清理大文件方式,这个看官方解释更清晰,地址 git-scm.com/book/zh/v2/…
我们把步骤梳理下:
0、先查看文件数量和大小,和最后gc后比较
git count-objects -v
1、git gc触发后会整理零散object对象,生成gc后的大文件,当然我们可以手动执行此命令
git会自动触发gc,当版本库大约需要 7000 个以上的松散对象或超过 50 个的包文件才能让 Git 启动一次gc
可以看到pack目录下是外面hash小文件合成的
2、执行git verify-pack获得前3大文件
最下面文件最大,1M,第一个数字表示文件大小,如果这三个文件就是我们可能要移除的,因为还要判断文件格式
git verify-pack -v .git/objects/pack/pack-d8fbb0e1716bf20b86409360dd35778e82bdc0cb.idx | sort -k 3 -n | tail -3
3、执行git rev-list查看文件路径
git rev-list --objects --all | grep dc7fa2a
4、查看历史记录中文件有哪些提交,修改最近一次提交前所有记录
git log --oneline --branches -- package-lock.json
5、修改历史
--index-filter 选项类似于在 重写历史 中提到的的 --tree-filter 选项, 不过这个选项并不会让命令将修改在硬盘上检出的文件,而只是修改在暂存区或索引中的文件。
git filter-branch --index-filter 'git rm --ignore-unmatch --cached package-lock.json' -- c5f0c18^..
6、移除文件和gc
rm -Rf .git/refs/original
rm -Rf .git/logs/
git gc
filter-branch失败了,因为有文件没提交,重新执行了