阿星的空间

Git LFS 推送故障排查与解决方案报告

Git LFS 推送故障排查与解决方案报告

一、问题描述

执行 git push 推送当前分支 wyx(领先 origin/wyx 11 个提交)时,遇到双重阻碍:

  1. git-lfs 进程崩溃(SIGSEGV 段错误),报错信息:
    panic: runtime error: invalid memory address or nil pointer dereference
    github.com/git-lfs/git-lfs/v3/ssh.(*SSHTransfer).SetConnectionCountAtLeast(...)
  2. GitLab 服务端 pre-receive hook 拦截,即使 git push 的对象包(214 个对象)传输成功,但远程 hook 检测到 LFS 对象缺失,拒绝推送:
    remote: GitLab: LFS objects are missing. Ensure LFS is properly set up or try a manual "git lfs push --all".

二、环境上下文

  • git-lfs 版本: 3.7.1(Homebrew 安装),Go 1.25.3 编译
  • Git 版本: 2.50.1(Apple Git-155)
  • 远程仓库: git@gitlab.xxxx.com:data/data-xxxx.git(内网 GitLab)
  • LFS 追踪的文件类型: *.xlsx, *.csv, *.zip, *.rar, *.pptx, *.xls, *.xlsm
  • 当前分支领先提交: 11 个 commit,涉及 51 个 LFS 对象需要推送

三、探索过程

阶段 1:初步诊断

git status → 工作区干净,领先 origin/wyx 11 个提交
git push → git-lfs 崩溃(SIGSEGV)

尝试禁用 LFS 后推送:

git -c filter.lfs.required=false push --no-verify

→ 代码对象推送成功(214/214),但远程 hook 拒绝:"LFS objects are missing"

阶段 2:修复 SSH 协议崩溃

通过 GIT_TRACE=1 定位崩溃根源:

  • git-lfs 先尝试纯 SSH 协议(git-lfs-transfer 命令),GitLab 返回 Disallowed command
  • 回落时 *SSHTransfer 对象为 nil,触发 nil pointer dereference

发现额外配置问题: 本地 .git/config 中配置了:

[lfs]
    url = ssh://git@gitlab.xxxx.com/data/data-xxxx.git/info/lfs

这个 /info/lfs 后缀是错误的——当 git-lfs 用此 URL 执行 git-lfs-authenticate 时,传递的路径是 /data/data-xxxx.git/info/lfs,GitLab 无法识别,返回 The project you were looking for could not be found

修复动作:

git config --local --unset lfs.url
git config --local lfs.ssh.autoProtocol false
  • 删除 lfs.url 让 git-lfs 自动从 remote URL 推导正确路径
  • autoProtocol false 禁用纯 SSH 协议尝试,避免崩溃

阶段 3:git lfs push 卡死

修复后 git lfs push origin wyx 不再崩溃,但卡死在 0% (0/1) 进度。

GIT_CURL_VERBOSE=1 GIT_TRACE=1 深入排查,发现:

  • SSH 认证成功(git-lfs-authenticate 返回 token)
  • HTTP batch 请求已发送(POST /info/lfs/objects/batch
  • 请求发出后无响应返回——git-lfs 的 HTTP 客户端挂起

但用 curl 模拟完全相同的请求(含 Accept: application/vnd.git-lfs+jsonContent-Type: application/vnd.git-lfs+jsonUser-Agent 和 Authorization header),63ms 即返回 200 OK。说明:

  • 服务端 LFS API 正常工作
  • 问题出在 git-lfs 二进制本身(Go 1.25.3 运行时的 HTTP 客户端可能有 bug)

阶段 4:手动推送 LFS 对象(解决方案)

绕过损坏的 git-lfs 二进制,直接使用 GitLab LFS HTTP API:

工作流:

  1. SSH 执行 git-lfs-authenticate 获取 Bearer token 和 LFS endpoint URL
  2. git lfs push --dry-run 获取需要推送的 51 个 OID
  3. POST /info/lfs/objects/batch 获取每个 OID 的上传 URL(含预签名认证)
  4. PUT <upload_url> 逐个上传文件内容(含 SHA256 校验)

使用 Python 脚本实现,全部 51 个对象上传成功(最大 756KB,总大小约 1.3MB)。

阶段 5:最终推送

LFS 对象上传完成后,禁用 LFS pre-push hook 执行 git push

git -c filter.lfs.required=false push --no-verify

推送成功91b76e7d..83d2c9e8 wyx -> wyx

四、根因总结

问题 根因
git-lfs 段错误崩溃 git-lfs/3.7.1 用 Go 1.25.3 编译,SSH 协议协商失败后 nil pointer dereference
LFS 认证失败 project not found .git/configlfs.url 配置了错误路径(带 /info/lfs 后缀)
git lfs push 卡死 git-lfs 二进制本身的 HTTP 客户端存在 bug,发出 batch 请求后无法接收响应
远程 hook 拒绝推送 LFS 对象未上传到 GitLab 存储

五、后续注意点

  1. git-lfs 二进制损坏:当前系统的 git-lfs/3.7.1(Go 1.25.3)有多个 bug。后续建议:

    • 尝试降级到稳定版本:brew install git-lfs@3.4 或更早版本
    • 或等 Homebrew 发布修复版本后升级
    • 应急直接使用 Python 脚本通过 API 推送
  2. lfs.url 配置规范:如果手动配置 lfs.url,路径必须是仓库路径本身(data/data-xxxx.git),不要加 /info/lfs 后缀。或者不设 lfs.url,让 git-lfs 自动从 remote URL 推导。

  3. LFS 推送替代方案:如果以后 git lfs push 又出问题,保留的解决路径:

    • git lfs push --dry-run 获取 OID 列表
    • 通过 git-lfs-authenticate 获取 token
    • 直接调 LFS batch API + PUT 上传
  4. git push 触发 LFS pre-push hook:在 git-lfs 修复前,推送时需显式禁用 LFS hook:

    git -c filter.lfs.required=false push --no-verify

原文来自阿星的空间:https://wanyaxing.com/blog/20260615163159.html

X