Git 仓库文件太大怎么瘦身清理历史记录
bash du -sh .git
这会显示 `.git` 目录的大小。如果这个数字远大于项目源码的体积,说明历史记录中有冗余数据。
### 找出历史中的大文件
**方法一:使用 `git rev-list` 遍历所有对象**
```bash
git rev-list --objects --all | \
git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
sed -n 's/^blob //p' | \
sort --numeric-sort --key=2 | \
tail -n 20
这条命令会列出仓库中最大的 20 个 blob(文件)。输出示例:
...
f7742... 49807360 some-large-video.mp4
a1b2c... 51200000 old-database.sql.gz
方法二:使用脚本 git-find-big-files
如果你习惯开箱即用的脚本,可以安装 git-extras 工具包:
# macOS
brew install git-extras
# Debian/Ubuntu
sudo apt install git-extras
git big-file
git big-file 会直接展示历史上最大的文件。
查看具体文件在哪些提交中出现
得到可疑文件名后,用 git log 追踪它的足迹:
git log --all --pretty=format:"%H %s" -- path/to/large-file
记录下来,之后在重写历史时可以用作过滤依据。
方法一:用 BFG Repo-Cleaner 快速清除(推荐新手)
BFG 是专门为快速清理 Git 历史而设计的工具,比原生 git filter-branch 快很多,并且用法简单。
安装 BFG
下载 bfg.jar (确保 Java 已安装)。
wget https://repo1.maven.org/maven2/com/madgag/bfg/1.14.0/bfg-1.14.0.jar
清理步骤
-
备份仓库
重写历史是破坏性操作,务必先备份或确认远程仓库有分支保护。 -
克隆一份裸仓库
BFG 需要在裸仓库上运行:git clone --mirror your-repo-url.git repo-mirror.git cd repo-mirror.git -
删除大于 100M 的所有文件
java -jar ~/bfg-1.14.0.jar --strip-blobs-bigger-than 100M . -
删除特定文件类型或文件
# 删除所有 *.zip 文件 java -jar bfg.jar --delete-files '*.zip' . # 精确删除某个路径下的文件 java -jar bfg.jar --delete-files path/to/hugefile.iso . -
清理无效数据并推送
git reflog expire --expire=now --all && git gc --prune=now --aggressive git push --force
BFG 默认会保护当前 HEAD 所在分支的最新提交,即最新的文件不会被删除,除非使用
--no-blob-protection。
方法二:使用 git filter-repo(官方推荐的重写工具)
从 Git 2.22 开始,官方建议使用 git filter-repo 替代 filter-branch。它更快、更安全,但需要通过包管理器或 pip 安装。
安装
# macOS
brew install git-filter-repo
# Linux (通过 pip)
pip install git-filter-repo
# 或者从源码安装
git clone https://github.com/newren/git-filter-repo.git
cd git-filter-repo
make install
使用场景
删除所有 *.tar.gz 文件
git filter-repo --path-glob '*.tar.gz' --invert-paths
--invert-paths 表示选择不匹配该路径的文件,即排除匹配的部分,只保留其他文件。
仅保留指定目录
如果想只保留 src/ 目录,其他全部删除:
git filter-repo --path src/
按大小批量删除
可以先列出超过 50MB 的文件路径,再传给 filter-repo:
# 1. 生成大文件列表
git rev-list --objects --all | \
git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
awk '/^blob/ {if ($3>52428800) print $4}' > large-files.txt
# 2. 滤除这些文件
git filter-repo --invert-paths --paths-from-file large-files.txt
操作完成后同样需要执行垃圾回收并强制推送。
方法三:基于 git filter-branch 的原生方案(不推荐但可用)
如果你无法安装第三方工具,可以使用内置的 filter-branch。但请注意,此方法已不推荐使用,且执行速度很慢。
删除某个特定文件
git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch path/to/bigfile.bin' \
--prune-empty --tag-name-filter cat -- --all
删除所有超过某个大小的文件
这需要配合 find 命令判断大小:
git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch $(git rev-list --objects --all | \
git cat-file --batch-check="%(objecttype) %(objectname) %(objectsize) %(rest)" | \
awk "/^blob/ {if (\$3>104857600) print \$4}")' \
--prune-empty --tag-name-filter cat -- --all
执行后同样清理:
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --aggressive --prune=now
清理后的必要步骤
无论使用哪种方法,重写历史后都需要强制推送所有分支和标签,并压缩仓库。
1. 清理本地冗余数据
git reflog expire --expire=now --all
git gc --prune=now --aggressive
2. 强制推送
git push origin --force --all
git push origin --force --tags