Git fetch 失败: gnutls_handshake() failed 的排查与修复

在项目中执行 git fetch 时报错:

复制代码
fatal: unable to access 'https://git.example.com/rxx/demo_project.git/':
gnutls_handshake() failed: Error in protocol version

2. 表现

  • git fetch / git pull 均失败,无法访问远程仓库。
  • 报错信息固定为 gnutls_handshake() failed: Error in protocol version
  • 系统其他工具(如 curl)访问同一域名却正常。

3. 根因分析

3.1 服务器只支持 TLS 1.3

openssl s_client 实测远端服务器:

bash 复制代码
echo | openssl s_client -connect git.example.com:443 -tls1_2   # 失败
# error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version
echo | openssl s_client -connect git.example.com:443 -tls1_3   # 成功

服务器对 TLS 1.2 握手直接拒绝,只放行 TLS 1.3。

3.2 git 客户端使用的 TLS 库不支持 TLS 1.3

  • 旧版系统 git(Ubuntu 18.04 自带 2.17.1)使用系统 gnutls 3.5.18,不支持 TLS 1.3。
  • 升级到 PPA 版 git 2.50.1 后依旧失败,因为 PPA 把 gnutls 版 libcurl 静态链接 进了 git 二进制:
    • ldd /usr/bin/git 看不到 libcurl 动态依赖(被静态打入)。
    • git -c http.sslBackend=opensslCould not set SSL backend to 'openssl': already set,说明编译时已固定为 gnutls。
  • 系统 curl 之所以正常,是因为它链接的是 openssl 版 libcurl(libcurl.so.4)。

3.3 关键点

组件 TLS 后端 TLS 1.3 支持 结果
系统 curl OpenSSL 1.1.1 支持 正常
系统 git 2.17.1 gnutls 3.5.18 不支持 失败
PPA git 2.50.1 静态 gnutls libcurl 不支持 失败

4. 解决办法:从源码编译 git,链接 openssl 版 libcurl

核心思路:让 git 使用系统已有的 openssl 版 libcurl.so.4,而不是默认的 -lcurl(它会解析到 gnutls 版的 libcurl.so -> libcurl-gnutls.so 符号链接)。

4.1 下载源码

bash 复制代码
cd /tmp/opencode
curl -sL -o git.tar.gz https://github.com/git/git/archive/refs/tags/v2.50.1.tar.gz
tar xzf git.tar.gz
cd git-2.50.1

4.2 生成 configure 并配置

bash 复制代码
make configure
./configure --prefix=$HOME/git-local --without-tcltk CURL_LDFLAGS="-Wl,-l:libcurl.so.4"

注意:configure 生成的 config.mak.autogen 会覆盖为 CURL_LDFLAGS=-lcurl,需要手动改回:

bash 复制代码
sed -i 's|^CURL_LDFLAGS=-lcurl$|CURL_LDFLAGS=-Wl,-l:libcurl.so.4|' config.mak.autogen

4.3 编译并安装

bash 复制代码
make -j$(nproc)
make install

4.4 验证

bash 复制代码
# 确认 git-remote-http 链接 openssl 版 libcurl
ldd $HOME/git-local/libexec/git-core/git-remote-http
#   libcurl.so.4 => /usr/lib/x86_64-linux-gnu/libcurl.so.4
#   libssl.so.1.1  => /usr/lib/x86_64-linux-gnu/libssl.so.1.1

# 测试 fetch
$HOME/git-local/bin/git fetch origin   # 成功,exit=0

4.5 设为默认

bash 复制代码
echo 'export PATH="$HOME/git-local/bin:$PATH"' >> ~/.zshrc
# 新开终端生效

5. 总结

  1. 服务器只接受 TLS 1.3,客户端 TLS 库不支持则握手失败。
  2. PPA 版 git 静态绑定了 gnutls 版 libcurl,无法通过配置切换后端。
  3. 从源码编译 git 并强制链接 openssl 版 libcurl.so.4 即可解决。
相关推荐
snowfoootball3 小时前
fork的仓库怎么使用git rebase与git merge同步上游仓库的更新及解决冲突问题的总结
git
小溪彼岸3 小时前
Git命令行可视化管理工具:lazygit
git
badhope4 小时前
入职第一天就把main分支搞崩了——我的Git血泪史和团队规范诞生记
git·devops
Eloudy6 小时前
git clone --mirror 完整迁移仓库的全流程
git
:-)21 小时前
开源项目二开 Git 规范工作流
git
阿米亚波1 天前
【C/C++包管理器】vcpkg(by microsoft)
c语言·c++·git·vscode·microsoft·github·vcpkg
苍煜1 天前
Git Worktree 多工作树实战教学-工作多分支实用教程
git
RainingTime1 天前
新版IDEA使用传统用户名、密码登录Git
git
DevangLic2 天前
【诡异的空文件夹 和 git:冲突合并】最新的尝试
git