Git 仓库文件太大怎么瘦身清理历史记录

FreeGuideOnline 最新 2026-07-04

bash du -sh .git


这会显示 `.git` 目录的大小。如果这个数字远大于项目源码的体积,说明历史记录中有冗余数据。

### 找出历史中的大文件

**方法一:使用 `git rev-list` 遍历所有对象**

```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 20

这条命令会列出仓库中最大的 20 个 blob(文件)。输出示例:

...
f7742... 49807360 some-large-video.mp4
a1b2c... 51200000 old-database.sql.gz

方法二:使用脚本 git-find-big-files

如果你习惯开箱即用的脚本,可以安装 git-extras 工具包:

# macOS
brew install git-extras
# Debian/Ubuntu
sudo apt install git-extras

git big-file

git big-file 会直接展示历史上最大的文件。

查看具体文件在哪些提交中出现

得到可疑文件名后,用 git log 追踪它的足迹:

git log --all --pretty=format:"%H %s" -- path/to/large-file

记录下来,之后在重写历史时可以用作过滤依据。

方法一:用 BFG Repo-Cleaner 快速清除(推荐新手)

BFG 是专门为快速清理 Git 历史而设计的工具,比原生 git filter-branch 快很多,并且用法简单。

安装 BFG

下载 bfg.jar (确保 Java 已安装)。

wget https://repo1.maven.org/maven2/com/madgag/bfg/1.14.0/bfg-1.14.0.jar

清理步骤

  1. 备份仓库
    重写历史是破坏性操作,务必先备份或确认远程仓库有分支保护。

  2. 克隆一份裸仓库
    BFG 需要在裸仓库上运行:

    git clone --mirror your-repo-url.git repo-mirror.git
    cd repo-mirror.git
    
  3. 删除大于 100M 的所有文件

    java -jar ~/bfg-1.14.0.jar --strip-blobs-bigger-than 100M .
    
  4. 删除特定文件类型或文件

    # 删除所有 *.zip 文件
    java -jar bfg.jar --delete-files '*.zip' .
    
    # 精确删除某个路径下的文件
    java -jar bfg.jar --delete-files path/to/hugefile.iso .
    
  5. 清理无效数据并推送

    git reflog expire --expire=now --all && git gc --prune=now --aggressive
    git push --force
    

BFG 默认会保护当前 HEAD 所在分支的最新提交,即最新的文件不会被删除,除非使用 --no-blob-protection

方法二:使用 git filter-repo(官方推荐的重写工具)

从 Git 2.22 开始,官方建议使用 git filter-repo 替代 filter-branch。它更快、更安全,但需要通过包管理器或 pip 安装。

安装

# macOS
brew install git-filter-repo
# Linux (通过 pip)
pip install git-filter-repo
# 或者从源码安装
git clone https://github.com/newren/git-filter-repo.git
cd git-filter-repo
make install

使用场景

删除所有 *.tar.gz 文件

git filter-repo --path-glob '*.tar.gz' --invert-paths

--invert-paths 表示选择不匹配该路径的文件,即排除匹配的部分,只保留其他文件。

仅保留指定目录

如果想只保留 src/ 目录,其他全部删除:

git filter-repo --path src/

按大小批量删除

可以先列出超过 50MB 的文件路径,再传给 filter-repo

# 1. 生成大文件列表
git rev-list --objects --all | \
  git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
  awk '/^blob/ {if ($3>52428800) print $4}' > large-files.txt

# 2. 滤除这些文件
git filter-repo --invert-paths --paths-from-file large-files.txt

操作完成后同样需要执行垃圾回收并强制推送。

方法三:基于 git filter-branch 的原生方案(不推荐但可用)

如果你无法安装第三方工具,可以使用内置的 filter-branch。但请注意,此方法已不推荐使用,且执行速度很慢。

删除某个特定文件

git filter-branch --force --index-filter \
  'git rm --cached --ignore-unmatch path/to/bigfile.bin' \
  --prune-empty --tag-name-filter cat -- --all

删除所有超过某个大小的文件

这需要配合 find 命令判断大小:

git filter-branch --force --index-filter \
  'git rm --cached --ignore-unmatch $(git rev-list --objects --all | \
    git cat-file --batch-check="%(objecttype) %(objectname) %(objectsize) %(rest)" | \
    awk "/^blob/ {if (\$3>104857600) print \$4}")' \
  --prune-empty --tag-name-filter cat -- --all

执行后同样清理:

rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --aggressive --prune=now

清理后的必要步骤

无论使用哪种方法,重写历史后都需要强制推送所有分支和标签,并压缩仓库。

1. 清理本地冗余数据

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

2. 强制推送

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