![gitlab: [remote rejected] pre-receive hook declined 排查手册:从 protected 分支到 TaoToken 统一 Key 的 develop 推](http://pic.xiahunao.cn/yaotu/gitlab: [remote rejected] pre-receive hook declined 排查手册:从 protected 分支到 TaoToken 统一 Key 的 develop 推)
1. 从一次真实的 develop 推送被拒说起gitlab: [remote rejected] pre-receive hook declined这个报错几乎每个用 GitLab 做团队协作的人都撞过。它的字面意思是你的本地提交已经打包好了GitLab 服务端也收到了但在真正写入仓库之前被服务端的pre-receive钩子拦下来了。注意这不是网络问题也不是你本地 Git 坏了而是服务端主动拒绝。很多人第一次看到这个报错会懵因为git push的输出里只有一行红字没有告诉你到底是权限不够、分支被保护还是钩子里有自定义校验。尤其是develop分支团队通常把它设成受保护分支只允许 Maintainer 推送普通 Developer 直接推就会被拒。这时候你需要的不是反复重试而是一套从分支保护规则到钩子日志的定位方法。这篇手册面向三类人刚接手 GitLab 项目、对 protected branch 不熟的新同学被pre-receive hook declined卡住、想快速定位原因的开发者以及想把 AI 编码工具的 endpoint 统一到 TaoToken 通道、顺便把 Git 推送流程理顺的工程团队。我会先讲清楚报错的判定逻辑再给出可复制的检查命令最后用一次真实的develop推送验证通过收尾。整个过程不需要你改 GitLab 源码也不需要动服务端配置除非你本身就有 Maintainer 权限。先说结论pre-receive hook declined是一个「统称」它背后可能是分支保护、可能是 push rule、可能是自定义钩子脚本返回了非零退出码。你要做的是把这个统称拆成具体原因而不是盲目地git push -f。下面按排查顺序展开。2. 定位 pre-receive hook declined 的真实原因与分支保护检查2.1 先确认是不是 protected branch 在拦你GitLab 的分支保护规则决定了「谁能推、谁能合并、谁能 force push」。当develop被设为 protected且你的角色是 DeveloperGitLab 会在 pre-receive 阶段直接拒绝报错就是pre-receive hook declined。判断方法有两种。第一种看 GitLab 网页端。进入项目 → Settings → Repository → Protected branches找到develop这一行看 Allowed to push 和 Allowed to merge 分别是谁。如果 Allowed to push 是No one或只有 Maintainer而你是 Developer那原因就找到了。第二种用 API 查适合脚本化或没有网页权限时curl --header PRIVATE-TOKEN: your_personal_access_token \ https://gitlab.example.com/api/v4/projects/project_id/protected_branches返回的 JSON 里会列出每个受保护分支的push_access_levels和merge_access_levels。access_level的数值含义0 表示 No access30 表示 Developer40 表示 Maintainer60 表示 Admin。如果你的用户角色对应的 level 低于push_access_levels里的要求推送必然被拒。2.2 区分「分支保护」和「push rule / 自定义钩子」不是所有pre-receive hook declined都来自 protected branch。GitLab 还有 Push Rules比如禁止提交信息不符合正则、禁止大文件、禁止 secrets以及管理员在服务端custom_hooks/pre-receive里写的脚本。区分方法是看报错附带的文字。如果只有pre-receive hook declined一行多半是分支保护或 push rule如果后面还跟着类似GitLab: You are not allowed to push code to protected branches on this project那就是明确的分支保护如果跟着commit message does not follow the pattern那是 push rule。你可以用下面这条命令看服务端返回的完整信息有时候git push默认输出被截断了GIT_TRACE_PACKET1 GIT_CURL_VERBOSE1 git push origin develop 21 | grep -i pre-receive\|remote:remote:开头的行就是服务端钩子打印的内容它比默认输出更完整。2.3 检查本地分支和远程分支的关系有时候报错看着像权限问题其实是本地develop和远程develop已经分叉而服务端配置了「不允许非快进推送」。先拉一下远程状态git fetch origin git log --oneline --graph --decorate origin/develop..develop git log --oneline --graph --decorate develop..origin/develop如果两边都有对方没有的提交说明分叉了。这时候即使你有推送权限非快进推送也可能被 push rule 拦下。正确做法是先git pull --rebase origin develop把本地提交挪到远程最新提交之后再推。2.4 用 git push 的 dry-run 预演在真正推送前可以用--dry-run看服务端会不会接受git push --dry-run origin develop--dry-run会走完整个协商流程但不真正写入。如果它同样报pre-receive hook declined说明问题在服务端规则不在你的网络或本地仓库。这一步能帮你排除「是不是我本地坏了」的疑虑。排查到这里基本能确定是分支保护、push rule 还是分叉。接下来讲怎么在合规前提下把代码推上去以及怎么把 AI 工具的 endpoint 统一到 TaoToken 通道让整个开发链路更顺。3. 可复制配置分支保护绕过策略与 TaoToken 统一 Key 接入3.1 分支保护的三种合规处理方式如果你没有 Maintainer 权限不要想着去改保护规则正确做法是走 Merge Request。流程是从develop切一个新分支提交推送新分支新分支通常不受保护然后在 GitLab 上发起 MR 合并到develop。git checkout develop git pull origin develop git checkout -b feature/fix-push-issue # 修改代码 git add . git commit -m fix: resolve pre-receive hook declined on develop git push origin feature/fix-push-issue推送新分支一般不会触发develop的保护规则。推成功后在 GitLab 网页端创建 Merge Request目标分支选develop等有权限的人合并。如果你确实有 Maintainer 权限且团队允许临时调整可以在 Settings → Repository → Protected branches 里把develop的 Allowed to push 临时改成 Maintainer Developer推完再改回来。但更推荐的做法是保留保护走 MR这样审计记录更清晰。3.2 把 AI 编码工具的 endpoint 统一到 TaoToken现在很多团队用 AI 编码助手比如 Cline、Continue、各类支持 OpenAI 兼容接口的插件每个工具各自配一个 Key管理起来很乱。TaoToken 提供统一的 API 通道Base URL 是https://taotoken.net/api你可以在一个地方管理 Key然后让各个工具都指向它。以 Cline 为例它的配置是一个 JSON 文件路径通常在 VS Code 的全局存储里。核心字段是apiProvider、baseUrl、apiKey、modelId。你要写全三件套Base URL、Key、Model ID。{ apiProvider: openai, baseUrl: https://taotoken.net/api, apiKey: sk-你的TaoToken密钥, modelId: claude-sonnet-4-20250514, modelInfo: { id: claude-sonnet-4-20250514, name: Claude Sonnet 4 } }注意baseUrl后面不要多加/v1TaoToken 的兼容层会处理路径。Key 在 TaoToken 控制台的 API Keys 页面生成生成后只显示一次记得保存。如果你用的是 Codex 类的工具配置在~/.codex/auth.json结构类似{ OPENAI_API_KEY: sk-你的TaoToken密钥, OPENAI_BASE_URL: https://taotoken.net/api }Model ID 根据你实际调用的模型填比如gpt-4o、claude-sonnet-4-20250514等。填错 Model ID 会报model not found这个后面排障会讲。3.3 用环境变量统一管理避免硬编码更工程化的做法是把 Base URL 和 Key 放到环境变量里工具配置引用变量export TAOTOKEN_BASE_URLhttps://taotoken.net/api export TAOTOKEN_API_KEYsk-你的TaoToken密钥然后在工具的 settings 里写${TAOTOKEN_BASE_URL}和${TAOTOKEN_API_KEY}。这样换 Key 只改一处团队共享配置时也不会把 Key 提交到仓库。注意不要把 Key 写进.env后提交到 Git.gitignore里加上.env和auth.json。配置完成后AI 工具的请求就走 TaoToken 统一通道了。接下来验证它是否真的通。4. 验证请求从 curl 到真实 push 的完整成功结果4.1 先用 curl 验证 TaoToken 通道在配置工具之前先用 curl 确认 Key 和 Base URL 可用curl https://taotoken.net/api/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer sk-你的TaoToken密钥 \ -d { model: claude-sonnet-4-20250514, messages: [{role: user, content: ping}], max_tokens: 16 }如果返回 JSON 里有choices数组说明通道正常。如果返回 401说明 Key 不对如果返回model not found说明 Model ID 写错了。这一步能帮你把 AI 工具的问题和 Git 推送的问题分开避免混在一起排查。4.2 验证 Git 推送走 MR 流程回到 Git 推送。假设你已经按 3.1 切了新分支并提交现在推送git push origin feature/fix-push-issue成功输出类似Enumerating objects: 5, done. Counting objects: 100% (5/5), done. Writing objects: 100% (3/3), 312 bytes | 312.00 KiB/s, done. Total 3 (delta 1), reused 0 (delta 0), pack-reused 0 remote: remote: To create a merge request for feature/fix-push-issue, visit: remote: https://gitlab.example.com/group/project/-/merge_requests/new?merge_request%5Bsource_branch%5Dfeature/fix-push-issue remote: To https://gitlab.example.com/group/project.git * [new branch] feature/fix-push-issue - feature/fix-push-issue看到[new branch]就说明推送成功没有pre-receive hook declined。然后按提示链接创建 MR目标分支选develop等合并。4.3 如果你有权限直接推 develop验证一次真实 push假设团队临时放开了develop的推送权限或者你本身就是 Maintainer可以这样验证git checkout develop git pull --rebase origin develop git push origin develop成功输出To https://gitlab.example.com/group/project.git a1b2c3d..e4f5g6h develop - develop看到develop - develop且没有 rejected就说明分支保护规则和你的权限匹配了。如果仍然被拒回到第 2 节重新检查push_access_levels。4.4 验证 AI 工具实际调用在 Cline 里发一条消息看它是否正常返回。如果返回内容正常说明 TaoToken 通道和 Model ID 都对。如果报错看下一节的排障对照表。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth5.1 401 Unauthorized这是最常见的。原因通常是 Key 写错、Key 过期、或者 Key 前面多了空格。检查方法echo -n sk-你的TaoToken密钥 | wc -c确认长度和 TaoToken 控制台显示的一致。另外注意Authorization: Bearer后面是一个空格不要多也不要少。如果用的是环境变量确认export在当前 shell 生效echo $TAOTOKEN_API_KEY能打印出来。5.2 local proxy failed这个报错通常出现在工具配置了本地代理但代理没启动。检查工具的 settings 里有没有proxy字段如果有确认代理地址和端口正确。如果你不需要代理直接删掉这个字段。注意这里说的是工具自身的代理配置不是网络层面的。5.3 reading choices 相关报错类似cannot read property choices of undefined或reading choices说明返回的 JSON 里没有choices字段。原因可能是Base URL 写成了https://taotoken.net/api/v1导致路径重复或者 Model ID 不被支持服务端返回了错误对象。先用 4.1 的 curl 确认返回结构再对照工具配置。5.4 OAuth 相关报错有些工具默认走 OAuth 登录而不是 API Key。如果你看到 OAuth 报错说明工具在尝试用账号登录而不是 Key。在设置里找authMode或useApiKey之类的选项切换成 API Key 模式然后填 TaoToken 的 Key。如果工具不支持切换可能需要换一个支持自定义 Base URL 的版本。5.5 排障对照表报错可能原因处理pre-receive hook declineddevelop 受保护 / 权限不足走 MR 或检查 push_access_levels401 UnauthorizedKey 错误或过期重新生成 Key检查空格local proxy failed工具代理配置错误删除或修正 proxy 字段reading choicesBase URL 或 Model ID 错误用 curl 验证返回结构OAuth 报错工具走 OAuth 而非 Key切换为 API Key 模式model not foundModel ID 拼写错误对照 TaoToken 文档的模型列表排查时建议一次只改一个变量改完立刻验证这样能快速定位是哪一步出的问题。6. 把 Git 推送和 AI 通道一起理顺回到最初的问题gitlab: [remote rejected] pre-receive hook declined在develop分支上出现绝大多数情况是分支保护规则在起作用。你要做的不是反复git push -f而是先确认push_access_levels再决定是走 MR 还是申请权限。新分支推送通常不受保护这是最稳妥的路径。AI 工具这边把 endpoint 统一到 TaoToken 之后Key 管理从「每个工具一个 Key」变成「一个 Key 走所有工具」换模型只改 Model ID。配置时记住三件套Base URL 用https://taotoken.net/apiKey 在控制台生成Model ID 按实际调用的模型填。写完配置先用 curl 验证再让工具发请求能省掉很多来回试错。如果你在配 Cline 或 Codex 的auth.json时拿不准字段可以直接去 TaoToken 的接入文档对照或者用模型对话页面先测通再写进配置。长期做编码和 Agent 的团队可以考虑 Coding Plan把额度集中管理省得每个成员各自充值。最后提醒一句.env和auth.json千万别提交到 Git否则下一次pre-receive hook declined可能就是因为 push rule 检测到了密钥。