From 1086c2f0cfa14eb3d624caa5d6143f51b625bef9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=AD=E6=A2=A6=E5=AE=81?= Date: Wed, 6 May 2026 19:36:04 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20[=E6=93=8D=E4=BD=9C?= =?UTF-8?q?=E6=8C=87=E5=8D=97]=20=E6=B8=85=E7=90=86=20Git=20=E5=8E=86?= =?UTF-8?q?=E5=8F=B2=E4=B8=AD=E7=9A=84=E5=A4=A7=E6=96=87=E4=BB=B6=20(?= =?UTF-8?q?=E5=87=8F=E5=B0=8F=20.git/pack=20=E4=BD=93=E7=A7=AF)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...B0%8F-.git%2Fpack-%E4%BD%93%E7%A7%AF%29.md | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 %5B%E6%93%8D%E4%BD%9C%E6%8C%87%E5%8D%97%5D-%E6%B8%85%E7%90%86-Git-%E5%8E%86%E5%8F%B2%E4%B8%AD%E7%9A%84%E5%A4%A7%E6%96%87%E4%BB%B6-%28%E5%87%8F%E5%B0%8F-.git%2Fpack-%E4%BD%93%E7%A7%AF%29.md diff --git a/%5B%E6%93%8D%E4%BD%9C%E6%8C%87%E5%8D%97%5D-%E6%B8%85%E7%90%86-Git-%E5%8E%86%E5%8F%B2%E4%B8%AD%E7%9A%84%E5%A4%A7%E6%96%87%E4%BB%B6-%28%E5%87%8F%E5%B0%8F-.git%2Fpack-%E4%BD%93%E7%A7%AF%29.md b/%5B%E6%93%8D%E4%BD%9C%E6%8C%87%E5%8D%97%5D-%E6%B8%85%E7%90%86-Git-%E5%8E%86%E5%8F%B2%E4%B8%AD%E7%9A%84%E5%A4%A7%E6%96%87%E4%BB%B6-%28%E5%87%8F%E5%B0%8F-.git%2Fpack-%E4%BD%93%E7%A7%AF%29.md new file mode 100644 index 0000000..541dfee --- /dev/null +++ b/%5B%E6%93%8D%E4%BD%9C%E6%8C%87%E5%8D%97%5D-%E6%B8%85%E7%90%86-Git-%E5%8E%86%E5%8F%B2%E4%B8%AD%E7%9A%84%E5%A4%A7%E6%96%87%E4%BB%B6-%28%E5%87%8F%E5%B0%8F-.git%2Fpack-%E4%BD%93%E7%A7%AF%29.md @@ -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)。 \ No newline at end of file