Git 大文件移除:彻底清理仓库历史,释放存储空间
前言
在 Git 项目开发中,有时会不小心将大文件(如视频、压缩包、数据库备份等)提交到仓库中。即使后续删除了这些文件,它们仍然会保留在 Git 的历史记录中,导致仓库体积臃肿,克隆和拉取速度变慢。本文将详细介绍如何从 Git 仓库中彻底移除大文件,并重写提交历史以释放存储空间。
移除大文件命令总结
# 1. 在powershell运行
git rev-list --objects --all | ForEach-Object { $p = $_ -split ' ', 2; if ($p[1]) { $s = [int](git cat-file -s $p[0] 2>$null); if ($s -gt 100MB) { "[$([math]::Round($s/1MB,1)) MB] $($p[1])" } } }
# 2. 输出 文件 例如
[367.5 MB] n-big-backup.json
# 3. 移除大文件
git filter-branch --force --index-filter "git rm --cached --ignore-unmatch n-big-backup.json" --prune-empty --tag-name-filter cat -- --all
# 4. 执行
git reflog expire --expire=now --all
git gc --prune=now --aggressive
git push -f origin master
具体可以查看以下内容🧑🏻🚀
1. 问题诊断:找出导致仓库臃肿的元凶
在动手清理之前,首先需要定位是哪些大文件或历史提交占用了大量空间。
1.1 查看仓库大小及对象统计
# 查看仓库的总体大小(包含所有对象)
git count-objects -vH
# 查看所有对象(blob、tree、commit)的详细统计
git gc --aggressive --prune=now
git count-objects -vH
1.2 找出最大的文件(按大小排序)
# 列出所有历史中的 blob 对象,按大小排序
git rev-list --objects --all | while read -r sha1 path; do
size=$(git cat-file -s "$sha1" 2>/dev/null || echo 0)
if [ "$size" -gt 0 ]; then
echo "$size $sha1 $path"
fi
done | sort -nr | head -20
或 注意 在 powershell
git rev-list --objects --all | ForEach-Object { $p = $_ -split ' ', 2; if ($p[1]) { $s = [int](git cat-file -s $p[0] 2>$null); if ($s -gt 100MB) { "[$([math]::Round($s/1MB,1)) MB] $($p[1])" } } }
2. 解决方案:彻底移除大文件
找到目标文件后,需要从所有分支的所有提交历史中将其删除。请注意,这会重写历史,所有协作者都需要重新克隆。
2.1 方法一:使用 git filter-branch(原生但较慢)
git filter-branch 是 Git 自带的命令,但速度较慢,适用于小型仓库或简单删除。
# 删除指定文件(例如 big_video.mp4)
git filter-branch --force --index-filter \
"git rm --cached --ignore-unmatch path/to/big_v.mp4" \
--prune-empty --tag-name-filter cat -- --all
# 删除所有 .zip 文件
git filter-branch --force --index-filter \
"git rm --cached --ignore-unmatch '*.zip'" \
--prune-empty --tag-name-filter cat -- --all
参数说明:
--force:强制覆盖备份。--index-filter:直接修改暂存区,比--tree-filter快得多。--prune-empty:删除因此操作而产生的空提交。--tag-name-filter cat:保留标签名称不变(但指向新的提交)。-- --all:对所有分支和标签进行操作。
2.2 方法二:使用 BFG Repo-Cleaner(快速、简单)
BFG 专门为删除大文件而设计,速度比 filter-branch 快几个数量级。
# 1. 克隆一个裸仓库(建议在清理前备份原仓库)
git clone --mirror git@example.com:your-repo.git
# 2. 运行 BFG 删除所有大于 100MB 的文件
java -jar bfg.jar --strip-blobs-bigger-than 100M your-repo.git
# 3. 删除指定文件(按文件名)
java -jar bfg.jar --delete-files big_video.mp4 your-repo.git
# 4. 删除指定文件夹下的所有文件
java -jar bfg.jar --delete-folders temp_bin your-repo.git
# 5. 进入裸仓库目录,执行 GC 清理
cd your-repo.git
git reflog expire --expire=now --all
git gc --prune=now --aggressive
# 6. 推送重写后的历史到远程(强制推送)
git push --force
2.3 方法三:使用 git-filter-repo(现代、功能强大)
git-filter-repo 是 Git 官方推荐替代 filter-branch 的工具,功能更强大且安全。
# 安装(Python 环境)
pip install git-filter-repo
# 1. 克隆或备份原仓库
git clone git@example.com:your-repo.git
cd your-repo
# 2. 删除指定文件
git filter-repo --path path/to/big_file.zip --invert-paths
# 3. 使用表达式删除一类文件
git filter-repo --path-glob '*.tar.gz' --invert-paths
# 4. 根据文件大小过滤(需结合分析报告手动编写脚本,或使用 --path-match 等)
# 5. 完成后,强制推送到远程
git remote add origin git@example.com:your-repo.git
git push --force --all
git push --force --tags
3. 清理本地仓库与重新克隆
历史重写后,本地仓库的旧对象依然存在,需要彻底清理。
3.1 强制垃圾回收
# 删除所有未被引用的对象(包括重写前的旧对象)
git reflog expire --expire=now --all
git gc --prune=now --aggressive
3.2 重新克隆(最彻底)
对于协作者来说,最简单安全的方式是删除本地仓库并重新克隆。
cd ..
rm -rf your-repo
git clone git@example.com:your-repo.git
4. 验证清理效果
清理完成后,确认仓库体积已减小。
# 查看当前仓库大小
git count-objects -vH
# 查看 .git/objects 目录大小
du -sh .git/objects
# 确认目标文件已不在历史中
git log --all --oneline -- path/to/big_file.zip
5. 预防措施
为了避免再次误提交大文件,可以采取以下预防措施:
- 使用 .gitignore:将临时文件、构建产物、媒体文件等加入
.gitignore。 - 设置 Git 钩子(pre-commit):在提交前检查文件大小。
# .git/hooks/pre-commit 示例 MAX_FILE_SIZE=10485760 # 10MB for file in $(git diff --cached --name-only); do size=$(git show :"$file" | wc -c) if [ "$size" -gt "$MAX_FILE_SIZE" ]; then echo "错误:文件 $file 超过 $MAX_FILE_SIZE 字节。" exit 1 fi done - 使用 Git LFS(大文件存储):对于必须版本控制的二进制大文件(如图片、模型),使用 Git LFS 管理。
- 定期清理:将仓库维护(包括清理历史)纳入日常开发流程。
6. 总结
彻底移除 Git 历史中的大文件需要重写提交历史,这是一个破坏性操作。务必在操作前完整备份仓库,并通知所有协作者。流程可总结为:
- 诊断:使用
git rev-list、git filter-repo --analyze或 BFG 找出大文件。 - 选择工具:根据仓库大小和复杂度,选择
git filter-branch、BFG 或git-filter-repo。 - 执行清理:运行工具命令,从所有分支和标签中删除目标文件。
- 强制推送:将重写后的历史推送到远程仓库。
- 协调团队:通知所有成员重新克隆仓库,并清理本地副本。
- 建立预防机制:通过
.gitignore、pre-commit 钩子和 Git LFS 避免问题重现。
遵循上述步骤,你可以有效地为 Git 仓库“瘦身”,提升团队协作效率。
附录:常用命令速查表
| 任务 | 命令 |
|---|---|
| 查看仓库大小 | git count-objects -vH |
| 列出历史中大文件 | git rev-list --objects --all | grep "$(git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -10 | awk '{print$1}')" |
| 使用 BFG 删除大于 50M 的文件 | java -jar bfg.jar --strip-blobs-bigger-than 50M --no-blob-protection . |
| 使用 git-filter-repo 删除文件 | git filter-repo --path big_file.zip --invert-paths |
| 强制垃圾回收 | git reflog expire --expire=now --all && git gc --prune=now --aggressive |
| 重新设置远程并强制推送 | git remote add origin <url> && git push --force --all && git push --force --tags |

浙公网安备 33010602011771号