GitHub 推送前预检 是做什么的?
推送新仓库失败的方式就那么几种无聊、重复的情况,而且错误信息往往指向错误的方向。src refspec main does not match any 听起来像分支问题,但通常意味着没有提交。一个在工作账户下静默成功的推送,看起来和正确成功的推送一模一样。本技能在用户浪费一次推送尝试之前,提前发现这些问题。
指导原则:诊断,不修改。 报告发现的问题,并提供可复制粘贴的修复命令。由用户自己运行。人们之所以保留多个账户,正是因为他们关心哪个账户在操作,所以悄悄地替他们运行 git config 或 git remote set-url 恰恰是错误的直觉。
运行方式
bash <skill-dir>/scripts/preflight.sh [repo-path] [remote-name]
两个参数都是可选的——默认使用当前目录和 origin。该脚本仅读取:git config --get、ls-remote、ssh -T、push --dry-run。它从不写入。
如果脚本因故无法运行,下面的检查每项都是一行命令;直接运行它们并采用相同的解释方式。
检查结果的含义
脚本打印 PASS / WARN / FAIL 行。解释比原始输出更重要,因此请翻译而非粘贴:
仓库 / 文件 / 提交。 空目录或无提交的仓库会在推送时产生 src refspec <branch> does not match any。Git 无法凭空构建提交。这是首次推送失败的最常见原因,因此一旦触发,请优先提及。
分支。 新 git init 可能停留在 master,而远程期望 main。重命名在首次提交之后是安全的(git branch -M main)——未出生分支上还没有任何可重命名的内容,因此命令顺序错误会浪费一个周期。
有效远程 URL。 值得精确了解,因为很容易错误检查:git remote get-url 已经展开了 url.<base>.insteadOf,git ls-remote --get-url 也是如此。因此比较这两者检测不出任何差异。原始值仅存于 git config --get remote.<name>.url 中,这就是脚本将其与 git remote get-url --push(--push 形式额外覆盖了 pushurl 和 pushInsteadOf,这正是推送实际使用的)进行比较的原因。当两者不同时,用户配置的 URL 并不是 Git 将要连接的 URL。经典的陷阱是全局的 url."https://github.com/".insteadOf = [email protected]:,它会将 SSH 静默重定向到 HTTPS,从而使用凭证管理器持有的任意账户——通常不是用户期望的那个。务必报告有效 URL,并明确标注差异。
身份验证账户。 对于 SSH,脚本通过 ssh -G 解析主机(因此像 github-personal 这样的主机别名会被跟踪到真实主机名和密钥),然后直接通过 ssh -T 询问 GitHub。GitHub 回复 Hi <username>! 并以 1 退出——这里的退出码 1 是成功情况,而非失败。对于 HTTPS,它读取当前 gh 账户。将该账户与 URL 的所有者部分进行比较:不匹配意味着推送将返回 403,或落入错误的命名空间。
提交身份。 从全局配置继承的 user.email 是静默失败。推送以一个账户进行身份验证,但每次提交的作者却是另一个地址——事后修复需要修改或重写历史。当仓库的远程属于不同所有者时,将全局作用域的身份视为 WARN 并重点提出,即使技术上尚未出现任何问题。
远程状态。 空的远程意味着首次推送是干净的。存在已有引用意味着普通推送将被拒绝(非快进),用户需要先 pull/rebase——在用户遇到拒绝之前就说明这一点。这里的错误通常意味着仓库不存在或账户缺少访问权限;GitHub 故意将两者都报告为“未找到”,因此提供两种解释而不是猜测。
试推送。 仅在存在提交后可能。这是真正的端到端检查——它演练身份验证和引用协商,但不写入。注意其盲点:它不会捕获服务器端拒绝,如 GH007(邮箱隐私),因为这些是在实际接收时评估的。
报告
给出一个简短的检查表格,然后——仅针对失败或警告的项目——给出可运行的精确修复命令,按执行顺序排列。不要将通过的检查表述为散文;用户想知道什么出错了以及要输入什么。
当需要设置身份时,优先使用仓库本地配置而非全局配置,因为关键点在于此仓库与机器的默认配置不同:
git config user.name "<account>"
git config user.email "<email>"
如果多个仓库需要同样的处理,提到 includeIf 作为持久解决方案:
[includeIf "gitdir:~/personal/"]
path = ~/.gitconfig-personal
如果推送被 GH007 拒绝,请指向 GitHub → 设置 → 邮箱中的 @users.noreply.github.com 地址,而不是猜测个人地址——关键评论和本地配置是标签,不是账户接受的证明。