ccusage 仓库 Git Push 安全流程:分支检查、上游确认与 pre-push 钩子全解析
【免费下载链接】ccusagenpx ccusage项目地址: https://gitcode.com/gh_mirrors/cc/ccusage
本文以 ccusage 仓库内.agents/skills/commit/references/push.md这一技能参考文档为主体,完整梳理"提交后推送"这一环节的规范操作:如何在推送前确认当前分支、如何判断是否存在上游分支、何时需要git push -u并征求用户同意,以及推送触发的一组 pre-push 钩子(treefmt、gitleaks、oxlint、clippy、Node 测试与 Cargo 测试)对变更质量的实际把关。读完本文,你将能安全地在 ccusage 这类"PR 分支 + squash 合并"的工作流中完成每次推送,并在钩子失败时按规范修复重推。
为什么推送之前要先做两道检查
ccusage 仓库的提交与推送流程有一套明确约定:所有变更最终通过 Pull Request 进入main,PR 分支在合并时采用 squash-merge。这一点在 commit 技能主文档 中写得很清楚:
Changes reach
mainthrough a PR, so commit on a feature branch;references/push.mdhas the branch and upstream checks.
也就是说,常规开发不在main/master上直接提交并推送,而是先切到功能分支。因此在git push之前必须回答两个问题:
- 当前在哪个分支?如果在
main或master上,需要先停下,把工作移到功能分支再推送。 - 这个分支有没有配置上游(upstream)?有上游直接
git push;没有上游则不能擅自执行git push -u origin HEAD,而必须先询问用户、获得同意后才执行。
这两道检查正是 push 参考文档的核心,也是本文接下来逐条拆解的对象。
第一步:用git branch --show-current确认当前分支
push 参考文档给出的第一条命令是:
git branch --show-current这条命令只输出当前所在分支的名字(例如feat/codex-report),与git branch的列表输出或git rev-parse --abbrev-ref HEAD相比更简洁、更适合在脚本和技能流程中直接取用。执行后存在两种需要处理的情形:
- 输出为
main或master:立即停止推送,先把当前工作移到功能分支,例如git switch -c feat/your-change后重新组织提交,再回到推送流程。 - 输出为功能分支名:继续下一步检查。
结合 commit 技能主文档 的约定,仓库本身也建议用小的后续提交(follow-up commits)迭代而非反复 amend——因为 PR 分支是 squash 合并,最终落入main的提交信息由 PR 标题决定,功能分支上的提交粒度服务于可审查性,而不是最终历史。因此在main上直接堆积提交并推送,既违背仓库约定,也会让后续基于 PR 的审查与合并变得混乱。
第二步:用git rev-parse检查上游分支是否存在
确认分支无误后,push 参考文档要求检查该分支是否已配置上游:
git rev-parse --abbrev-ref --symbolic-full-name @{u}逐段拆解这条命令的作用:
@{u}是@{upstream}的简写,指向当前分支配置的上游分支;--symbolic-full-name要求输出符号引用名(如refs/remotes/origin/feat/x)而不是解析后的提交哈希;--abbrev-ref把引用名简写为人类可读的形式(如origin/feat/x)。
执行结果对应两种分支走向:
| 结果 | 含义 | 后续动作 |
|---|---|---|
正常输出origin/<分支名> | 上游已存在 | 直接执行git push |
报错(fatal: no upstream configured之类) | 无上游 | 询问用户是否执行git push -u origin HEAD,用户拒绝则跳过推送 |
值得强调的是"询问用户"这一步:push 参考文档明确要求"No upstream: ask the user before runninggit push -u origin HEAD, and skip the push if they decline"。git push -u origin HEAD不仅推送当前提交,还会把本地分支与origin的对应分支建立跟踪关系(这也是后续 PR 的基础),属于会改变远程状态的操作,因此规范将其设为"必须征得同意"的步骤,而不是 Agent 的自动动作。
两种推送路径的执行细节
综合 push 参考文档,完整的推送决策可以概括为下面的流程:
git branch --show-current—— 确认不在main/master;git rev-parse --abbrev-ref --symbolic-full-name @{u}—— 确认上游状态;- 上游存在 →
git push; - 无上游 → 询问用户,同意则
git push -u origin HEAD,拒绝则跳过。
其中第 4 步的"询问"语义值得注意:它意味着在无上游分支的首次推送场景下,技能不允许静默推进,而是把是否建立远程跟踪分支的选择权交还给用户。这与 create-pr 技能的 open-pr 参考文档 中git push -u origin <branch-name>的用法前后衔接——推送建立上游后,下一步就可以基于该分支创建 PR。
推送触发的 pre-push 钩子:六道质量闸门
推送并不是"把提交发出去"就结束。ccusage 仓库通过 nix/git-hooks.nix 定义了完整的 pre-push 钩子集,git push会按顺序触发它们。commit 技能主文档对此的描述是:
Push once every commit is in place and let the hooks in
nix/git-hooks.nixrun — treefmt and gitleaks on commit; treefmt, gitleaks, oxlint,clippy -D warnings, node test, and cargo test on push.
也就是说,commit 阶段跑 treefmt 与 gitleaks(针对暂存内容),而push 阶段会跑六项,在 nix/git-hooks.nix 中对应以下钩子:
| 钩子名称 | 实际命令 | 作用 |
|---|---|---|
ccusage-treefmt-check | treefmt --fail-on-change | 全树格式化校验,任何未格式化差异都会导致推送失败 |
gitleaks-detect | gitleaks detect --config .gitleaks.toml | 扫描全部文件,防止密钥/令牌等敏感信息被推送到远程 |
ccusage-oxlint-check | oxlint . | 对.ts/.tsx/.js/.jsx/.mjs/.cjs文件做 lint |
ccusage-clippy | cargo clippy --workspace --all-targets -- -D warnings | Rust 工作区全目标检查,把警告升级为错误 |
node-test | node --test apps/ccusage/src/cli.test.ts nix/tools/models-dev-gen/compact.test.ts | 运行 Node 内置测试(TypeScript 包与工具测试) |
cargo-test | cargo test --workspace --all-targets | 运行整个 Rust 工作区测试 |
其中 cargo 相关钩子只在rust/路径发生变更时触发(通过files规则限定),oxlint 与 node-test 只在 JS/TS 文件变更时触发,而 treefmt-check 与 gitleaks-detect 是全量执行。这些钩子失败是"正常的验证过程的一部分",push 参考文档与 commit 技能的处理一致:不修改已推送的提交,而是新建一个小提交修复问题,再重新推送。这正是前文提到的"PR 分支 squash 合并 + 小步后续提交"工作流的延伸——push 失败修复也遵循同一套小提交哲学。
推送前的提交规范:scope 校验如何影响 push
推送的质量闸门并不只在 pre-push 阶段。ccusage 在commit-msg阶段运行 scripts/validate-commit-scope.nu,由 Nushell 脚本校验提交主题(subject)是否符合 Conventional Commits 且 scope 与暂存文件所属的 agent 一致。这个约束会在你推送之前就拦截掉不合规的提交信息:
- 变更落在
rust/adapters/<agent>/下时,scope 必须是该 agent 名(如fix(kimi))、跨领域 scope(deps、release、pricing、revert),或跨多个 agent 时的工作区 scope(adapter、all、rust); rust/adapters/common/派生为adapter而非common;- 其他路径不派生 scope,提交者的选择不受约束。
由于 squash 合并会把 PR 标题写成落入main的提交主题,CONTRIBUTING.md 和 open-pr 参考文档 都指出同一套 scope 规则会被 CI 用于校验 PR 标题。因此,推送前把提交主题写对,等于提前为 PR 标题合规打好了基础。
推送失败的常见处理:结合 patch 与钩子修复
推送被钩子拦下时,规范的响应是修复后以新提交重推,而不是改写历史。若钩子失败暴露出的是暂存区/补丁层面的问题(例如某个 hunk 应用不上),commit 技能的 git-apply 参考文档 提供了配套的补丁式暂存手段:
git apply --check patch_file.patch # 先验证补丁能否干净应用 git apply --cached -v patch_file.patch # 只暂存不进工作树,-v 报告被拒绝的 hunk以及不干净应用时的兜底参数:--whitespace=fix(尾随空白)、--reject(仅冲突 hunk 落盘为.rej)、--ignore-whitespace/--ignore-space-change(上下文与行尾差异)、--reverse(撤销已应用的补丁)。
把两条参考文档连起来看,ccusage 的推送流程形成了完整的闭环:提交阶段用 patch 精确暂存并让commit-msg钩子校验 scope →推送前确认分支与上游 →推送时由六道 pre-push 钩子做全量验证 →失败时新建小提交修复重推 →成功后基于上游分支创建 PR,由 CI 以同一套 scope 规则校验 PR 标题。
结语:一条可复用的安全推送检查清单
把 push 参考文档沉淀为日常可执行的清单,每次推送前对照执行即可:
# 1. 确认不在 main/master 上 git branch --show-current # 2. 确认上游存在 git rev-parse --abbrev-ref --symbolic-full-name @{u} # 3a. 有上游:直接推送,让 pre-push 钩子把关 git push # 3b. 无上游:先询问用户,同意后才执行 git push -u origin HEAD这套流程的价值在于把"推送到远程"从一次性的命令执行,升级为"分支策略确认 + 上游状态检查 + 钩子质量验证 + 失败后小步修复"的完整规范,既适用于 ccusage 仓库的 Agent 技能调用(/commit push=true),也可以直接迁移到其他采用 PR 分支与 squash 合并工作流的项目。
【免费下载链接】ccusagenpx ccusage项目地址: https://gitcode.com/gh_mirrors/cc/ccusage
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考