第 13 章 Git 工作流与 Code Review
Git 是人类对代码变更保留的最终控制面。Claude Code 的
/commit/pr/code-review/security-review/review把提交与审查的体力活自动化,但人工 diff 审查不可让渡——Claude 说"修复了"不算数,git diff看到的才算数。这一章讲清楚五条命令的真实语义、effort 档取舍、GitHub Actions 集成,以及哪些事绝对不能交给机器。
13.1 结论:五条命令各管一段,人工 diff 是终审
本章涉及的五条命令都是 Bundled Skill(官方随包附带,装好即有,本质是 skill,可被覆盖),它们的语义边界是本章的核心:
| 命令 | 作用对象 | 产生状态变化 | 内置类别 |
|---|---|---|---|
/commit | 当前工作树 | 是,执行 git commit | Bundled Skill |
/pr | 当前分支 | 是,创建 PR | Bundled Skill |
/code-review | 当前 diff | 否(默认)/ 是(--fix) | Bundled Skill |
/security-review | 待提交变更 | 否(只读审查) | Bundled Skill |
/review | GitHub PR(远程) | 否(只读审查,可 --comment) | Bundled Skill |
关键区分:
/code-review审本地 diff——你工作区里还没提交的改动,或当前分支相对main的差异/review审GitHub PR——远程仓库里已存在的 Pull Request,通过ghCLI 拉取
两者对象不同,不要混用。本地改完还没推,用 /code-review;PR 已开、要看别人开的 PR,用 /review。
完整的提交前流水线:
working tree
↓
机械检查(git diff --check / lint / typecheck / test) ← 先机器
↓
Claude 建立变更清单(只读) ← 再模型,语义分组
↓
/code-review + /security-review ← 模型审查
↓
人工 git diff 审查 ← 人类终审,不可让渡
↓
/commit 或 /pr ← 产生状态不要把"生成 commit message"和"执行 git commit"混为一个权限。前者是只读分析,后者产生持久状态。/commit 会直接执行提交,所以它必须排在人工审查之后,而不是之前。
13.2 提交前的机械检查:先机器后模型
在让模型做语义审查前,先清除机器能直接发现的问题。模型推理很贵,不该浪费在尾随空格和冲突标记上。
git diff --check
npm run lint
npm run typecheck
npm testgit diff --check 能发现尾随空格和冲突标记,这类问题不需要消耗模型推理:
$ git diff --check
apps/api/src/routes/user-audit-events.ts:42: trailing whitespace.预期输出:无输出表示干净;有输出则定位到具体行。
验证:修复后重跑,直到 git diff --check 静默。
失败边界:跳过 git diff --check 直接 /commit,会把尾随空格、冲突标记一起写进历史;后续 rebase 或 cherry-pick 时冲突标记会再次浮现。
13.3 让 Claude 先建立变更清单(只读)
进入 Claude Code 后,先让模型只读地建立变更清单,不要修改文件、不要暂存、不要提交:
运行 `git status --short`、`git diff --stat` 和 `git diff`。
不要修改文件,不要暂存,不要提交。
输出按语义分组:
1. 用户可见行为变化
2. 内部重构
3. 测试变化
4. 配置或依赖变化
5. 可疑的无关改动模拟工具输出:
● Bash(git status --short)
M apps/api/src/app.ts
M apps/web/src/pages/UserDetailPage.tsx
M packages/auth/src/can.ts
?? apps/api/src/routes/user-audit-events.ts
?? apps/web/src/components/AuditEventList.tsx
?? packages/contracts/src/audit-event.ts
● Bash(git diff --stat)
9 files changed, 286 insertions(+), 14 deletions(-)Claude 的摘要应该按语义分组,而不是逐文件复述:
Behavior:
- Adds a protected audit-events endpoint.
- Renders audit events on the user detail page.
Authorization:
- Allows admin or self; other users receive 403 before repository access.
Tests:
- Adds role matrix, no-query-on-forbidden, rendering, empty, error cases.
Unrelated:
- package-lock.json changed although no dependency was added.验证:对照 git diff --stat 的文件列表,确认 Claude 没有遗漏文件,也没有把无关改动混进功能描述。最后一项"Unrelated"最重要——package-lock.json 的无关变化应该先调查,而不是跟着功能一起提交。
失败边界:如果 Claude 在这一步就开始 git add 或修改文件,立即中止,重新限定范围。建立清单是只读操作,任何写动作都违反流程。
13.4 /commit:Conventional Commit 自动生成
/commit 是 Bundled Skill,它会基于当前 git diff 生成符合 Conventional Commits 规范的提交信息,然后执行 git commit。
/commit约束规范(由 skill 内置,但可在 prompt 中追加):
type限定为featfixrefactortestdocschoreperfbuildcistylescope使用受影响的产品模块(如usersauthapi)subject使用 imperative mood(祈使句),不超过 72 字符body解释 why,不逐文件复述 what- footer 标注 BREAKING CHANGE、关联 issue
预期输出:
feat(users): add authorized audit event history
Expose paginated audit events to administrators and the target user while
rejecting cross-user access before data lookup. Add the user-detail UI and
cover authorization, empty, error, and rendering states.
[main abc1234] feat(users): add authorized audit event history
9 files changed, 286 insertions(+), 14 deletions(-)如果 diff 同时包含无关依赖升级,Claude 应建议拆分:
Split recommended:
1. feat(users): add authorized audit event history
2. chore(deps): update test tooling
已中止提交,请先拆分改动。验证:提交后跑 git log -1 --stat 确认 commit message、作者、改动文件与审查对象一致。
失败边界:
- 未跑测试就
/commit——/commit不会替你跑测试,它只看 diff。测试挂了照样能提交成功,脏代码就进历史了。务必先跑npm test再/commit。 - 盲目信任 message——Claude 生成的 message 可能漏掉 BREAKING CHANGE,或把
feat误标为fix。提交前看一眼 message,不对就git commit --amend改。 /commit会真实提交——它不是"生成 message 让你手动 commit",而是直接执行。如果你想先看 message 再决定,用下一节的 print mode 方式。
只生成 message 不提交(适合 CI 或谨慎场景):
git diff --cached | claude -p \
--tools "" \
"根据输入的 staged diff 生成 Conventional Commit message,只输出 message"--tools "" 禁用工具,Claude 只能分析 stdin,适合最小权限自动化。人类确认后再手动 git commit。
13.5 /pr:创建 Pull Request
/pr 是 Bundled Skill,基于当前分支相对 base 分支的改动,生成 PR 标题与描述,然后通过 gh CLI 创建 PR。
/pr预期输出:
PR title: feat(users): add authorized audit event history
PR body:
## Summary
- Expose paginated audit events to administrators and the target user
- Reject cross-user access before data lookup
- Add user-detail UI with empty/error/loading states
## Test plan
- [x] Role matrix tests (admin/self/other)
- [x] No-query-on-forbidden test
- [x] Rendering and empty-state tests
- [ ] Manual smoke test on staging
Creating PR...
https://github.com/org/repo/pull/142PR body 通常包含:Summary(改了什么、为什么)、Test plan(验证清单)、关联 issue。gh pr create 执行后返回 PR URL。
验证:gh pr view 142 --web 打开 PR 页面,确认标题、描述、base 分支、 reviewers 配置正确。
失败边界:
- 未推送就
/pr——/pr会先git push -u origin HEAD,如果分支有未推送的 commit,这一步会产生推送动作。如果你不想推送(比如还没审查完),不要跑/pr。 - base 分支选错——
/pr默认用仓库的 default branch 作为 base。如果你要 PR 到develop而不是main,显式指定,否则 PR 会开错目标。 - PR 描述泄露 secrets——Claude 生成 body 时可能把 diff 里的测试 token、内部 URL 写进描述。推送前扫一眼 body。
13.6 /code-review:当前 diff 审查
/code-review 是本章的主力审查命令。它审查当前 diff(工作区未提交改动 + 当前分支相对 base 的差异),支持 effort 档与两个动作选项。
13.6.1 effort 档取舍
/code-review # 默认 medium
/code-review low
/code-review high
/code-review xhigh
/code-review max五档的取舍是少而精 vs 广而杂:
| 档位 | 行为 | 适用场景 |
|---|---|---|
low | 少量、高置信 finding,几乎无误报 | 快速过一遍小改动、临提交前最后一道闸 |
medium | 平衡,默认档 | 日常 feature 开发 |
high | 广覆盖,可能包含中等置信问题 | 重构、安全敏感改动 |
xhigh | 更广,会报告需要进一步确认的疑似问题 | 大型 diff、跨模块影响 |
max | 全覆盖,包含低置信推测 | 关键路径(支付、鉴权、加密)上线前 |
核心取舍:低档少而高置信,高档广覆盖但噪音多。max 档会报告"疑似问题",需要你人工甄别,不要见到 finding 就改。
13.6.2 审查范围限定
只审查某个文件:
/code-review high apps/api/src/routes/user-audit-events.ts只审查某个目录:
/code-review high apps/api/src/routes/范围限定能减少噪音、加快审查,适合大 diff 里只关心某块改动的场景。
13.6.3 --fix:应用修复
/code-review high --fix--fix 会在审查后直接修改工作树,应用高置信度修复。
预期输出:
[High] Cursor accepted without binding to userId
File: apps/api/src/repositories/audit-event-repository.ts:41
Applied fix: bind cursor payload to userId
[Medium] Missing error case for empty cursor
File: apps/api/src/routes/user-audit-events.ts:78
Applied fix: add 400 response for malformed cursor
2 findings, 2 fixed. Re-run tests to verify.验证:--fix 后必须:
git diff看修复内容,确认修的是问题、不是症状- 重跑
npm test与npm run typecheck - 再次
/code-review确认 finding 已消除
失败边界:--fix 会修改工作树。运行前先 git stash 或确认工作区干净(除了待审查的改动),否则修复与你的改动混在一起难以回退。--fix 后必须重跑测试,修复本身可能引入新问题。
13.6.4 --comment:贴 PR 评论
/code-review high --comment--comment 把 finding 作为 inline 评论贴到 GitHub PR 对应的代码行上。需要当前分支已开 PR(gh CLI 能找到)。
预期输出:每条 finding 生成一条 PR inline comment,挂在对应文件行号上。
失败边界:--comment 会向远程 PR 写入评论。如果你在本地还没开 PR,或 PR 是别人的,不要用 --comment——它会失败或贴错地方。
13.6.5 有效 finding vs 无效 finding
一个有效的 review finding 应包含:
[High] Cursor is accepted without binding it to userId
The repository decodes a global cursor and uses its event ID directly.
An authorized user could reuse a cursor from another user and cause the
query to start from an unrelated row. Bind the cursor payload to userId
or include userId in the database predicate.
File: apps/api/src/repositories/audit-event-repository.ts:41无效 finding 通常是:
Consider improving error handling.它没有可触发路径、影响和修复位置,不能直接行动。如果你看到大量这类 finding,说明 effort 档开太高了,降到 medium 或 low。
可以追加 prompt 控制噪音:
只报告能由当前 diff 引入、可给出触发路径、且置信度高的问题。
不要报告纯风格偏好。
按 correctness、security、performance 分类。13.7 /security-review:安全审查
/security-review 专做安全审查,关注攻击面而非代码质量。它对待提交变更做只读审查,不修改文件。
/security-review针对常见 Web 服务,重点检查:
- IDOR:是否只验证"已登录",却没验证目标用户
- 信息泄露:
403404是否暴露用户存在性(timing、消息差异) - 注入:cursor、排序字段、过滤条件是否进入原始 SQL
- 越权缓存:响应是否被跨用户缓存(CDN、浏览器缓存)
- 敏感数据:响应是否包含 token、邮箱、IP 等不应展示字段
- 资源耗尽:分页 limit 是否有上限、是否有无界查询
- 日志注入:用户输入是否直接写入结构化日志(日志伪造、SIEM 注入)
- SSRF:用户控制的 URL 是否被服务端请求
- 密钥泄露:diff 里是否硬编码 token、密钥、连接串
可以追加领域约束:
/security-review
额外检查:
- 任何 member 都不能读取其他用户事件
- 未授权请求不能触发 repository 查询
- API 不能返回 audit payload 中的 metadata.secret
- cursor 必须和 target user 绑定模拟发现:
[Critical] API returns raw metadata from storage
The route maps repository rows with `{ ...row }`, which includes
`metadata.secret`. This field can contain OAuth provider details.
Construct `AuditEventDto` explicitly and return only contract fields.
File: apps/api/src/routes/user-audit-events.ts:55修复方式应是输出白名单:
return {
id: row.id,
actorName: row.actorName,
action: row.action,
createdAt: row.createdAt.toISOString()
};而不是黑名单删除:
delete row.metadata.secret;
return row;白名单能抵御未来数据库新增敏感字段时的意外暴露——新增字段不会自动进白名单,但会自动进 { ...row }。
验证:/security-review 报告的 Critical/High 必须逐条确认修复,或在 prompt 中明确说明为何不修(误报、已由其他层防御)。修复后重跑 /security-review 确认 finding 消失。
失败边界:
/security-review不是渗透测试——它做静态审查,看不到运行时行为、配置、依赖漏洞。生产前仍需跑npm audit、SCA 工具、SAST 扫描。- 领域知识缺失——Claude 不知道你的业务规则("member 不能读其他用户事件"这类约束必须显式告知)。
- 误报与漏报并存——安全审查的 recall 比 precision 重要,宁可多报也别漏。但多报意味着你需要人工甄别,不能见到 finding 就改。
13.8 /review:审查 GitHub PR
/review 审查远程 GitHub PR,与 /code-review 审本地 diff 的语义不同。
/review 142或:
/review https://github.com/org/repo/pull/142/review 通过 gh CLI 拉取 PR 的 diff、metadata、CI 状态、已有评论,做完整审查。适合:
- 审查同事开的 PR(你本地没有这个分支)
- 审查依赖库的 PR(开源贡献 review)
- 在 PR 合并前做最后一道独立审查
预期输出:
PR #142: feat(users): add authorized audit event history
Author: alice, base: main, +286/-14, CI: passing
Findings:
[High] Cursor not bound to userId (audit-event-repository.ts:41)
[Medium] Missing test for empty cursor (user-audit-events.ts:78)
[Low] Typo in error message (AuditEventList.tsx:23)
Overall: Request changes. Address High before merge.可附加选项(视 skill 版本):
/review 142 --comment # 把 finding 贴为 PR inline comment
/review 142 --approve # 审查通过,approve PR
/review 142 --request-changes # 请求修改验证:审查后 gh pr view 142 确认评论已贴、状态已更新。
失败边界:
/review与/code-review混用——本地还没 push 的改动,/review看不到(它看远程 PR);远程别人开的 PR,/code-review也看不到(它看本地 diff)。选错命令会得到空结果或审查错对象。--approve滥用——不要让 Claude 自动 approve PR。approve 是人类的合并决策,涉及业务、依赖、发布窗口判断,Claude 看不到全貌。让 Claude 报告 finding,人类做 approve 决策。- CI 状态盲区——
/review会读 CI 状态,但不会替你跑 CI。如果 PR 的 CI 还在 pending,等 CI 跑完再 review,否则可能漏掉测试失败引入的问题。
13.9 GitHub Actions 中的 Claude:anthropics/claude-code-action
除了在本地用 Claude Code,你还可以把 Claude 集成进 GitHub Actions,让 PR/issue 中的 @claude 触发自动响应。官方 action 是 anthropics/claude-code-action。
13.9.1 典型 YAML 示例
以下是一个完整的 GitHub Actions workflow 示例(标注:示例,需替换 org/repo 与 secrets):
# .github/workflows/claude.yml
# 示例:PR/issue 评论中 @claude 触发 Claude Code 响应
# 核对日期:2026-07-08,anthropics/claude-code-action@v1
name: Claude Code
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
issues:
types: [opened, assigned]
jobs:
claude:
# 仅在评论包含 @claude 时触发
if: |
(github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment')
&& contains(github.event.comment.body, '@claude')
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Run Claude Code
uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
# 可选:指定模型(默认 Sonnet)
# model: claude-sonnet-5
# 可选:限定允许的工具,收紧权限
# allowed_tools: "Read,Grep,Glob,Bash(git diff *)"
# 可选:直接触发而非等 @claude
# direct_trigger: true13.9.2 触发方式
- PR 评论中
@claude:Claude 拉取 PR diff、评论上下文,执行任务(改代码、回答问题、修 bug) - issue 评论中
@claude:Claude 读取 issue 描述与评论,响应 @claude+ 指令:@claude fix the failing test@claude review this PR for security issues
13.9.3 权限收紧
claude-code-action 默认权限较宽(可写 contents、PR、issues)。生产环境务必收紧:
allowed_tools: "Read,Grep,Glob,Bash(git status *),Bash(git diff *)"这样 Claude 只能只读分析,不能直接改代码、不能 push。需要它改代码时,再通过 PR 评论显式授权。
13.9.4 密钥管理(失败边界)
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}关键纪律:
- API key 必须放 GitHub Secrets,绝不能硬编码在 workflow 文件里
- Secrets 不会自动脱敏日志——如果 workflow 把
ANTHROPIC_API_KEYecho 到日志,Secrets 脱敏可能漏掉变体(如 base64、截断)。不要在 step 里打印环境变量 - fork PR 的陷阱——默认情况下,fork PR 触发的 workflow 无法访问 secrets(
pull_request事件来自 fork 时 secrets 不注入)。但issue_comment事件可以。如果你用pull_request触发且允许 fork,务必在 workflow 里加if: github.event.pull_request.head.repo.full_name == github.repository限制只跑自家仓库 GITHUB_TOKEN与ANTHROPIC_API_KEY分离——GITHUB_TOKEN是 GitHub 给的,权限受限;ANTHROPIC_API_KEY是你的 Anthropic 账户 key,花钱。两者都要放 Secrets,不要混用
验证:workflow 跑完后,在 Actions 日志里确认 Claude 的输出符合预期,且日志里没有明文 key。定期在 Anthropic console 查 API 用量,异常飙升说明 key 泄露或被滥用。
失败边界:
- CI 中密钥泄露——这是最高危的失败。一旦 key 进日志、进 PR 评论、进 artifact,立即在 Anthropic console 吊销 key,重新生成。不要"等下再处理"。
@claude被滥用——任何能评论 PR/issue 的人都能触发@claude,消耗你的 API 额度。用if条件限制触发者(如只允许 org member):yamlif: | contains(github.event.comment.body, '@claude') && contains(fromJson('["alice","bob","carol"]'), github.event.comment.user.login)- Claude 直接 push 到 main——
contents: write权限下,Claude 可以直接 push。务必让 Claude 走 PR 流程(改到 feature branch、开 PR),而不是直接 push main。在claude-code-action配置里禁用直接 push,或用 branch protection rule 锁死 main。
13.10 人工 diff 审查:不可让渡的控制面
前面所有自动化——/code-review /security-review /review——都是辅助,不是替代。人类读 git diff 是不可让渡的终审。
为什么?因为 Claude 的审查有已知盲区:
- 看不到运行时行为——它读代码,不跑代码。内存泄漏、竞态、性能退化,静态审查很难发现
- 看不到业务上下文——它不知道这个改动是否符合产品需求、是否破坏了未写文档的兼容性承诺
- 会被精心构造的代码欺骗——变量名误导、注释与实现不符、看似无害的重构实际改变了语义
- effort 档越高噪音越多——
max档会报告大量低置信 finding,你可能疲于甄别而漏掉真问题
人工 diff 审查的标准动作:
# 1. 看工作区改动(未暂存)
git diff
# 2. 看暂存区改动(已 git add)
git diff --cached
# 3. 看当前分支相对 main 的全部改动
git diff main...HEAD
# 4. 看某个文件的改动
git diff apps/api/src/routes/user-audit-events.ts
# 5. 逐行审查(交互式)
git diff --cached -p审查清单:
重新读取最终 git diff,只报告问题,不修改文件:
1. 是否残留调试代码、console.log、TODO、注释掉的代码
2. 是否包含秘密、token、内部 URL、连接串
3. 是否修改了依赖或 lockfile(package-lock.json、yarn.lock、go.sum)
4. 是否缺少失败路径测试(只测了 happy path)
5. 是否有与任务无关的改动(混入的格式化、无关重构)
6. 是否有看似无害但语义变化的改动(如 === 改 ==、同步改异步)
7. 是否删除了错误处理或降级逻辑
8. 是否扩大了权限范围(新增的 endpoint 没鉴权)关键陷阱:审查工作树、却提交另一个 staged diff。这是常见流程漏洞:
# 你审查的是工作区
git diff
# 但提交的是暂存区
git commit
# 两者可能不一致!正确做法:
# 1. 审查工作区
git diff
# 2. 暂存要提交的改动
git add -p # 交互式暂存,逐块确认
# 3. 再次审查暂存区
git diff --cached
# 4. 确认一致后提交
git commit只有在你刚刚审查过的 diff 与 git diff --cached 完全一致时,才提交。
13.11 失败边界汇总
本章所有命令的失败边界,集中在三类:
13.11.1 盲目信任 /code-review
/code-review 报告"无 finding"不等于代码没问题。它可能:
- 漏掉只有运行时才暴露的 bug
- 在
low档下只报高置信问题,中等置信的真问题被过滤 - 被精心构造的代码欺骗(变量名误导、注释不符)
对策:/code-review 是辅助,不是替代。关键路径(支付、鉴权、加密)必须人工逐行审查,且 effort 档开到 high 以上。
13.11.2 未跑测试就 commit
/commit 不会替你跑测试。测试挂了照样能提交成功,脏代码进历史。
对策:提交前流水线固定为:
git diff --check
npm run lint
npm run typecheck
npm test
# 全绿后再 /commit把这套命令写进 Hook(第 14 章)或 CI,让机器强制执行。
13.11.3 CI 中密钥泄露
anthropics/claude-code-action 需要 ANTHROPIC_API_KEY。一旦泄露:
- 攻击者可以冒用你的账户调用 Claude API,产生巨额账单
- 如果 Claude 有仓库写权限,攻击者可以通过
@claude指令让 Claude 改代码、开 PR、甚至 push
对策:
- API key 只放 GitHub Secrets,绝不硬编码
- workflow 里限制
@claude触发者(只允许 org member) - 用
allowed_tools收紧 Claude 权限,默认只读 - branch protection 锁死 main,禁止直接 push
- 定期在 Anthropic console 查用量,异常立即吊销 key
- fork PR 不触发带 secrets 的 workflow
13.11.4 其他边界
/commit会真实提交——不是生成 message 让你手动 commit。想先看 message,用 print mode/pr会真实推送 + 开 PR——不想推送就别跑--fix会修改工作树——跑前确认工作区状态,跑后重跑测试--comment会写远程 PR——本地没 PR 或 PR 是别人的,不要用/review --approve滥用——approve 是人类合并决策,不让 Claude 自动 approve- effort 档过高——
max档噪音多,疲于甄别反而漏掉真问题。日常用medium,关键路径用high/xhigh
13.12 小结
Git 是人类对代码变更的最终控制面。Claude Code 的五条命令把提交与审查的体力活自动化:
/commit——生成 Conventional Commit 并执行提交/pr——基于改动创建 Pull Request/code-review——审查当前 diff,支持 effort 档、--fix、--comment/security-review——对待提交变更做安全审查/review——审查远程 GitHub PR(与/code-review审本地 diff 的语义区别)
GitHub Actions 中的 anthropics/claude-code-action 把 Claude 集成进 CI,PR/issue 中 @claude 触发,但密钥管理与权限收紧是不可妥协的纪律。
核心心法不变:你做架构决策和关键审查,Claude 做实现和探索。提交前的最后一步,永远是 git diff 人工审查,而不是 Claude 的一句"已修复"。
上一章:测试与质量保障
下一章:Hooks 与自动化