git历史大文件清理

799 阅读4分钟

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小文件合成的 image.png

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

image.png

filter-branch失败了,因为有文件没提交,重新执行了 image.png