其实是因为我自己经常做这种事,导致必须要写个东西来警告一下自己(以及方便以后自己使用)
当意外地将一个大文件、二进制文件或者包含敏感信息(如密码、密钥)的文件提交到 Git 仓库后,仅仅在新的 commit 中删除它是不够的。因为它依然存在于 Git 的历史记录中,会持续占用仓库空间,或者带来安全风险。本指南将介绍如何从 Git 的历史记录中彻底抹除文件。
修改 Git 历史记录是一项具有破坏性的操作。它会改变仓库中许多 commit 的哈希值。在对共享仓库执行此操作前,请务必与所有协作者沟通,并先克隆一份出来做备份。
分析仓库
在删除之前,需要定位那些不需要的文件,尤其是占用空间大的文件。
纯 git 也是可以做到分析占用空间的,但需要使用很长的命令行,这是一个 powershell 用的版本。
git rev-list --objects --all | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | Where-Object { $_ -match '^blob ' } | ForEach-Object { $_ -replace '^blob ' } | Sort-Object { [int]($_.Split(' ')[1]) } | Select-Object -Last 10
macOS 或者 Git Bash
git rev-list --objects --all | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | sed -n 's/^blob //p' | sort --numeric-sort --key=2 | tail -n 10
一般安装完 git,都是有 lfs 的,使用 git lfs
命令可以直观的按类型列出占用空间。
git lfs migrate info --everything
git-sizer
是 GitHub 官方推出的一款用于分析 Git 仓库“健康状况”的工具。它能提供更全面的报告,但需要额外安装。
git-sizer
的安装需要从其 Releases 页面 下载对应系统的可执行文件,然后将其所在目录添加到系统的PATH
环境变量中。
安装后,进入你的仓库目录运行:
git-sizer --verbose
这个工具不仅仅是看文件大小,还会分析 blob 数量、树深度等,帮助你全面了解仓库的“臃肿”程度。
从历史记录中删除文件
我们将使用 git-filter-repo
这个现代化工具来重写历史记录。它是官方推荐的、替代 git filter-branch
和 BFG Repo-Cleaner 的新一代工具。
通过 pip 安装:
pip install git-filter-repo
如果你非常现代化的转向了 uv,可以使用:
uv tool install git-filter-repo
提示: git-filter-repo
为了安全起见,要求在一个全新的、干净的克隆仓库上进行操作。它会自动断开与远程仓库的连接,防止你意外推送错误的修改。
使用 git-filter-repo
删除指定文件一定不能忘记 --invert-paths
参数! 否则,仓库里除了这个文件,其它所有文件都将被删除。(点名批评 gemini-2.5-flash)
-
删除单个文件或文件夹:
git filter-repo --path path/to/your/example.jpg --invert-paths
-
删除指定类型或模式的文件:
git filter-repo --path-glob '*.jpg' --invert-paths
-
删除所有名字相同的文件:
git filter-repo --path '.DS_Store' --invert-paths
执行完毕后,git-filter-repo
会自动清理相关的 commit。
重新推送至远程仓库
现在需要将其强制推送到远程仓库。
-
为了防止未来再次误提交这类文件,最好先将它们添加到
.gitignore
文件中。 -
重新关联远程仓库并强制推送
git-filter-repo
会删除origin
配置,需要重新添加并使用强制镜像推送给远程,--mirror
是一个很强的操作,请确保你事先备份了。git remote add origin <your-repo-url> git push --force --mirror
同步所有协作者的本地仓库
这一步至关重要,也是整个操作代价最大的地方。
所有协作者(包括你自己其它设备上的)都不能再使用 git pull
来更新他们的本地仓库。因为本地历史和被重写的远程历史已经完全不同,必须从远程重新 git clone
一个全新的仓库。
因此,在执行历史清理前,务必三思并与项目的参与者充分协调。