Featured image of post 使用 BFG Repo Cleaner 从 Git 历史中彻底删除大文件

使用 BFG Repo Cleaner 从 Git 历史中彻底删除大文件

介绍如何使用 BFG Repo Cleaner 这个强大的工具来清理 Git 仓库历史中的大文件和敏感信息

前言

在使用 Git 进行版本控制时,我们可能会不小心将一些大文件(如编译产物、数据集、安装包等)提交到仓库中。即使后来删除了这些文件,它们仍然存在于 Git 的历史记录中,导致仓库体积臃肿,克隆和拉取变得缓慢。

本文将介绍 BFG Repo Cleaner —— 一个比 git-filter-branch 更简单、更快的工具,用于清理 Git 仓库历史中的大文件和敏感信息。

什么是 BFG Repo Cleaner?

BFG Repo Cleaner 是一个专门用于清理 Git 仓库历史的工具,它可以:

  • 删除大文件:移除历史记录中超过指定大小的文件
  • 删除敏感信息:移除密码、凭证等私人数据
  • 删除特定文件:按文件名模式删除文件或文件夹

BFG vs git-filter-branch

相比 Git 原生的 git-filter-branch 命令,BFG 有以下优势:

特性 BFG Repo Cleaner git-filter-branch
速度 10-720 倍更快 较慢
易用性 专注特定任务,简单直观 功能强大但复杂
性能 多核并行处理 单线程处理
语言 Scala + JVM Bash + C

根据实际用户反馈:

  • Elliot Glaysher(Google Chrome 工程师):BFG 在 10 分钟内将仓库缩减到 ~500MB,而他手工脚本需要 3 天才能处理到 615MB
  • Jason Frey(Red Hat 工程师):对 BFG 的性能感到惊叹

安装 BFG

前置要求

BFG 需要 Java 11 或更高版本(BFG v1.14.0 是最后一个支持 Java 8 的版本)。

下载安装

  1. GitHub Releases 下载最新版本的 JAR 文件
  2. 下载后得到类似 bfg-1.14.0.jar 的文件
    # 创建一个便捷的别名(可选)
alias bfg='java -jar /path/to/bfg-1.14.0.jar'

# 或者将 bfg.jar 移动到 PATH 中
sudo mv bfg-1.14.0.jar /usr/local/bin/bfg.jar
alias bfg='java -jar /usr/local/bin/bfg.jar'
  

使用步骤

⚠️ 重要提醒

在开始之前,请务必备份你的仓库! 重写 Git 历史是不可逆的操作。

基本流程

1. 镜像克隆仓库

使用 --mirror 标志克隆一个完整的仓库副本:

    git clone --mirror git://example.com/some-big-repo.git
  

这会创建一个裸仓库(bare repo),虽然看不到正常文件,但包含了完整的 Git 数据库。

务必在此阶段备份整个文件夹!

2. 运行 BFG 清理

    # 删除所有大于 100M 的文件
bfg --strip-blobs-bigger-than 100M some-big-repo.git

# 删除特定文件
bfg --delete-files id_{dsa,rsa} my-repo.git

# 替换敏感文本(需要先创建一个包含敏感词的文件)
bfg --replace-text passwords.txt my-repo.git
  

BFG 会更新所有提交、分支和标签,但不会物理删除数据。

3. 清理 Git 数据库

进入仓库目录,使用 git gc 清理垃圾数据:

    cd some-big-repo.git
git reflog expire --expire=now --all && git gc --prune=now --aggressive
  

4. 推送更改

    git push
  

注意:由于使用了 --mirror 标志,这会更新远程服务器上的所有引用。

5. 团队协作

告诉所有协作者:

  • 删除旧的本地克隆
  • 重新克隆清理后的仓库

如果保留旧克隆并推送,可能会将脏历史重新引入!

常用示例

示例 1:删除超过 50MB 的大文件

    # 删除所有大于 50MB 的文件
bfg --strip-blobs-bigger-than 50M my-repo.git
  

示例 2:删除特定文件

    # 删除所有名为 id_rsa 或 id_dsa 的文件
bfg --delete-files id_{dsa,rsa} my-repo.git

# 删除 .git 文件夹/文件(从其他版本控制系统迁移时常见)
bfg --delete-folders .git --delete-files .git --no-blob-protection my-repo.git
  

示例 3:删除最大的 100 个文件

    # 删除仓库中最大的 100 个文件
bfg --strip-biggest-blobs 100 my-repo.git
  

示例 4:替换敏感文本

创建 passwords.txt 文件,每行一个要替换的内容:

    password=secret123
regex:api[_-]key=\w+
glob:*config.yml
  

然后运行:

    bfg --replace-text passwords.txt my-repo.git
  

BFG 会将这些内容替换为 ***REMOVED***

示例 5:组合使用

    # 同时删除大文件和敏感文本
bfg --strip-blobs-bigger-than 100M --replace-text banned.txt repo.git
  

重要特性说明

当前提交保护机制

默认情况下,BFG 不会修改你最新提交的内容(通常是 masterHEAD 分支)。

这是一个保护机制,因为:

  • 最新提交通常是部署到生产环境的代码
  • 简单删除可能导致代码损坏(例如删除了仍在使用的密码或大文件)
  • 你需要先手动清理当前提交,BFG 再处理历史提交

如何禁用保护(不推荐)

    bfg --strip-biggest-blobs 100 --no-blob-protection repo.git
  

清理原则

  • 如果"坏东西"(如超过大小限制的文件)在受保护的提交中,它不会被删除
  • 即使文件在受保护提交中未被修改,这些提交的 ID 也会改变(因为历史改变了)
  • 如果想让 BFG 删除某些内容,确保当前提交是干净的

性能对比

根据 BFG 官方文档和用户反馈:

场景 git-filter-branch BFG Repo Cleaner 性能提升
大型仓库清理 3 天 10 分钟 432x
标准清理任务 过夜作业 < 10 分钟 10-720x

BFG 的性能优势来自:

  1. 智能处理:不需要检查每个提交的完整文件层次结构
  2. 多核并行:利用 Scala 和 JVM 的并行处理能力
  3. 单进程:不需要频繁的 fork 和 exec 操作

实战案例

假设你的仓库不小心提交了一个 200MB 的数据集文件 dataset.csv

    # 1. 镜像克隆
git clone --mirror https://github.com/username/repo.git
cd repo.git

# 2. 运行 BFG 删除大文件
bfg --strip-blobs-bigger-than 50M repo.git

# 查看清理报告
# BFG 会显示类似:
# Scanning git repo... found 1 dirty commits
# Cleaning commits:   100% done
# Found 1 dirty blobs (that needed cleaning)

# 3. 清理 Git 数据库
git reflog expire --expire=now --all && git gc --prune=now --aggressive

# 4. 推送
git push

# 输出可能显示:
# To https://github.com/username/repo.git
#  + abc1234..def5678 master -> master (forced update)
  

注意事项

⚠️ 强制推送会改变历史

清理后需要强制推送,这会改写 Git 历史。确保:

  • 通知所有协作者
  • 他们需要重新克隆仓库
  • 备份重要数据

⚠️ 检查依赖

如果仓库被其他项目引用为 submodule 或依赖,清理后需要更新引用。

⚠️ CI/CD 配置

检查 CI/CD 系统是否依赖特定的提交 SHA,清理后可能需要更新配置。

替代方案

git-filter-branch

虽然 BFG 更快更简单,但 git-filter-branch 功能更强大,可以处理更复杂的场景:

    # 删除特定文件的所有历史记录
git filter-branch --force --index-filter \
  "git rm --cached --ignore-unmatch path/to/file" \
  --prune-empty --tag-name-filter cat -- --all
  

git filter-repo(推荐)

git filter-repo 是新一代的 Git 历史重写工具,Python 编写,性能优异且更安全:

    pip install git-filter-repo

# 删除大于 100M 的文件
git filter-repo --strip-blobs-bigger-than 100M
  

总结

BFG Repo Cleaner 是清理 Git 仓库历史的优秀工具:

速度快:比 git-filter-branch 快 10-720 倍 ✅ 简单易用:专注特定任务,命令直观 ✅ 安全可靠:保护当前提交,有备份机制 ✅ 功能全面:支持删除大文件、敏感信息和特定文件

推荐工作流程

  1. 定期检查仓库大小
  2. 发现大文件后立即使用 BFG 清理
  3. 通知团队重新克隆
  4. .gitignore 中添加防止误提交

参考资源


Happy Coding! 🚀

A winner is just a loser who tried one more time.
Robust AI
使用 Hugo 构建
主题 StackJimmy 设计