其实是因为我自己经常做这种事,导致必须要写个东西来警告一下自己(以及方便以后自己使用)

当意外地将一个大文件、二进制文件或者包含敏感信息(如密码、密钥)的文件提交到 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。

重新推送至远程仓库

现在需要将其强制推送到远程仓库。

  1. 为了防止未来再次误提交这类文件,最好先将它们添加到 .gitignore 文件中。

  2. 重新关联远程仓库并强制推送 git-filter-repo 会删除 origin 配置,需要重新添加并使用强制镜像推送给远程,--mirror 是一个很强的操作,请确保你事先备份了。

    git remote add origin <your-repo-url>
    git push --force --mirror
    

同步所有协作者的本地仓库

这一步至关重要,也是整个操作代价最大的地方。

所有协作者(包括你自己其它设备上的)都不能再使用 git pull 来更新他们的本地仓库。因为本地历史和被重写的远程历史已经完全不同,必须从远程重新 git clone 一个全新的仓库。

因此,在执行历史清理前,务必三思并与项目的参与者充分协调。