Git 大文件导致上传失败的终极解决方案

问题根源分析

当 Git 推送失败并出现 error: RPC failed; HTTP 500fatal: the remote end hung up unexpectedly 错误时,通常是由于以下原因:

  1. 历史提交中包含大文件(>100MB)
  2. 当前提交包含大文件
  3. 大文件已被删除但历史记录仍保留
  4. 网络不稳定导致大文件传输中断

解决方案全景图

Git 大文件导致上传失败的终极解决方案_第1张图片

场景一:不需要保留大文件

彻底清除历史中的大文件(推荐方案)

步骤 1:安装必要工具
  1. 安装 Python

    • 下载并安装 Python 3.x(建议选择最新版本,如3.11)。

    • 安装时勾选 “Add Python to PATH”

    • 验证安装:

      python --version
      
  2. 安装 git filter-repo

    • 打开命令提示符(CMD)或 PowerShell,运行:

      pip install git-filter-repo
      
    • 验证安装:

      git filter-repo --version
      
步骤 2:定位大文件
git rev-list --objects --all \
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
| awk '/^blob/ { if ($3 > 100000000) print $4 }' \
| sort -u
步骤 3:清除历史文件
# 示例:清除 bigfile.zip
# filter-repo
git filter-repo --path bigfile.zip --invert-paths --force
步骤 4:清理并推送
git gc --aggressive --prune=now
git push origin master --force

备选方案( 无需安装工具)

# 清除特定文件的历史
git filter-branch --force --index-filter \
  "git rm --cached --ignore-unmatch path/to/largefile" \
  --prune-empty --tag-name-filter cat -- --all

# 清理仓库
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
git reflog expire --expire=now --all
git gc --prune=now --aggressive

场景二:需要保留大文件

方案一:使用 Git LFS(推荐)

步骤 1:安装 Git LFS
# Linux
curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | sudo bash
sudo apt install git-lfs

# macOS
brew install git-lfs

# Windows
choco install git-lfs
步骤 2:初始化 LFS
git lfs install
git lfs track "*.zip" "*.psd" "*.bin"
git add .gitattributes
步骤 3:迁移历史文件
# 迁移特定文件类型
git lfs migrate import --include="*.zip" --everything

# 迁移特定文件
git lfs migrate import --include="path/to/bigfile.zip" --everything
步骤 4:推送更改
git push origin master --force

方案二:云存储 + Git 子模块(企业级方案)

  1. 将大文件上传到云存储(AWS S3, Google Cloud Storage 等)
  2. 创建包含下载脚本的仓库
  3. 在主项目中添加子模块
# 添加云存储仓库作为子模块
git submodule add https://github.com/yourcompany/assets-repo assets

# 更新子模块
git submodule update --init --recursive

场景三:大文件在历史中存在但当前已删除

最佳实践:重写历史清除

# 1. 创建备份分支
git checkout -b backup-before-clean

# 2. 使用 BFG 工具清除
java -jar bfg.jar --delete-files bigfile.zip .

# 3. 清理仓库
git reflog expire --expire=now --all
git gc --prune=now --aggressive

# 4. 强制推送
git push origin master --force

替代方案:浅层克隆

# 创建不含历史的新分支
git checkout --orphan new-branch
git add .
git commit -m "Initial commit without history"

# 推送到新分支
git push origin new-branch

# 在远程设置新分支为默认

网络优化方案

解决连接问题

# 增加 POST 缓冲区大小
git config http.postBuffer 524288000  # 500MB

# 降低压缩级别
git config core.compression 0

# 使用 SSH 替代 HTTPS
git remote set-url origin [email protected]:user/repo.git

# 启用长连接
git config http.version HTTP/1.1

分块推送大仓库

# 每次推送部分历史
git push origin master --thin-pack --depth=100

预防措施

添加预提交钩子

创建 .git/hooks/pre-commit

#!/bin/sh
MAX_SIZE=104857600 # 100MB
for file in $(git diff --cached --name-only); do
  size=$(git cat-file -s :$file 2>/dev/null || ls -l $file | awk '{print $5}')
  if [ $size -gt $MAX_SIZE ]; then
    echo "错误: $file 超过100MB ($size bytes)"
    echo "使用 'git reset HEAD $file' 取消暂存"
    exit 1
  fi
done

设置权限:chmod +x .git/hooks/pre-commit

永久配置

# 禁止大文件
echo '*.zip filter=lfs diff=lfs merge=lfs -text' >> .gitattributes

# 设置全局忽略
git config --global core.excludesfile ~/.gitignore_global
echo "*.log" >> ~/.gitignore_global
echo "*.tmp" >> ~/.gitignore_global

不同场景推荐方案

场景 推荐方案 优点 缺点
个人项目 不需要历史大文件 git filter-repo 永久解决,仓库瘦身 重写历史
团队协作 需要保留大文件 Git LFS 版本控制大文件 需要LFS支持
企业项目 超大文件管理 云存储+子模块 无仓库膨胀 增加架构复杂度
历史大文件 当前已删除 BFG工具 快速清除历史 需要Java环境

操作后验证

# 检查仓库大小
git count-objects -vH

# 测试推送
git push --dry-run origin master

# 验证历史
git log --all --find-object=<大文件哈希>

注意事项

  1. 强制推送风险--force 会覆盖远程历史,确保团队协调
  2. 备份优先:操作前执行 git clone --mirror 创建完整备份
  3. LFS成本:Git LFS 可能有存储和带宽限制
  4. 团队通知:历史重写后通知成员重新克隆仓库

通过以上方案,您可以彻底解决 Git 大文件导致的推送问题,并根据实际需求选择最适合的处理方式。

你可能感兴趣的:(开发周边,git)