实用脚本能自动清理Git大文件吗?

wen 实用脚本 2

本文目录导读:

实用脚本能自动清理Git大文件吗?

  1. 方案一:使用专业工具 git-filter-repo(最推荐,官方推荐)
  2. 方案二:查找大文件的脚本(配合手动操作)
  3. 方案三:使用 BFG Repo-Cleaner(较老但依然有效)
  4. 方案四:防止未来的大文件提交(Pre-receive Hook)
  5. 方案五:自动化清理脚本(完整版,带安全措施)
  6. 重要注意事项

这是一个非常实用的问题,答案是:可以,而且有非常成熟的脚本和工具来自动完成此操作。

不过需要先明确一个核心概念:Git 本身并不擅长管理大文件,一旦一个大的二进制文件被提交到 Git 历史中(哪怕后续删除了),它仍然会永久保存在 Git 的对象库(.git/objects)里,导致仓库体积臃肿。

以下介绍几种从专业工具手动脚本的解决方案,按推荐程度排序。

使用专业工具 git-filter-repo(最推荐,官方推荐)

这是 Git 官方文档推荐的、用于重写历史的工具,功能强大且速度快,它专门用来清理大文件或敏感信息。

安装

# macOS
brew install git-filter-repo
# Linux (Ubuntu/Debian)
sudo apt install git-filter-repo
# 或者通过 pip 安装
pip install git-filter-repo

核心脚本:查找并删除所有超过 10MB 的文件

#!/bin/bash
# 脚本名称: clean-git-large-files.sh
# 功能: 自动扫描历史中所有超过 10MB 的文件,并将其从 Git 历史中彻底删除
# --- 安全警告:这会永久重写历史!确保团队已备份并知晓 ---
TARGET_SIZE="10M"  # 可修改为 100M, 500M 等
# 1. 先分析仓库,查看大文件
echo "正在分析仓库中的大文件(超过 $TARGET_SIZE)..."
git filter-repo --analyze
# 2. 强制删除所有大于指定大小的文件
echo "正在从所有历史记录中删除大文件..."
git filter-repo --strip-blobs-bigger-than $TARGET_SIZE
# 3. 清理并回收空间
echo "正在清理并回收空间..."
git reflog expire --expire=now --all
git gc --prune=now
echo "完成!请检查仓库大小。"
echo "⚠️ 注意:需要 git push --force 到远程仓库。"

高级用法: 如果只想删除特定扩展名的大文件(如 .zip, .exe, .mp4):

git filter-repo --path-glob '*.zip' --path-glob '*.exe' --invert-paths

查找大文件的脚本(配合手动操作)

如果你不想用工具,或者需要先分析谁占用了空间,可以用这个纯 Bash 脚本。

查找大文件脚本(只读分析)

#!/bin/bash
# 脚本名称: find-large-git-files.sh
# 功能: 在 Git 历史中查找最大的 50 个文件
echo "正在扫描 Git 历史中的大文件..."
echo "这可能需要几分钟,取决于仓库大小..."
# 列出所有 blob 对象,按大小排序并取前 50
git rev-list --objects --all | \
  git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
  awk '/^blob/ {print $3, $4}' | \
  sort -rn | \
  head -50 | \
  awk '{printf "大小: %d KB, 文件名: %s\n", $1/1024, $2}'
echo "-----"
echo "注意:删除这些文件需要重写历史,建议使用 git filter-repo。"

配套操作(如果真想用纯 Git 命令手动操作):

# 1. 找到所有引用某大文件的提交
git log --all --full-history -- <大文件名>
# 2. 使用 git filter-branch (不推荐,很慢)
git filter-branch --force --index-filter \
  'git rm --cached --ignore-unmatch <大文件名>' \
  --prune-empty --tag-name-filter cat -- --all

使用 BFG Repo-Cleaner(较老但依然有效)

这是 JIRA 的 Atlassian 开发的 Java 工具,专门用于清理大文件。

下载 JAR 包

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

执行清理脚本

#!/bin/bash
# 脚本名称: clean-with-bfg.sh
# 删除所有大于 100MB 的文件
java -jar bfg-1.14.0.jar --strip-blobs-bigger-than 100M my-repo.git
# 删除某些特定的文件
java -jar bfg-1.14.0.jar --delete-files '*.zip' my-repo.git
# 清理
cd my-repo.git
git reflog expire --expire=now --all
git gc --prune=now

防止未来的大文件提交(Pre-receive Hook)

在服务端部署这个脚本,可以在根源上阻止大文件被推送。

服务端 Hook 脚本

#!/bin/bash
# 位置: .git/hooks/pre-receive (服务端)
# 设置最大文件大小 (100MB)
max_size=$((100 * 1024 * 1024))
while read oldrev newrev refname; do
    # 获取这次推送的所有对象
    for object in $(git rev-list --objects $oldrev..$newrev); do
        # 检查是否为 blob 对象
        if [[ $object == *"blob"* ]]; then
            size=$(git cat-file -s ${object#* })
            if [ $size -gt $max_size ]; then
                echo "❌ 拒绝推送: 文件 ${object##* } 大小为 $(($size/1024/1024))MB,超过限制 100MB"
                exit 1
            fi
        fi
    done
done

自动化清理脚本(完整版,带安全措施)

结合上述思路,写一个更友好的全自动脚本:

#!/bin/bash
# 脚本名称: git-cleaner.sh
# 作者: 自动化助手
set -e
# 配置
SIZE_LIMIT=${1:-"50M"}  # 可传参,默认 50M
BACKUP_BRANCH="backup-before-clean-$(date +%Y%m%d)"
echo "=== Git 大文件清理工具 ==="
echo "文件大小限制: $SIZE_LIMIT"
# 1. 安全检查
read -p "⚠️  此操作会重写 Git 历史,是否创建备份分支?(y/n): " answer
if [ "$answer" = "y" ]; then
    echo "创建备份分支: $BACKUP_BRANCH"
    git branch $BACKUP_BRANCH
    echo "✅ 备份分支已创建,如后悔可恢复:"
    echo "   git checkout $BACKUP_BRANCH"
fi
# 2. 检查工具
if ! command -v git-filter-repo &> /dev/null; then
    echo "❌ 未安装 git-filter-repo,请先安装:"
    echo "   brew install git-filter-repo (macOS)"
    echo "   pip install git-filter-repo"
    exit 1
fi
# 3. 执行清理
echo "正在删除大于 $SIZE_LIMIT 的文件..."
git filter-repo --strip-blobs-bigger-than $SIZE_LIMIT
# 4. 回收空间
echo "正在回收空间..."
git reflog expire --expire=now --all
git gc --prune=now
# 5. 检查结果
echo "=== 清理完成 ==="
echo "当前仓库大小:"
du -sh .git
echo ""
echo "🎉 清理完成!下一步操作:"
echo "1. 强制推送: git push --force --all"
echo "2. 强制推送标签: git push --force --tags"
echo "3. 通知团队成员重新 clone 或 git fetch --force"

重要注意事项

  1. 团队协作:重写历史会破坏大家的本地仓库,必须通知所有协作者,并让他们运行:

    git fetch --force
    git rebase origin/main
    # 或者直接重新 clone
  2. 备份:永远先备份整个仓库(cp -r 整个目录)再操作。

  3. 替代方案:对于真正的版本管理的大文件(如设计稿、模型、数据库备份),不要用 Git 管理,而改用 Git LFS

    git lfs track "*.psd"
    git lfs track "*.zip"
    git add .gitattributes
场景 推荐方案
一次性清理历史 git filter-repo --strip-blobs-bigger-than 50M
只分析查找 上面的 find-large-git-files.sh
预防未来 服务端 pre-receive hook
长期管理大文件 改用 Git LFS

最推荐的做法是:先用 git filter-repo 清理历史,然后马上配置 Git LFS 来管理未来的大文件。

抱歉,评论功能暂时关闭!