换电脑后 git push 弹窗要重新登录的排查笔记
现象
把旧电脑的 git 配置(.gitconfig、.git-credentials、.ssh 目录)整套拷到新电脑后,在新电脑执行 git push(走 HTTPS 协议),本来以为会和旧电脑一样直接成功,结果弹出一个「connect to github」的登录窗口,要求重新授权。而走 SSH 协议则一切正常,不弹窗、秒推。
本文记录这次排查的过程和根因。
前置知识:git 配置的三个层级
git 读取配置是分层的,从低到高优先级依次为:
| 层级 | 文件位置 | 作用域 |
|---|---|---|
| 系统级 | <Git安装目录>\etc\gitconfig |
所有用户、所有仓库 |
| 全局级 | C:\Users\<用户名>\.gitconfig |
当前用户的所有仓库 |
| 项目级 | <仓库>\.git\config |
单个仓库 |
大部分配置项是「后者覆盖前者」,但 credential.helper 是个例外——它是叠加的,所有层级的 helper 都会生效,按「系统级 → 全局级 → 项目级」顺序依次执行。这一点是后面所有问题的核心。
排查过程
第一步:对比新旧电脑的配置
用 git config --list --show-origin 查看所有层级的配置及其来源文件。重点对比 credential 相关项:
新电脑:
系统级 (etc/gitconfig): credential.helper = manager
全局级 (.gitconfig): credential.helper = store
旧电脑:
系统级 (etc/gitconfig): (没有 credential.helper 这一行)
全局级 (.gitconfig): credential.helper = store
差异就一行:新电脑系统级多了一行 credential.helper = manager。
第二步:理解两个 helper 的区别
credential.helper = xxx 告诉 git:「要 HTTPS 凭证时,去调用 git-credential-xxx 这个程序」。
| helper | 对应程序 | 凭证存哪 | 没缓存时怎么办 |
|---|---|---|---|
manager (GCM) |
git-credential-manager.exe |
Windows 凭据管理器(加密) | 主动弹窗让用户授权,拿到后存起来 |
store |
git-credential-store.exe(git 自带) |
~/.git-credentials(明文文件) |
返回空,让 git 去问下一个 helper |
关键区别在于「没缓存时的行为」:
store是被动的:有就用,没有就返回空,让位给下一个 helper。manager是主动的:没缓存就弹窗去拿,不会让位。对 git 来说它「返回了凭证」(哪怕是通过弹窗拿的),调用链就结束了。
第三步:理解 helper 的调用规则
git 调用 credential helper 要凭证时,规则是:
依次调用每个 helper.get()
→ 任何一个返回了凭证,就停,不再问后面的
→ 全都返回空,才报认证失败
是串行的,不是「所有 helper 一起查,谁有用谁」。前面成功,后面就不跑了。
根因分析
旧电脑为什么不弹窗
旧电脑 push (HTTPS) 执行链:
1. 系统级:没有 credential.helper → 没有 GCM
2. 全局级:helper = store → 只有 store 一个 helper 上场
3. store 读 ~/.git-credentials
4. 命中 github 的记录(之前存过的 token)
5. 自动填进 HTTP 请求 → 认证通过
结果:什么都没发生,push 成功
新电脑为什么弹窗
新电脑 push (HTTPS) 执行链:
1. 系统级:helper = manager → GCM 上场(排第一)
2. GCM 查 Windows 凭据管理器 → 没有 github 的 token
3. GCM 主动弹 OAuth 窗,让用户授权
4. 用户在窗里授权 → GCM 拿到 token,存进 Windows 凭据管理器
5. GCM 返回凭证给 git
6. git 拿到凭证,停止调用后续 helper
→ store 根本没被调用
结果:弹窗,要重新登录
「.git-credentials 明明拷过来了,为什么没用」
这是排查中最容易卡住的点。直觉是:.git-credentials 里明明有 github 的 token(从旧电脑拷来的),store 读它不就行了?
答案:store 压根没被调用到。因为:
- GCM 排在 store 前面(系统级 → 全局级的顺序)。
- GCM 没缓存时不会让位,而是自己弹窗去拿。
- GCM 拿到凭证后,git 就不再调用 store。
- store 没被调用,
.git-credentials里有再多 token 也没用。
换句话说,token 一直静静地躺在 .git-credentials 里,但新电脑的 git 从头到尾没去读过它一次——因为前面的 GCM 把活儿自己干了(通过弹窗)。
那行 helper = manager 是哪来的
helper = manager 是 Git for Windows 安装程序自动写入系统级 gitconfig 的。较新版本的 Git for Windows 安装时默认启用 GCM,就会写这行。旧电脑装的 Git 版本较旧、或安装时手动选了「不使用 GCM」,所以那行没写进去。
一个旁证:旧电脑系统级 gitconfig 里
sslCAInfo路径是D:/Git/...,新电脑是D:/Installer/Git/Git/...,两台电脑装的 Git 不是同一个版本/同一次安装。
顺带发现的配置差异(与弹窗无关)
对比新旧电脑配置时,还发现几处差异,均与弹窗问题无关,记录在此:
| 配置项 | 旧电脑 | 新电脑 | 说明 |
|---|---|---|---|
http.sslBackend |
openssl |
schannel |
HTTPS 证书校验的底层实现,功能等价 |
http.sslCAInfo |
指向 ca-bundle.crt | 无 | 配合 openssl 用,schannel 不需要 |
core.useBuiltinFSMonitor |
true |
无 | 文件系统监视器,加速 git status,纯性能项 |
[difftool/mergetool "sourcetree"] |
有 | 无 | SourceTree 安装时自动写入的注册信息,不用该 GUI 就不需要 |
解决方案
方案一:切 SSH(推荐,一劳永逸)
git remote set-url origin git@github.com:<账号>/<仓库>.git
把 remote URL 从 HTTPS 改成 SSH,直接绕开整个 credential 体系。SSH 走 ~/.ssh/id_* 密钥对认证,不经过 credential.helper,manager 在不在都不影响。前提是 SSH 密钥已经从旧电脑拷过来,且公钥已加到 GitHub 账号。
方案二:禁用系统级 GCM,只用 store(还原旧电脑行为)
让新电脑行为和旧电脑一致,用拷来的 .git-credentials。有三种做法,都能达到「走 HTTP、读 .git-credentials、不弹窗」的效果,但各有取舍。
做法 A:删除系统级的 helper = manager
直接编辑系统级 gitconfig(<Git安装目录>\etc\gitconfig),把 [credential] 段下的 helper = manager 那行删掉。
改后效果:
系统级:无 credential.helper
全局级:helper = store
→ 只剩 store 一个 helper → 读 .git-credentials → 命中 token → 不弹窗
做法 B:把系统级改成 helper = store
把系统级那行 helper = manager 改成 helper = store。
改后效果:
系统级:helper = store
全局级:helper = store
→ 两个 store 叠加,第一个(系统级)读 .git-credentials 命中 → 返回凭证 → 停止
→ 第二个(全局级)不会被调用
→ 不弹窗
功能上和做法 A 等价,但系统级和全局级都配 store,有点冗余。
做法 C:不动系统级,用全局配置覆盖(推荐)
git config --global --replace-all credential.helper ""
git config --global --add credential.helper store
原理:
- 第一行
credential.helper = ""(空字符串)有特殊含义——取消之前累积的所有 helper,包括系统级的 manager。 - 第二行重新 add 一个 store。
- 最终生效的只有 store,读
.git-credentials,不弹窗。 - 配置写在全局级
.gitconfig,系统级文件原封不动。
三种做法对比:
| 做法 | 改哪里 | Git 升级会被覆盖吗 | 推荐度 |
|---|---|---|---|
| A. 删系统级 manager | 系统级 gitconfig | 会 | 中 |
| B. 系统级改 store | 系统级 gitconfig | 会 | 低(冗余) |
| C. 全局级清空 + 设 store | 全局 .gitconfig | 不会 | 高 |
做法 A 和 B 都改了系统级 gitconfig,而 Git for Windows 升级时安装程序可能重新写入 helper = manager,把修改覆盖掉,弹窗又回来。做法 C 只动全局级 .gitconfig,系统级文件碰都不碰,升级覆盖不到,最稳。
改完后的验证:
git config --list --show-origin | findstr /I credential
确认生效的 helper 只剩 store(没有 manager),然后 git push 走 HTTP,应该不弹窗直接成功。
前提:拷来的
.git-credentials里 github 那条 token 还没过期。如果过期了,store 读到了但 GitHub 认证失败(401),这时得去 GitHub 重新生成 PAT,更新到.git-credentials里。
方案三:保留 GCM,走一次 browser 授权
在弹窗里选「Sign in with your browser」,走 OAuth 授权一次。GCM 会把 token 存进 Windows 凭据管理器,以后就不弹窗了。优点是 token 自动刷新、不用管过期;缺点是第一次要走浏览器。
只有在电脑/网络打不开 OAuth 页面、或在无头环境时,才考虑选「Token」方式手动粘 PAT。
验证根因的方法
如果想亲眼确认「store 没被调用是因为 GCM 挡路」,可以临时把新电脑系统级的 helper = manager 注释掉,然后 git push。这时只剩 store,它会读拷来的 .git-credentials,直接命中 token,不弹窗就成功。这就证明了:token 是好的,只是被 GCM 挡着没机会用。
总结
换电脑后 git push 弹窗的根因就一句话:新电脑装的 Git for Windows 版本在系统级 gitconfig 里自动写了 credential.helper = manager,而旧电脑没这行。GCM 排在 store 前面,没缓存就主动弹窗,store 被挡住读不了拷来的 .git-credentials。
理解了 credential helper 的三个机制——分层叠加、串行调用、GCM 主动获取不让位——整个现象就解释得通了。解决上,切 SSH 最省心;想保持旧电脑 HTTPS 体验就禁用 GCM;不介意走一次浏览器授权就让 GCM 存一次 token。

浙公网安备 33010602011771号