1. 先别急着改代码,pre-receive hook declined 到底卡在哪
git push到 master 时看到! [remote rejected] master -> master (pre-receive hook declined),第一反应往往是「我代码写错了?」其实这条报错跟你的 commit 内容基本无关,它是服务端在真正写入仓库之前,用一段叫pre-receive的钩子脚本把你的推送拦下来了。换句话说,代码已经打包发到服务端了,是服务端在「入库前安检」这一步说了不。
这个钩子能拦的东西很多:分支保护规则、推送权限、提交信息格式、大文件限制、签名校验、CI 门禁,甚至仓库配额。所以同一个报错,背后可能是完全不同的原因。我见过有人折腾一下午改 SSH key,最后发现只是 master 被设成了 Protected Branch,而他的角色是 Developer,压根没资格直推。
这篇面向的场景很具体:你本地git push origin master被拒,报的就是 pre-receive hook declined,你想按「服务端钩子 → 分支保护 → 权限 → 提交规范」四条线逐一定位,并且顺手把 TaoToken 的统一 Key/API 通道接进你的开发流里。适合刚接手一个陌生仓库、或者团队刚开了分支保护、又或者你在用自动化脚本推代码却突然被拦的开发者。下面每一步都给可复制的命令和配置,你照着排查就行。
2. 把 TaoToken 统一 Key 通道接进来,先解决「用哪个凭证」的问题
排查推送问题之前,有个容易被忽略的点:很多团队现在不止一个模型服务或 API 通道,本地脚本、CI、编辑器插件各配各的 Key,一出问题根本不知道是哪个凭证在生效。TaoToken 做的就是把这些统一到一个 Key 通道上,官网在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 入口是 https://taotoken.net/api 。
它的定位不是替代 Git 或编辑器,而是给你一个统一的模型调用出口:你申请一个 Key,然后在 config.toml、settings.json 这类配置文件里指向同一个 base_url,本地调试、脚本、Agent 都走这条通道。这样当推送被拦、你需要临时用模型帮忙分析钩子日志时,不用再翻五个不同的 Key。
接入前先做两件事。第一,去控制台建 Key,地址是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,建完在 API Keys 页面能看到明文(只显示一次,记得存好),页面是 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。第二,确认你要接的是哪类工具:如果是命令行里想让模型帮你读钩子报错,用模型对话入口 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content=model-chat&utm_campaign=rewrite ;如果是长期写代码、跑 Agent,建议直接看 Coding Plan https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ,额度模型更适合持续调用。
注意:TaoToken 是模型 API 通道,不参与你的 Git 权限判定。它帮不了你「绕过」pre-receive hook,但能帮你快速读懂服务端返回的钩子日志、生成规范的 commit message、写排查脚本。别把两件事混在一起。
3. 可复制的配置骨架:config.toml 与 settings.json
下面给两份骨架,一份给偏命令行的工具(config.toml),一份给偏编辑器/插件的工具(settings.json)。你把 Key 换成自己申请的那串即可,base_url 统一指向 TaoToken。
先看config.toml:
# ~/.taotoken/config.toml # TaoToken 统一 Key 通道配置骨架 [provider] name = "taotoken" base_url = "https://taotoken.net/api" api_key = "sk-你的TaoTokenKey" timeout_seconds = 60 [model] default = "claude-sonnet" # 需要长上下文或复杂推理时切换 fallback = "gpt-4o" [request] max_retries = 3 retry_backoff_ms = 800 # 排查 git 钩子日志时,把温度调低更稳 temperature = 0.2 [logging] level = "info" # 出问题时打开,能看到实际请求的 endpoint log_request = true再看settings.json,适合编辑器插件或 Agent 类工具:
{ "taotoken": { "baseUrl": "https://taotoken.net/api", "apiKey": "sk-你的TaoTokenKey", "defaultModel": "claude-sonnet", "models": { "fast": "gpt-4o-mini", "strong": "claude-sonnet" }, "request": { "timeout": 60000, "maxRetries": 3 }, "features": { "explainGitHookLog": true, "generateCommitMessage": true } } }两份配置的核心就三点:base_url 指向https://taotoken.net/api,api_key 用你控制台建的那串,模型名按需选。放好之后,你的工具就会走这条统一通道,而不是散落各处的旧 Key。
配置完先别急着推代码,用一条最小请求验证通道是否通。命令行里可以这样测:
curl -s https://taotoken.net/api/v1/models \ -H "Authorization: Bearer sk-你的TaoTokenKey" \ -H "Content-Type: application/json" | head -c 500返回里有模型列表,说明 Key 和 base_url 都对。这一步过了,再回到 Git 排查,你就有个能随时问的模型助手了。
4. 四条线逐条定位 pre-receive hook declined
现在进入正题。按「服务端钩子 → 分支保护 → 权限 → 提交规范」顺序查,基本能覆盖九成情况。
4.1 服务端钩子本身在拦什么
pre-receive 是服务端仓库hooks/目录下的脚本,它在收到推送、还没更新 ref 之前执行。如果脚本exit 1,Git 就返回 pre-receive hook declined。你要做的是看到脚本到底输出了什么。
GitLab 用户看Settings -> Repository -> Protected Branches,同时让管理员查/var/opt/gitlab/git-data/repositories/<namespace>/<repo>.git/custom_hooks/pre-receive或系统钩子目录。GitHub 没有自定义 pre-receive 给你改,但它的分支保护、必需状态检查、签名要求都会以类似方式拒绝。Gitea/Gogs 看仓库hooks/pre-receive.d/。
一个常见坑:钩子里有while read oldrev newrev refname循环,如果脚本对refname做了白名单,而你推的是refs/heads/master,不在白名单里就被拒。让管理员把钩子日志级别调高,或者临时在脚本里加echo "ref=$refname"输出,你就能看到它卡在哪一行。
4.2 分支保护:master 是不是被锁了
这是最高频原因。很多平台默认把 master/main 设为保护分支,只允许 Maintainer 及以上直推,Developer 必须走 Merge Request。你给用户 B 开了 Developer 权限,但他依然推不了 master,就是因为保护规则里Allowed to push没放开。
以 GitLab 为例,路径是Settings -> Repository -> Protected Branches -> Expand,把Allowed to push从Maintainers改成Developers + Maintainers,或者干脆允许特定人。改完再推。GitHub 在Settings -> Branches -> Branch protection rules,检查Require a pull request before merging和Restrict who can push。
提示:生产仓库不建议直接放开 master 直推。更稳的做法是让 B 推到 feature 分支,再开 MR。保护规则的本意就是防误推,别为了省事把它拆了。
4.3 权限与角色:Developer 到底能干什么
权限模型各平台不同,但逻辑类似。GitLab 里 Guest 只能看,Reporter 能看不能推,Developer 能推非保护分支、能开 MR,Maintainer 才能改保护规则和直推保护分支。所以你给 B 开 Developer,他推 feature 分支没问题,推 master 就会被 pre-receive 拦。
验证方法:让 B 执行git push origin HEAD:refs/heads/test-branch,如果这个能成,说明权限本身没问题,问题在 master 的保护规则。如果连非保护分支都推不了,那要查仓库成员角色、SSH key 是否绑对账号、以及是否有 IP 白名单。
4.4 提交规范:commit message 和文件大小
有些团队的 pre-receive 钩子会校验 commit message 格式,比如必须匹配^(feat|fix|docs|chore): .+。你的 message 写成「update」就会被拒。还有钩子限制单文件大小,比如超过 100MB 直接拒,报错里通常带文件名。
排查命令:
# 看最近几条 commit message 格式 git log --oneline -5 # 找出本次推送里的大文件 git diff --stat HEAD~1 HEAD | sort -k3 -n -r | head # 如果某个大文件是误提交,从历史里移除 git rm --cached path/to/big.file git commit --amend -m "chore: remove large file"如果钩子要求 GPG 签名,你还要git config commit.gpgsign true并配好 key。签名缺失也会以 pre-receive 拒绝的形式出现。
5. 验证请求与成功结果长什么样
排查完,用一条干净的推送验证。假设你已放开保护或改推 feature 分支:
git checkout -b fix/pre-receive-check git add . git commit -m "fix: verify push after hook adjustment" git push origin fix/pre-receive-check成功时你会看到:
Enumerating objects: 5, done. Counting objects: 100% (5/5), done. Writing objects: 100% (3/3), 320 bytes | 320.00 KiB/s, done. Total 3 (delta 1), reused 0 (delta 0) remote: remote: To create a merge request for fix/pre-receive-check, visit: remote: https://your-git-host/.../merge_requests/new?... remote: To https://your-git-host/group/repo.git * [new branch] fix/pre-receive-check -> fix/pre-receive-check关键是没有remote rejected,且最后一行是[new branch]或master -> master。如果推的是 master 且保护已放开,你会看到abc1234..def5678 master -> master。
同时验证 TaoToken 通道:让模型读一段钩子日志,确认它能正常返回。用模型对话入口 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content=model-chat&utm_campaign=rewrite 贴入报错,问「这段 pre-receive 脚本在哪一行拒绝了 refs/heads/master」,能给出定位就说明通道和配置都生效了。
6. 本篇常见错排查清单
报错依旧但日志为空:钩子可能把 stderr 吞了。让管理员在 pre-receive 里加set -x或把输出重定向到文件,再复现一次。
改了保护规则还是被拒:检查是否有多个保护规则叠加,或者仓库继承了 group 级别的保护设置。GitLab 里 group 的 Protected Branches 会覆盖项目级。
Developer 推 feature 也被拒:查仓库是否设了push rules,比如禁止未签名提交、禁止作者邮箱不匹配。这类规则在Settings -> Repository -> Push Rules。
SSH 能连但 push 被拒:ssh -T git@your-host确认身份是哪个账号。多 Key 场景下~/.ssh/config里 Host 别名可能指错账号。
TaoToken 请求 401:Key 复制时带了空格,或 base_url 写成了带路径的https://taotoken.net/api/v1而工具又自动拼/v1。统一用https://taotoken.net/api,让工具自己拼版本路径。
模型返回超时:config.toml 里timeout_seconds调大到 120,max_retries保持 3。长日志分析容易超时,可以先把日志截断到关键 200 行再喂。
commit message 校验总不过:用git commit --amend改最近一条,历史多条用git rebase -i HEAD~n批量改。改完强推 feature 分支没问题,master 上慎用--force。
排查推送问题时,把钩子日志、保护规则截图、git remote -v输出一起丢给模型,比你自己逐行读脚本快得多。通道配好之后,这类「读日志定位」的活基本可以交给它,你专注改代码就行。