Git 大文件移除:彻底清理仓库历史,释放存储空间

前言

在 Git 项目开发中,有时会不小心将大文件(如视频、压缩包、数据库备份等)提交到仓库中。即使后续删除了这些文件,它们仍然会保留在 Git 的历史记录中,导致仓库体积臃肿,克隆和拉取速度变慢。本文将详细介绍如何从 Git 仓库中彻底移除大文件,并重写提交历史以释放存储空间。

移除大文件命令总结

# 1. 在powershell运行
git rev-list --objects --all | ForEach-Object { $p = $_ -split ' ', 2; if ($p[1]) { $s = [int](git cat-file -s $p[0] 2>$null); if ($s -gt 100MB) { "[$([math]::Round($s/1MB,1)) MB] $($p[1])" } } }

# 2. 输出 文件 例如
[367.5 MB] n-big-backup.json

# 3. 移除大文件
git filter-branch --force --index-filter "git rm --cached --ignore-unmatch n-big-backup.json" --prune-empty --tag-name-filter cat -- --all

# 4. 执行
git reflog expire --expire=now --all
git gc --prune=now --aggressive
git push -f origin master

具体可以查看以下内容🧑🏻‍🚀

1. 问题诊断:找出导致仓库臃肿的元凶

在动手清理之前,首先需要定位是哪些大文件或历史提交占用了大量空间。

1.1 查看仓库大小及对象统计

# 查看仓库的总体大小(包含所有对象)
git count-objects -vH

# 查看所有对象(blob、tree、commit)的详细统计
git gc --aggressive --prune=now
git count-objects -vH

1.2 找出最大的文件(按大小排序)

# 列出所有历史中的 blob 对象,按大小排序
git rev-list --objects --all | while read -r sha1 path; do
  size=$(git cat-file -s "$sha1" 2>/dev/null || echo 0)
  if [ "$size" -gt 0 ]; then
    echo "$size $sha1 $path"
  fi
done | sort -nr | head -20
或 注意 在 powershell
git rev-list --objects --all | ForEach-Object { $p = $_ -split ' ', 2; if ($p[1]) { $s = [int](git cat-file -s $p[0] 2>$null); if ($s -gt 100MB) { "[$([math]::Round($s/1MB,1)) MB] $($p[1])" } } }

2. 解决方案:彻底移除大文件

找到目标文件后,需要从所有分支的所有提交历史中将其删除。请注意,这会重写历史,所有协作者都需要重新克隆。

2.1 方法一:使用 git filter-branch(原生但较慢)

git filter-branch 是 Git 自带的命令,但速度较慢,适用于小型仓库或简单删除。

# 删除指定文件(例如 big_video.mp4)
git filter-branch --force --index-filter \
  "git rm --cached --ignore-unmatch path/to/big_v.mp4" \
  --prune-empty --tag-name-filter cat -- --all

# 删除所有 .zip 文件
git filter-branch --force --index-filter \
  "git rm --cached --ignore-unmatch '*.zip'" \
  --prune-empty --tag-name-filter cat -- --all

参数说明:

  • --force:强制覆盖备份。
  • --index-filter:直接修改暂存区,比 --tree-filter 快得多。
  • --prune-empty:删除因此操作而产生的空提交。
  • --tag-name-filter cat:保留标签名称不变(但指向新的提交)。
  • -- --all:对所有分支和标签进行操作。

2.2 方法二:使用 BFG Repo-Cleaner(快速、简单)

BFG 专门为删除大文件而设计,速度比 filter-branch 快几个数量级。

# 1. 克隆一个裸仓库(建议在清理前备份原仓库)
git clone --mirror git@example.com:your-repo.git

# 2. 运行 BFG 删除所有大于 100MB 的文件
java -jar bfg.jar --strip-blobs-bigger-than 100M your-repo.git

# 3. 删除指定文件(按文件名)
java -jar bfg.jar --delete-files big_video.mp4 your-repo.git

# 4. 删除指定文件夹下的所有文件
java -jar bfg.jar --delete-folders temp_bin your-repo.git

# 5. 进入裸仓库目录,执行 GC 清理
cd your-repo.git
git reflog expire --expire=now --all
git gc --prune=now --aggressive

# 6. 推送重写后的历史到远程(强制推送)
git push --force

2.3 方法三:使用 git-filter-repo(现代、功能强大)

git-filter-repo 是 Git 官方推荐替代 filter-branch 的工具,功能更强大且安全。

# 安装(Python 环境)
pip install git-filter-repo

# 1. 克隆或备份原仓库
git clone git@example.com:your-repo.git
cd your-repo

# 2. 删除指定文件
git filter-repo --path path/to/big_file.zip --invert-paths

# 3. 使用表达式删除一类文件
git filter-repo --path-glob '*.tar.gz' --invert-paths

# 4. 根据文件大小过滤(需结合分析报告手动编写脚本,或使用 --path-match 等)
# 5. 完成后,强制推送到远程
git remote add origin git@example.com:your-repo.git
git push --force --all
git push --force --tags

3. 清理本地仓库与重新克隆

历史重写后,本地仓库的旧对象依然存在,需要彻底清理。

3.1 强制垃圾回收

# 删除所有未被引用的对象(包括重写前的旧对象)
git reflog expire --expire=now --all
git gc --prune=now --aggressive

3.2 重新克隆(最彻底)

对于协作者来说,最简单安全的方式是删除本地仓库并重新克隆。

cd ..
rm -rf your-repo
git clone git@example.com:your-repo.git

4. 验证清理效果

清理完成后,确认仓库体积已减小。

# 查看当前仓库大小
git count-objects -vH

# 查看 .git/objects 目录大小
du -sh .git/objects

# 确认目标文件已不在历史中
git log --all --oneline -- path/to/big_file.zip

5. 预防措施

为了避免再次误提交大文件,可以采取以下预防措施:

  1. 使用 .gitignore:将临时文件、构建产物、媒体文件等加入 .gitignore。
  2. 设置 Git 钩子(pre-commit):在提交前检查文件大小。
    # .git/hooks/pre-commit 示例
    MAX_FILE_SIZE=10485760 # 10MB
    for file in $(git diff --cached --name-only); do
      size=$(git show :"$file" | wc -c)
      if [ "$size" -gt "$MAX_FILE_SIZE" ]; then
        echo "错误:文件 $file 超过 $MAX_FILE_SIZE 字节。"
        exit 1
      fi
    done
    
  3. 使用 Git LFS(大文件存储):对于必须版本控制的二进制大文件(如图片、模型),使用 Git LFS 管理。
  4. 定期清理:将仓库维护(包括清理历史)纳入日常开发流程。

6. 总结

彻底移除 Git 历史中的大文件需要重写提交历史,这是一个破坏性操作。务必在操作前完整备份仓库,并通知所有协作者。流程可总结为:

  1. 诊断:使用 git rev-list、git filter-repo --analyze 或 BFG 找出大文件。
  2. 选择工具:根据仓库大小和复杂度,选择 git filter-branch、BFG 或 git-filter-repo。
  3. 执行清理:运行工具命令,从所有分支和标签中删除目标文件。
  4. 强制推送:将重写后的历史推送到远程仓库。
  5. 协调团队:通知所有成员重新克隆仓库,并清理本地副本。
  6. 建立预防机制:通过 .gitignore、pre-commit 钩子和 Git LFS 避免问题重现。

遵循上述步骤,你可以有效地为 Git 仓库“瘦身”,提升团队协作效率。

附录:常用命令速查表

任务 命令
查看仓库大小 git count-objects -vH
列出历史中大文件 git rev-list --objects --all | grep "$(git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -10 | awk '{print$1}')"
使用 BFG 删除大于 50M 的文件 java -jar bfg.jar --strip-blobs-bigger-than 50M --no-blob-protection .
使用 git-filter-repo 删除文件 git filter-repo --path big_file.zip --invert-paths
强制垃圾回收 git reflog expire --expire=now --all && git gc --prune=now --aggressive
重新设置远程并强制推送 git remote add origin <url> && git push --force --all && git push --force --tags
posted @ 2026-10-23 13:58  木得是  阅读(3)  评论(0)    收藏  举报
//雪花飘落效果