这篇文章是在折腾 Git 提交签名和 SSH 推送的时候写的,属于踩坑记录。如果你也遇到过 signed commit 但是 push 被拒的问题,可能对你有帮助。
起因
最近在捣鼓 AlephNote 这个项目,想搞一个比较干净的 Git 工作流:writing 分支写东西,然后 merge 到 deploy 分支,再推到远程仓库。
过程中碰到了一个很奇怪的事——我配好了 GPG 签名提交,commit 确实是 signed 的,但 push 的时候居然被拒绝了。一脸懵逼,明明之前没问题的。
后来才发现:Git 的签名(signing)和推送(push)用的密钥根本不是一回事。
两件事,两条线
简单来说,你在用 Git 的时候,涉及两种完全不同的认证场景:
1. Commit 签名
这玩意儿用来证明”这个 commit 确实是我提交的”。你可以用 GPG 或者 SSH 来签名。
配置方法:
1 | 设置使用 GPG 签名所有提交 |
签完名之后,你可以用 git log -gpg-sign 或者 GitHub/GitLab 上看 commit 旁边有个 verified badge,就是这货生效了。
但是! 这个签名只作用于 commit object,不参与网络传输。换句话说,它跟你的远端仓库有没有权限推送,没有任何关系。
2. SSH Push 认证
这才是你真正往远程仓库写东西时用的。SSH key 负责的是”你是谁,我能相信你能操作这个仓库吗”这个问题。
生成方式也很简单:
1 | ssh-keygen -t ed25519 -C "your_email@example.com" -f ~/.ssh/git_push |
然后把公钥加到你的 GitHub/Gitee 账户里就行了。GitHub 上在 Settings → SSH and GPG keys → New SSH key,把 id_ed25519.pub 的内容贴上去。
混在一起会怎样?
问题就出在这里。很多人以为配好签名就够了,或者反过来,配了 SSH 推送就觉得 commit 也会自动签名。都不对。
我之前遇到的情况就是:
- GPG signing key 配的是
~/.secret/id_sign.pub - SSH push key 配的是
~/.ssh/id_ed25519
这两个完全独立,一个管签名字符串里的 identity,一个管 SSH transport 层的认证。
如果你不小心把它们搞混了,就会遇到以下症状:
- commit 报
gpg: skipped secret key—— 签名 key 不对或者没导入 - push 报
Permission denied—— SSH key 没配或者不在 authorized keys 里
这两者看起来都是 push 失败的样子,但根本原因完全不同。
怎么正确配置?
我的实际配置长这样:
Commit Signing
1 | git config --global commit.gpgSign true |
这里我用的是 SSH 格式的签名 key,而不是传统的 GPG。这是因为 SSH key 更常见,管理起来也更直观。注意 -S 参数表示 signed commit。
SSH Push
SSH push 的配置相对省心。只要确保你的 SSH agent 知道你的私钥就行:
1 | ssh-add ~/.secret/id_sign.pub |
或者在你的 ~/.ssh/config 里写好 Host 对应的 identity file:
1 | Host github.com |
这样每次 push 到 GitHub 的时候,SSH client 会自动用指定的 key 做身份认证。
为什么用同一个文件也没事?
有人可能会说:”那你刚才说的两个 key 最后都用的是 id_sign.pub,这不还是一回事?”
实际上,同一个文件的 公钥部分 可以在两个地方复用——因为 SSH 格式既可以用作 GPG-substitute 签名,也可以直接当 SSH auth key 用。但这只是因为你用了同一种算法的不同用途,本质上还是两个独立的协议层。
如果用 GPG key 做 commit 签名 + SSH key 做 push,那就是两个完全不同的密钥文件了。
总结
一句话:Git 的 commit 签名和网络推送是两件独立的事情,各自有自己的 key 体系。
- Commit 签名 = 证明这条记录是谁写的,用的是 GPG 或 SSH 签名 key
- Push 认证 = 证明你有权限操作这个仓库,用的是 SSH auth key
别把它们混为一谈,排查问题的时候效率能高不少。
下次再遇到 push 失败,先搞清楚到底是签名出了问题还是 SSH 认证出了问题。看错误信息就能大致判断——有 gpg 字眼的肯定是签名问题,纯 Permission denied 的话就去检查 SSH key 配置。