git push --mirror 报 pre-receive hook declined:把 remote 改到 TaoToken 后如何排查 1. 镜像推送被 pre-receive hook declined 卡住先看清报错到底在拒绝什么git push --mirror报pre-receive hook declined本质不是网络问题也不是 Key 失效而是目标仓库的服务端钩子在收到你推送的引用ref之后主动拒绝了这次更新。--mirror的特殊之处在于它会把本地镜像仓库里的所有 ref原样推过去包括refs/heads/*、refs/tags/*、refs/remotes/*甚至一些临时分支和已删除分支的引用。只要其中任意一个 ref 触发服务端规则整批推送就会被整体拒绝报错信息里通常只给你一行pre-receive hook declined具体原因藏在服务端返回的提示里。我这次遇到的场景很典型用git clone --mirror把源仓库拉到本地再git push --mirror推到目标仓库脚本跑了几个月都正常某天突然全部分支被拒提示里出现了The keywords cherry-pick and revert are branch names reserved by CodeHub, You are not allowed to create.。这句话才是真正的线索——目标平台把cherry-pick和revert这两个名字保留为系统分支名不允许用户创建。而源仓库里恰好存在一个合并期间产生的临时分支名字里带了这类关键字镜像推送时被一并带过去于是整批被拒。所以排查git push --mirror的pre-receive hook declined核心思路是不要盯着「推送失败」这个结果而要找出是哪个 ref 触发了钩子。方向有三个——remote 地址是否指向了正确的目标、目标仓库的分支保护与命名规则、以及本地镜像里是否存在大文件或异常提交历史。这篇就按这条链路把每一步可复制的命令和验证动作写清楚同时说明在统一 Key/API 通道下怎么把 remote 配置对避免把问题误判成鉴权失败。适合谁看正在做仓库迁移、周期性镜像同步、或者用脚本批量push --mirror的开发和运维同学。如果你只是普通git push origin main报这个错思路也通用只是--mirror会把问题放大到所有 ref。先明确一个概念pre-receive hook是服务端在接收推送前执行的脚本它拿到的是「一批 ref 更新」可以整体接受或整体拒绝。它拒绝的理由五花八门分支名保留、分支保护、提交信息格式、文件大小限制、禁止强推、禁止删除分支等。--mirror因为一次性推所有 ref命中任意一条规则的概率远高于单分支推送。理解这一点后面的排查就不会跑偏。2. 把 remote 改到 TaoToken 通道前先确认地址、Key 与模型标识三件套很多人一看到pre-receive hook declined就以为是鉴权问题跑去重新生成 Key结果发现根本不是。这里要先区分两类失败一类是连接/鉴权层失败比如401、could not read Username、local proxy failed另一类是服务端钩子层失败也就是本文的pre-receive hook declined。前者是「你进不去门」后者是「你进去了但门卫不让搬东西」。把 remote 改到统一通道时先保证第一层是通的再去查第二层。在 TaoToken 这套统一 Key/API 通道下做镜像推送remote 地址、Key、模型标识这三件套要一次性配全缺一个都会在后续排查时干扰判断。Git 走的是 HTTPS 传输remote URL 里可以直接带上访问凭据也可以用凭据管理器。我建议在脚本里用环境变量注入避免把 Key 写死在仓库配置里。先看 remote 的配置方式。假设你的目标仓库通过统一通道暴露的 Git 地址是https://taotoken.net/api/your-org/your-repo.git那么可以这样设置# 查看当前 remote确认没有指向旧地址 git remote -v # 移除旧的 origin如果是镜像仓库通常不需要 origin 指向源 git remote remove origin # 添加目标 remote命名清晰避免和源混淆 git remote add target https://taotoken.net/api/your-org/your-repo.git # 再次确认 git remote -v如果目标平台要求用 Token 作为密码可以在 URL 里带上用户名和 Token或者用 Git 凭据存储。注意不要把真实 Key 提交进任何仓库文件。更稳妥的做法是用git config的credential.helper或者环境变量# 临时在本次会话注入凭据示例替换为你的实际 Key export GIT_TOKENyour_taotoken_key_here # 用带凭据的 URL 推送仅当前命令生效不写入配置 git push https://user:${GIT_TOKEN}taotoken.net/api/your-org/your-repo.git --mirror三件套里的「模型标识」在纯 Git 操作里不直接出现但如果你是用某个 AI 编码工具比如 Claude Code、Cline、Codex 这类来生成或执行迁移脚本那它的配置文件里同样需要 Base URL、Key、Model ID 三项对齐。以常见的settings.json或auth.json为例配置片段长这样{ base_url: https://taotoken.net/api, api_key: your_taotoken_key_here, model: your-model-id }如果你用的是 TOML 形式的配置部分工具用config.tomlbase_url https://taotoken.net/api api_key your_taotoken_key_here model your-model-id这里要强调Base URL 用https://taotoken.net/api不要带多余的路径后缀Key 从控制台的 API Keys 页面获取Model ID 按你实际要调用的模型填写。三件套对齐之后工具侧的请求才不会出现401或model not found。而 Git 镜像推送本身走的是 Git 协议和模型调用是两条链路排查时要分开看别把 Git 的钩子拒绝误当成 Key 问题。配置完成后先做一次最小连通性验证不要直接上--mirror# 只拉取远程引用列表验证地址和凭据是否可用 git ls-remote target # 如果上面能列出 refs说明连接和鉴权没问题 # 再尝试推送单个分支验证钩子层 git push target main如果git ls-remote target能正常返回引用列表但git push target main报pre-receive hook declined那问题就锁定在服务端钩子规则而不是地址或 Key。这一步的区分非常关键能帮你省掉大量无效的重新配置。3. 可复制的镜像推送配置与复现命令定位到具体被拒的 ref排查--mirror被拒第一步是复现并拿到完整报错。很多脚本把 stderr 吞掉了只留下一句pre-receive hook declined看不到服务端返回的具体原因。所以先把推送命令单独拎出来手动跑一遍让完整输出打出来。# 进入镜像仓库目录 cd /tmp/git_mirror_migration_xxx/repo_name # 查看本地镜像里到底有哪些 ref这是关键 git show-ref # 分类查看分支和标签 git branch -a git tag -l # 手动执行镜像推送保留完整输出 git push --mirror target 21 | tee push_error.loggit show-ref会列出所有引用包括你可能没注意到的临时分支、已删除但引用还在的分支、以及refs/remotes/*。--mirror会把这些全部推过去而普通git push默认只推当前分支。这就是为什么单分支推送正常、--mirror却全被拒。拿到完整报错后对照服务端提示定位。我这次的关键字是cherry-pick和revert被保留于是去本地找带这些名字的分支# 查找名字里包含关键字的 ref git show-ref | grep -iE cherry-pick|revert|tmp # 或者更宽泛地列出所有分支名 git for-each-ref --format%(refname) refs/heads/找到可疑分支后确认它是不是源仓库里遗留的临时分支。合并请求期间平台常会生成tmp-开头的临时分支合并完成后如果没清理镜像克隆就会把它带过来。确认无误后在本地镜像仓库里删除它# 删除本地镜像中的临时分支 git branch -D tmp-branch-xxxx # 再次确认已删除 git show-ref | grep -i tmp删除后不要急着--mirror先用精确的 refspec 推送缩小影响范围也方便观察# 只推分支不推 remotes 和其他引用 git push target refs/heads/*:refs/heads/* # 如果分支推送成功再单独推标签 git push target refs/tags/*:refs/tags/*这里表示允许非快进更新镜像场景下通常需要。如果refs/heads/*推送成功说明钩子拒绝的确实是那个临时分支问题解决。如果仍然被拒就继续用git show-ref逐个排除或者用--dry-run预演# 预演推送不实际写入观察哪些 ref 会被处理 git push --mirror --dry-run target--dry-run不会触发真正的钩子校验但能帮你确认推送范围。真正定位钩子拒绝原因还是要靠服务端返回的提示文字。如果服务端只返回pre-receive hook declined而不给细节可以尝试推送单个 ref 来二分定位# 逐个推送可疑分支看哪个触发拒绝 git push target refs/heads/suspicious-branch:refs/heads/suspicious-branch把范围从「全部」缩小到「单个」是排查钩子类问题最有效的手段。下面这张表把常见触发点和对应动作列出来方便对照触发点典型报错关键字排查动作分支名被保留reserved / not allowed to creategit show-ref找关键字分支并删除分支保护protected branch检查目标仓库保护规则改用允许的分支大文件超限file size / exceeds limit用git rev-list --objects找大文件提交历史异常commit message / hook检查提交信息格式是否符合规范禁止删除分支deletion / deny确认镜像里是否有已删除分支的残留引用这张表建议收藏下次遇到pre-receive hook declined直接按行排查比盲目重试快得多。4. 验证推送成功与钩子校验通过从 ls-remote 到分支比对删掉问题分支、用精确 refspec 推送之后怎么确认钩子真的通过了、镜像真的完整不能只看命令返回 0要做几项验证。第一项确认远程引用列表和本地镜像一致。推送完成后对比本地和远程的 ref# 本地所有 ref git show-ref | sort local_refs.txt # 远程所有 ref git ls-remote target | awk {print $2} | sort remote_refs.txt # 对比差异 diff local_refs.txt remote_refs.txt如果两边一致说明镜像推送完整。注意git ls-remote返回的是hash refname格式上面用awk取了第二列。如果只想看分支可以加过滤git ls-remote --heads target git ls-remote --tags target第二项验证关键分支的提交哈希是否一致。镜像推送的核心是「内容一致」哈希一致才算真正同步# 本地某分支的哈希 git rev-parse refs/heads/main # 远程同名分支的哈希 git ls-remote target refs/heads/main两个哈希相同说明该分支内容完全一致。对几个核心分支都做一遍比只看推送成功更可靠。第三项如果目标平台有钩子校验日志或 Web 界面去确认这次推送是否被记录为「通过」。有些平台的钩子会在提交上打标记或者要求提交信息符合特定格式。如果推送成功但后续 CI 没触发可能是钩子通过了但其他规则没满足这时要回到目标平台的仓库设置里看。第四项针对周期性镜像同步的场景建议在脚本里加一段校验逻辑而不是推完就删临时目录。比如# 推送后校验远程分支数量是否与本地一致 LOCAL_COUNT$(git show-ref --heads | wc -l) REMOTE_COUNT$(git ls-remote --heads target | wc -l) if [ $LOCAL_COUNT -eq $REMOTE_COUNT ]; then echo 校验通过本地与远程分支数量一致 ($LOCAL_COUNT) else echo 校验失败本地 $LOCAL_COUNT 个分支远程 $REMOTE_COUNT 个分支 fi这段逻辑能帮你及早发现「推送返回成功但实际没同步全」的情况。我踩过的坑就是脚本只看退出码结果某个分支被钩子静默跳过直到几天后才发现目标仓库缺分支。另外如果目标仓库启用了分支保护--mirror推送已存在的受保护分支时即使内容一致也可能被拒。这时要么调整保护规则要么改用精确 refspec 只推需要同步的分支。验证阶段可以用git push --dry-run先预演确认没有 ref 会被拒再正式推。最后把验证动作固化进脚本每次同步后自动比对比人工检查靠谱。镜像同步这种周期性任务最怕的就是「这次成功、下次失败」却没人发现。5. 本篇常见错排查401、local proxy failed、reading choices 与 OAuth 报错对照排查过程中除了pre-receive hook declined还会遇到几类容易混淆的报错。把它们放在一起对照能避免把钩子问题误判成鉴权或网络问题。401 Unauthorized这是鉴权层失败说明 Key 无效、过期或没带上。检查 remote URL 里的凭据、环境变量GIT_TOKEN是否注入成功、以及 Key 是否从正确的控制台页面获取。注意 Git 的凭据和模型调用的 Key 可能是两套别混用。如果用的是统一通道确认 Base URL 是https://taotoken.net/api路径拼错也会导致 401。local proxy failed这类报错通常出现在工具侧发起请求时表示本地代理配置有问题。检查环境变量HTTP_PROXY、HTTPS_PROXY是否指向了不可用的地址或者工具的代理设置是否和实际网络环境冲突。Git 场景下可以用git config --global --get http.proxy查看是否残留了代理配置必要时清掉git config --global --unset http.proxy git config --global --unset https.proxyreading choices / choices 相关报错这类报错多出现在调用模型接口时返回体解析失败常见原因是 Base URL 或 Model ID 配错导致返回的不是预期的 JSON 结构。检查三件套是否对齐尤其是 Model ID 是否拼写正确、是否是该通道支持的模型。用curl直接打一次接口验证curl -s https://taotoken.net/api/v1/models \ -H Authorization: Bearer ${GIT_TOKEN} | head -c 500如果返回的是 HTML 或错误页说明地址不对返回 JSON 模型列表说明通道正常。OAuth 相关报错部分工具用 OAuth 流程获取凭据如果回调地址、客户端配置不对会卡在授权环节。这类问题通常和 Git 镜像推送无关属于工具侧配置。检查工具的 OAuth 配置是否指向正确的回调以及授权后拿到的 Token 是否写入了正确的配置文件如auth.json。把这几类报错和pre-receive hook declined区分开的关键是看报错发生在哪一层。连接层、鉴权层、钩子层、应用层每层的排查手段不同。下面这张对照表帮你快速定位报错所在层首要排查动作401 Unauthorized鉴权层检查 Key、Base URL、凭据注入local proxy failed网络/代理层清理代理配置确认网络可达reading choices应用层核对 Base URL 与 Model IDOAuth 报错授权层检查回调地址与 Token 写入pre-receive hook declined服务端钩子层用 show-ref 定位被拒 ref排查顺序建议从下往上先确认连接和鉴权通再看钩子最后看应用层。这样不会在错误的层反复折腾。还有一个容易忽略的点--mirror推送时如果本地镜像仓库是旧的里面可能残留了源仓库已经删除的分支引用。这些「幽灵引用」在推送时会被带到目标触发钩子。定期用git remote prune或重新git clone --mirror可以避免。如果目标平台对分支命名有保留字规则最好在源仓库侧就规范分支命名从源头减少被拒概率。6. 统一通道下的镜像推送收尾把排查动作变成脚本里的固定步骤把上面的排查链路固化下来下次再遇到pre-receive hook declined就不用从头查。核心是把「定位被拒 ref」这一步自动化而不是每次手动git show-ref翻。一个可复用的收尾流程是这样的推送前先git show-ref导出本地引用过滤掉已知的保留字分支推送时用精确 refspec 而不是无脑--mirror推送后比对本地和远程引用数量与哈希。这三步写进脚本能挡住大部分钩子拒绝。# 推送前过滤保留字分支生成待推送列表 git for-each-ref --format%(refname) refs/heads/ \ | grep -viE cherry-pick|revert|tmp- push_refs.txt # 推送时按列表逐个推失败即停方便定位 while read -r ref; do git push target ${ref}:${ref} || { echo 被拒: $ref; break; } done push_refs.txt如果确实需要完整镜像可以在删除问题分支后用git push --mirror再跑一次但推送前务必确认本地没有残留的异常引用。对于周期性同步任务建议每次同步前重新git clone --mirror而不是复用旧镜像这样能避免历史残留引用累积。统一 Key/API 通道的价值在于Git 传输和工具调用可以共用一套凭据管理减少配置分散带来的排查成本。但要注意 Git 协议和模型 API 是两条链路配置时分别验证。Git 侧用git ls-remote验证连通工具侧用curl或工具自带的连通性检查验证。最后给一个实用技巧把服务端返回的完整报错保存到日志并在脚本里对pre-receive hook declined做特殊处理自动触发git show-ref输出方便事后分析。很多钩子拒绝的原因就藏在服务端返回的那几行文字里别让脚本把它们吞掉。如果你在配置 remote 或获取 Key 时需要参考可以从 API Keys 页面拿到凭据接入细节看接入文档想先验证模型通道是否正常可以用模型对话页面直接试如果是长期做编码或 Agent 类任务Coding Plan 会更合适。把地址、Key、Model ID 三件套对齐Git 镜像推送和工具调用就都能在一条通道上跑顺。