1. 概述

在本篇文章中,我们将学习如何使用多种工具从 Git 仓库的提交历史中删除大文件。这在项目维护过程中非常常见,尤其是当某些大文件(如日志、数据库导出、临时文件等)被误提交后,不仅会拖慢克隆速度,还会占用不必要的存储空间。

2. 使用 git filter-branch

这是最常用的方法之一,可以用来重写提交历史,删除指定文件。

例如,我们不小心提交了一个名为 blob.txt 的大文件,即使后续删除了它,在 Git 历史中依然存在:

$ git log --graph --full-history --all --pretty=format:"%h%x09%d%x20%s"
* 9e87646        (HEAD -> master) blob file removed
* 2583677        blob file
* 34ea256        my first commit

我们可以使用以下命令删除该文件的历史记录:

$ git filter-branch --tree-filter 'rm -f blob.txt' HEAD

--tree-filter 表示对工作树进行操作,rm -f 强制删除文件。
⚠️ 如果文件在某些提交中不存在,不加 -f 可能会导致命令失败。

执行后,Git 会生成一个新的提交历史,但原始历史仍保留在 refs/original/ 中:

* 8f39d86        (HEAD -> master) blob file removed
* e99a81d        blob file
| * 9e87646      (refs/original/refs/heads/master) blob file removed
| * 2583677      blob file
|/  
* 34ea256        my first commit

为了彻底清除这些残留信息,我们可以运行以下命令:

$ git update-ref -d refs/original/refs/heads/master
$ git reflog expire --expire=now --all
$ git gc --prune=now

✅ 这些命令分别用于删除原始引用、清理 reflog、以及压缩仓库。
⚠️ 执行前请确保已经备份,避免误删重要数据。

最终,我们的提交历史将不再包含该文件:

* 6f49d86        (HEAD -> master) my first commit

优化方式:使用 --index-filter

如果只想删除文件但不操作工作目录,可以使用 --index-filter,速度更快:

$ git filter-branch --index-filter 'git rm --cached --ignore-unmatched blob.txt' HEAD

--cached 只操作索引,不修改文件系统。
--ignore-unmatched 避免因文件不存在而报错。

⚠️ 该方法适用于删除大文件,但如果文件多次提交,效率仍然不高。

3. 使用 git-filter-repo

这是官方推荐的替代 git filter-branch 的工具,速度快、功能丰富,推荐使用。

3.1 安装

要求:

  • Python 3 >= 3.5
  • Git >= 2.22.0(部分功能需要 2.24.0 或更高)

Linux 安装方式如下:

$ sudo apt install python3-pip
$ pip install --user git-filter-repo

或者手动下载安装:

export PATH="${HOME}/bin:${PATH}"
mkdir -p ~/bin
wget -O ~/bin/git-filter-repo https://raw.githubusercontent.com/newren/git-filter-repo/7b3e714b94a6e5b9f478cb981c7f560ef3f36506/git-filter-repo
chmod +x ~/bin/git-filter-repo

3.2 删除文件

先查看当前提交历史:

$ git log --graph --full-history --all --pretty=format:"%h%x09%d%x20%s"
* ee36517        (HEAD -> master) blob.txt removed
* a480073        project folder

分析仓库内容:

$ git filter-repo --analyze
Processed 5 blob sizes
Processed 2 commits
Writing reports to .git/filter-repo/analysis...done.

然后执行删除命令:

$ git filter-repo --force --invert-paths --path-match blob.txt

--invert-paths 表示删除匹配路径的文件。
--path-match 指定要删除的文件名。
⚠️ 执行后所有相关提交的哈希值都会改变。

最终提交历史如下:

* 8940776        (HEAD -> master) project folder

4. 使用 BRG Repo-Cleaner

这是一个基于 Java 的工具,适合删除大文件、敏感信息等。

✅ 优点:速度快,操作简单。
✅ 适合批量处理大文件。

例如,删除所有大于 200MB 的 blob 文件:

$ java -jar bfg.jar --strip-blob-bigger-than 200M my-repo.git

然后清理仓库:

$ git gc --prune=now --aggressive

⚠️ 使用前建议备份仓库,防止误删。

5. 使用 git rebase

适用于删除最近几次提交中的文件。

查看提交历史:

$ git log --graph --full-history --all --pretty=format:"%h%x09%d%x20%s"
* 535f7ea        (HEAD -> master) blob file removed
* 8bffdfa        blob file
* 5bac30b        index.html

假设我们要删除 blob file 这个提交,可以使用如下命令进入交互式 rebase:

$ git rebase -i 5bac30b

在编辑器中删除对应行:

pick 535f7ea blob file removed
drop 8bffdfa blob file 

保存退出后继续 rebase:

$ git rebase --continue

最终提交历史如下:

* 5bac30b        (HEAD -> master) index.html

✅ 适用于少量提交的调整。
❌ 不适合处理大量提交,效率较低。

6. 总结

方法 优点 缺点
git filter-branch 传统方法,兼容性好 慢,维护性差
git-filter-repo ✅ 官方推荐,速度快,功能强 需要安装,依赖 Python
BFG Repo-Cleaner 快速,适合批量处理 依赖 Java
git rebase 简单,适合少量提交 不适合处理大量历史记录

✅ 推荐优先使用 git-filter-repo,它在性能和易用性方面都优于其他方法。
⚠️ 操作前务必做好备份,避免误删导致数据丢失。


原始标题:Remove a Large File from Commit History in Git