本文主要记录误将本地数据库等大文件纳入 Git 版本库后的处理:用 git rm --cached 停止追踪并写入 .gitignore;若需从历史中剔除则使用 filter-branch(或 git-filter-repo)并 force push;以及 Gitea 上仓库体积暂增时的 GC 说明与协作/CI 注意点。

示例文件名来自某项目误提交的 SQLite 文件:app.dbapp.db-shmapp.db-wal(原文为具体业务库名,此处泛化)。改写历史会变更 commit id,需团队与 CI 配合,操作前备份仓库。

背景说明

目标通常有两层:

  1. 工作区保留文件,但之后不再提交(--cached + .gitignore)。
  2. 历史中也不再保留大文件,以缩小 .git(filter + gc + force push)。

只做第 1 步时,旧 blob 仍在历史里,clone 体积不会明显下降。

停止追踪并忽略(保留本地文件)

在仓库根目录:

git rm --cached app.db app.db-shm app.db-wal

--cached 只从索引移除,不删工作区文件。

写入 .gitignore

# 本地数据库(勿提交)
app.db
app.db-shm
app.db-wal
# 或:app.db*

提交:

git add .gitignore
git commit -m "chore: stop tracking local database files"

之后 git status 不应再把这些文件列为未跟踪待添加(已被 ignore)。git push 后,远端当前树不再包含它们;历史提交里若曾经加入,blob 仍在,需下一节处理。

从历史中剔除(高风险)

风险

  • 重写所有相关 commit,改变 hash
  • 协作者基于旧历史的分支会分叉;错误 pull/push 可能把大文件带回来。
  • 必须 force push 才能更新远端默认分支。
  • 操作前复制整个项目目录或确认远端可回滚。

使用 filter-branch(无额外依赖)

git filter-branch --force --index-filter \
  "git rm --cached --ignore-unmatch app.db app.db-shm app.db-wal" \
  --prune-empty --tag-name-filter cat -- --all

清理 filter-branch 备份引用并 GC:

rm -rf .git/refs/original/
# Windows PowerShell: Remove-Item -Recurse -Force .git\refs\original\

git reflog expire --expire=now --all
git gc --prune=now --aggressive

再推远端(确认团队已知晓):

git push origin --force --all
git push origin --force --tags

更现代、通常更快的工具是 git-filter-repo,语法不同,生产团队可优先评估。

为何 Gitea 显示体积反而变大

force push 之后,服务端往往同时保留新历史与尚未回收的旧对象,体积可能短期上升(例如数 MB 涨到接近两倍)。本地 gc 不会自动等于服务端已 gc

处理方式:

  1. 站点管理中对仓库执行 Garbage Collection(路径因 Gitea 版本而异,常见在管理员配置页)。
  2. 等待 Gitea 定时 GC cron。
  3. 有服务器权限时,在对应 bare 仓库目录执行 git reflog expire + git gc(路径形如 <gitea-data>/gitea-repositories/<owner>/<repo>.git,以实际部署为准)。

GC 完成后体积应回落。

协作方如何同步

不要在未沟通时对已重写分支普通 git pull 再推。

可选:

A. 无本地未推送改动:删目录重 clone

git clone <repo-url>

B. 有本地修改:stash 后硬对齐远端

git stash push -u -m "before history rewrite sync"
git fetch origin
git reset --hard origin/<branch>
git stash pop

冲突时暂停推送,人工解决后再提交。

CI / Docker 注意

  • Runner 缓存的旧 workspace 可能仍基于旧历史:清理 Runner 缓存,或临时将策略改为全量 clone 跑通一次。
  • Docker 若 COPY 过含大文件的上下文,旧镜像层仍占空间:docker build --no-cache,必要时 docker image prune
  • 应用若依赖仓库内 db 文件:取消追踪后,容器启动需自动初始化库或挂载独立数据卷,避免“镜像/仓库里没有 db 就起不来”。

验证结果

步骤判断
rm –cached工作区文件仍在;git ls-files 不再列出它们
gitignore修改 db 不再出现在 git status
filter + gc本地 .git 体积下降;git log -- all -- app.db 无历史路径(或符合预期)
远端force push 成功;GC 后 Gitea 仓库体积回落

注意事项

  • 密码、密钥、生产库文件一旦进过 Git,即使后来删除,仍视为可能泄露,需轮换凭据。
  • --force 推送受保护分支时需管理员权限;优先走团队约定流程。
  • 本文不鼓励在共享主干上频繁 rewrite;能只 untrack 就不必改历史。

参考资料