Clone
1
[操作指南] 清理 Git 历史中的大文件 (减小 .git/pack 体积)
谭梦宁 edited this page 2026-05-06 19:36:04 +08:00
This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

📌 问题描述与现象

在项目开发过程中,如果不慎将大文件(如编译产物、二进制文件、设计图、数据包等)提交到了 Git 仓库中,即使后续在代码中删除了它们,这些文件仍然会留在 Git 的提交历史中。 这会导致本地 .git/objects/pack/ 目录下的 .pack 文件异常庞大(达到几百 MB 甚至更大),严重拖慢 git clonegit 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 记录:

  1. 进入 Gitea 仓库页面 -> 设置 -> 危险操作区 -> 删除此仓库。
  2. 在 Gitea 上重新创建同名的空仓库。
  3. 在本地执行:
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)。