结论是 ssh 认证失败,需确认密钥是否存在、是否添加到 github、是否被正确加载;执行 ssh -t git@github.com 验证,若返回 “hi 用户名” 则密钥正常,否则需检查密钥生成、添加、权限、ssh-agent 加载及公钥完整性。

直接结论:这不是 Git 本身的问题,而是 SSH 认证失败。你本地没有可用的、且被 GitHub/GitLab/Gitee 等平台认可的 SSH 密钥对,或者密钥没被正确加载。
怎么确认是不是 SSH 密钥问题
执行 ssh -T git@github.com(或 git@gitlab.com、git@gitee.com),看返回:
- 如果输出
Hi username! You've successfully authenticated...→ 密钥正常,问题在别处(比如子模块地址写死、Git 配置冲突) - 如果输出
Permission denied (publickey)→ 确实是 SSH 密钥缺失或未生效 - 如果提示
Could not resolve hostname或连接超时 → 是网络或 DNS 问题,和密钥无关
为什么生成密钥后还是报错
常见原因不是“没生成”,而是以下任一环节断了:
-
ssh-agent没启动,或没把私钥加进去:eval "$(ssh-agent -s)"和ssh-add ~/.ssh/id_ed25519必须都执行,且当前终端 session 有效 - 私钥文件权限太宽松(如
chmod 755 id_ed25519):SSH 客户端会拒绝加载,必须是600(chmod 600 ~/.ssh/id_ed25519) - 用了多个密钥但没配
~/.ssh/config,导致 Git 默认选错 key(尤其当你有 GitHub + GitLab 多账户时) - 公钥复制时带了换行或空格(比如从记事本粘贴),
id_ed25519.pub内容必须是一整行,开头是ssh-ed25519,结尾是邮箱
子模块报 Permission denied (publickey) 怎么办
重点:子模块地址来自 .gitmodules 文件,它可能是硬编码的 SSH 地址(url = git@github.com:xxx/yyy.git)。即使主仓库用 HTTPS 克隆,子模块仍会走 SSH。
- 临时解决:强制所有子模块改用 HTTPS 协议(适合公开仓库):
git config --global submodule.$name.update checkout不起作用;正确做法是运行:git submodule foreach --recursive 'git config remote.origin.url $(git config -f $toplevel/.gitmodules submodule.$name.url | sed "s/^git@/https:\/\//; s/:/\//; s/\.git$//")'
然后重跑git submodule update --init --recursive - 长期解决:确保 SSH 密钥已配置并生效(见前两节),再执行子模块更新
- 注意:如果子模块是私有仓库,HTTPS 方式需配合 PAT(Personal Access Token)使用,不能直接填密码
容易被忽略的细节
Windows 用户用 Git Bash 时,ssh-agent 不会自动继承到新打开的窗口;每次新开终端都要重新 eval 和 ssh-add,除非你把它写进 ~/.bashrc;Mac 用户用 Terminal 可能默认不加载 ssh-agent,而 iTerm2 有时会自动处理——这种不一致最容易让人误以为“昨天还好好的”。











