添加 [操作指南] 清理 Git 历史中的大文件 (减小 .git/pack 体积)

2026-05-06 19:36:04 +08:00
parent abb6439a12
commit 1086c2f0cf
@@ -0,0 +1,139 @@
### 📌 问题描述与现象
在项目开发过程中,如果不慎将大文件(如编译产物、二进制文件、设计图、数据包等)提交到了 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`
在执行清理的机器上,需要先安装该工具:
```bash
# 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
```
### 第二步:准备工作(备份与克隆)
强烈建议在一个全新的克隆副本中操作,避免污染当前工作区。
```bash
# 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 个文件,确认你要删除的目标:
```bash
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
```
*(输出结果的最后一列就是文件路径)*
### 第四步:从历史中彻底删除大文件
根据上一步找到的路径,执行删除操作。
```bash
# 删除单个文件
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 文件体积:
```bash
# 清除 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,或者你拥有强制推送权限:
```bash
# 因为历史变了,必须加 --force
git push origin --force --all
git push origin --force --tags
```
#### 方式 B:删除 Gitea 仓库并重建(最彻底)
如果这是一个新项目,不需要保留 Gitea 上的 Wiki、Issues、PR 记录:
1. 进入 Gitea 仓库页面 -> 设置 -> 危险操作区 -> 删除此仓库。
2. 在 Gitea 上重新创建同名的空仓库。
3. 在本地执行:
```bash
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
```
### 💣 终极捷径:不需要保留历史记录
如果这个项目根本不需要看以前的提交记录(比如只是个文档库或个人笔记),最快的方法是“清空历史,只保留当前代码”:
```bash
# 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 中严格限制大文件:
```gitignore
# 常见的大文件/编译产物
*.zip
*.tar.gz
*.rar
*.iso
*.exe
*.dll
node_modules/
dist/
build/
*.psd
*.ai
```
**进阶建议:** 如果是必须版本控制的大文件(如游戏资源、大型数据集),请使用 Git LFS (Large File Storage)。