📌 问题描述与现象
在项目开发过程中,如果不慎将大文件(如编译产物、二进制文件、设计图、数据包等)提交到了 Git 仓库中,即使后续在代码中删除了它们,这些文件仍然会留在 Git 的提交历史中。
这会导致本地 .git/objects/pack/ 目录下的 .pack 文件异常庞大(达到几百 MB 甚至更大),严重拖慢 git clone、git pull 的速度。
⚠️ 注意事项(必读)
-
破坏性操作: 重写历史会改变所有的 Commit Hash。
-
团队协作: 操作完成后,所有团队成员必须丢弃本地旧仓库,重新 Clone。如果有人基于旧历史提交代码并强推,大文件会被“复活”。
-
切勿手动删除: 绝对不要直接手动删除
.git/pack/下的文件,这会导致仓库直接损坏。
🔧 解决方案:使用 git-filter-repo 重写历史
git-filter-repo 是目前官方推荐的重写历史工具,速度快且安全(替代了已废弃的 git filter-branch)。
第一步:安装 git-filter-repo
在执行清理的机器上,需要先安装该工具:
# Ubuntu / Debian
sudo apt install git-filter-repo
# CentOS / RHEL (需要 EPEL 源)
sudo yum install epel-release
sudo yum install git-filter-repo
# macOS
brew install git-filter-repo
# Windows (通过 Python)
pip install git-filter-repo
第二步:准备工作(备份与克隆)
强烈建议在一个全新的克隆副本中操作,避免污染当前工作区。
# 1. 获取仓库的 Gitea 克隆地址(例如:https://gitea.your-domain.com/org/repo.git)
# 2. 重新克隆一份干净的代码
git clone https://gitea.your-domain.com/org/repo.git repo-clean
cd repo-clean
第三步:找出历史中的大文件(诊断)
运行以下命令,列出历史记录中体积最大的 20 个文件,确认你要删除的目标:
git rev-list --objects --all \
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
| awk '/^blob/ {print substr($0,6)}' \
| sort --numeric-sort --key=2 \
| tail -n 20
(输出结果的最后一列就是文件路径)
第四步:从历史中彻底删除大文件
根据上一步找到的路径,执行删除操作。
# 删除单个文件
git filter-repo --invert-paths --path "path/to/large-file.zip"
# 删除整个目录(例如 node_modules 或 dist)
git filter-repo --invert-paths --path "build/"
# 按照后缀名批量删除(例如所有 .psd 文件)
git filter-repo --invert-paths --path-glob "*.psd"
参数说明:--invert-paths 表示“排除/删除”指定的路径。如果不加这个参数,效果会变成“只保留”指定的路径(会把其他代码全删了,千万别漏加!)。
第五步:清理本地垃圾对象
虽然历史被重写了,但大文件的数据还残留在 .git 的缓存里。需要执行垃圾回收(GC)才能真正减小 pack 文件体积:
# 清除 filter-repo 留下的备份引用
rm -rf .git/filter-repo
# 强制过期所有 reflog,并立即执行垃圾回收与重新打包
git reflog expire --expire=now --all
git gc --prune=now --aggressive
执行完后,可以运行 du -sh .git 检查体积是否已经恢复正常。
🚀 第六步:推送到 Gitea 远端
根据实际情况,选择以下一种方式同步到 Gitea:
方式 A:强制推送覆盖原仓库(推荐,保留 Issues 等)
如果该仓库在 Gitea 上没有过多的 Fork,或者你拥有强制推送权限:
# 因为历史变了,必须加 --force
git push origin --force --all
git push origin --force --tags
方式 B:删除 Gitea 仓库并重建(最彻底)
如果这是一个新项目,不需要保留 Gitea 上的 Wiki、Issues、PR 记录:
- 进入 Gitea 仓库页面 -> 设置 -> 危险操作区 -> 删除此仓库。
- 在 Gitea 上重新创建同名的空仓库。
- 在本地执行:
git remote remove origin
git remote add origin https://gitea.your-domain.com/org/repo.git
git push -u origin --all
git push origin --tags
💣 终极捷径:不需要保留历史记录
如果这个项目根本不需要看以前的提交记录(比如只是个文档库或个人笔记),最快的方法是“清空历史,只保留当前代码”:
# 1. 在当前代码目录下,创建一个没有任何历史的孤立分支
git checkout --orphan latest_branch
# 2. 把当前所有文件加入暂存区
git add -A
# 3. 提交一次
git commit -m "初始化提交:清理历史大文件"
# 4. 删除原来的主分支,并把当前分支改名
git branch -D main # 或者 master,看你的默认分支名
git branch -m main
# 5. 强推到 Gitea
git push origin --force --all
🛡️ 预防措施(防患于未然)
为了避免下次再出现这种情况,请在项目根目录的 .gitignore 中严格限制大文件:
# 常见的大文件/编译产物
*.zip
*.tar.gz
*.rar
*.iso
*.exe
*.dll
node_modules/
dist/
build/
*.psd
*.ai
进阶建议: 如果是必须版本控制的大文件(如游戏资源、大型数据集),请使用 Git LFS (Large File Storage)。