> ## Documentation Index
> Fetch the complete documentation index at: https://aicoding.cscitech.top/llms.txt
> Use this file to discover all available pages before exploring further.

# 02-Git与GitHub审查

> 从 status、diff、branch、commit 到 Pull Request、gh CLI、审查评论闭环、冲突处理、权限边界和回滚，建立可验收的 Git 与 GitHub 协作流程。

## 你将完成什么

本页把一次 Git/GitHub 协作拆成可检查的步骤：确认位置，查看状态和差异，建立分支，提交小改动，创建 PR，处理 review 评论，解决冲突，最后按权限和回滚规则交付。

读完后，你应该能够：

* 分清工作区、暂存区和 `HEAD` 的差异；
* 使用 `status`、`diff`、`branch`、`commit` 完成一次任务；
* 使用 `gh` 创建、查看、检查和评论 PR；
* 让每条审查意见都有修改、验证、回复和复查；
* 处理 merge/rebase 冲突并在必要时中止；
* 区分 `restore`、`revert` 和发布系统回滚；
* 守住 push、force-push、主干 merge、权限和生产操作红线。

示例使用 macOS、Linux 和 Git Bash。Windows PowerShell 的路径和环境变量写法以本机为准；Git 或 `gh` 版本有差异时，先运行 `git --help`、`gh --help` 和对应子命令帮助。

## 01 先建立边界

Git 动作的影响范围不同。读状态通常只读，commit 改变本地历史，push 改变远端分支，merge、权限变更和发布会影响团队或用户。

| 动作                    | 影响       | 默认策略               |
| --------------------- | -------- | ------------------ |
| `status`、`diff`、`log` | 本地只读     | 可直接执行              |
| `add`、`commit`        | 本地索引和历史  | 看完 staged diff 再执行 |
| `fetch`               | 本地远程跟踪引用 | 可执行，不改工作文件         |
| `push`                | 远端分支     | 核对远端和分支后执行         |
| 创建 PR、请求 review       | 协作记录     | 核对 base/head 和内容   |
| merge、删除分支            | 共享历史     | 有负责人明确放行           |
| `push --force`        | 改写远端历史   | 默认禁止               |
| 生产、权限、密钥操作            | 外部系统     | 使用专门审批和回滚          |

进入项目先回答：当前目录是否正确？当前分支是否正确？是否有别人的未提交改动？本次命令是否会写远端或影响外部系统？

建议在项目 `AGENTS.md` 或贡献指南中写入测试命令、分支命名、commit 格式、PR 要求和禁止自动 push/merge/force-push 的规则。持久规则写文件，一次性范围写当前任务。

## 02 `status`：先确认你在哪里

```bash theme={null}
pwd
git rev-parse --show-toplevel
git remote -v
git branch --show-current
git status --short --branch
```

预期输出：

```text theme={null}
/work/acme-shop
/work/acme-shop
origin  git@github.com:acme/shop.git (fetch)
origin  git@github.com:acme/shop.git (push)
feat/cart-total
## feat/cart-total...origin/feat/cart-total [ahead 1]
 M src/cart/total.ts
?? tests/cart/total.test.ts
```

`rev-parse` 确认仓库根目录；`remote -v` 确认真实远端；`branch --show-current` 确认分支；`ahead 1` 表示本地比远程跟踪分支多一个提交。

`status --short` 的两个字符分别表示暂存区和工作区：

| 标记              | 含义       |
| --------------- | -------- |
| ` M file`       | 只改了工作区   |
| `M  file`       | 已暂存      |
| `MM file`       | 暂存后又继续修改 |
| `A  file`       | 新文件已暂存   |
| `?? file`       | 未跟踪文件    |
| `D  file`       | 文件被删除    |
| `R  old -> new` | 被识别为重命名  |

不认识的改动先记录和确认归属。不要为得到“干净状态”直接运行 `git restore .`、`git clean -fd` 或覆盖式复制，它们可能删除未提交工作。

任务开始时建议记录：

```text theme={null}
仓库：/work/acme-shop
分支：feat/cart-total
基线：origin/main
允许修改：src/cart、tests/cart
禁止动作：push、merge、删除文件
```

## 03 `diff`：看清提交会包含什么

`git diff` 是工作区相对暂存区，`git diff --cached` 是暂存区相对 `HEAD`，`git diff HEAD` 包含暂存和未暂存改动。

```bash theme={null}
git diff
git diff --cached
git diff HEAD
git diff --stat
git diff --name-only
git diff --check
git diff -- src/cart/total.ts tests/cart/
```

预期摘要：

```text theme={null}
 src/cart/total.ts        | 5 ++++-
 tests/cart/total.test.ts | 18 ++++++++++++++++++
 2 files changed, 22 insertions(+), 1 deletion(-)
```

预期补丁：

```diff theme={null}
-  return items.reduce((sum, item) => sum + item.price, 0);
+  return items.reduce((sum, item) => {
+    if (item.quantity <= 0) return sum;
+    return sum + item.price * item.quantity;
+  }, 0);
```

读 diff 的顺序是：文件范围、无关格式化、逻辑是否对应需求、异常和边界、权限和数据泄露、测试是否覆盖。`git diff --check` 无输出通常表示没有空白错误，但它不能证明逻辑正确。发现 trailing whitespace、冲突标记或密钥时先停止。

任务范围清楚时按路径暂存：

```bash theme={null}
git add src/cart/total.ts tests/cart/total.test.ts
git status --short
git diff --cached --stat
git diff --cached --check
git diff --cached
```

混杂文件只暂存部分 hunk：

```bash theme={null}
git add -p src/cart/total.ts
```

不要把 `git add .` 当默认提交策略。它可能带入 `.env`、生成物、调试输出和别的任务。

## 04 分支：一个任务一条边界

先更新远端引用，再从最新主干创建分支：

```bash theme={null}
git fetch --prune origin
git switch main
git pull --ff-only origin main
git switch -c feat/cart-total
```

预期：

```text theme={null}
From github.com:acme/shop
   0b4c2de..7f91a20  main -> origin/main
Switched to a new branch 'feat/cart-total'
```

也可以直接从远程引用创建：

```bash theme={null}
git fetch origin
git switch -c feat/cart-total origin/main
```

推荐命名：

```text theme={null}
feat/SHOP-142-cart-total
fix/API-88-timeout
refactor/auth-token-parser
chore/update-eslint
```

检查分支：

```bash theme={null}
git branch --show-current
git status --short --branch
git log --oneline --decorate --graph --all -12
git merge-base --is-ancestor origin/main HEAD && echo "based on origin/main"
```

预期：

```text theme={null}
feat/cart-total
based on origin/main
```

不要让两个任务共享一条功能分支。并行开发使用独立 worktree；同一分支同一时间只能在一棵 worktree 中检出。用 `git worktree list --porcelain` 查看占用关系，不要修改 `.git/worktrees` 绕过保护。

## 05 Commit：保存可解释的工作单元

一个 commit 应有单一目的、有限范围、可复现验证和清晰消息。提交前：

```bash theme={null}
git status --short --branch
git diff --cached --stat
git diff --cached --check
git diff --cached --name-only
pnpm test --filter cart
pnpm typecheck
```

测试命令以项目已有脚本为准，不要猜命令。失败时保留完整输出，先修复再重新查看 diff。

提交并检查：

```bash theme={null}
git commit -m "fix: calculate cart total by quantity"
git show --stat --oneline --decorate HEAD
git status --short --branch
```

预期：

```text theme={null}
[feat/cart-total 3c81a7f] fix: calculate cart total by quantity
 2 files changed, 22 insertions(+), 1 deletion(-)
3c81a7f (HEAD -> feat/cart-total) fix: calculate cart total by quantity
## feat/cart-total
```

提交信息可采用：

```text theme={null}
<type>: <summary>

why this change is needed

Refs: SHOP-142
```

常见 type 有 `feat`、`fix`、`refactor`、`test`、`docs`、`chore`。测试、实现和文档若意图不同，拆成独立 commit；这样 review 和回滚都更精确。

## 06 Push 前检查与红线

Push 前检查目标、分支、差异和敏感文件：

```bash theme={null}
git status --short --branch
git remote get-url origin
git log --oneline --decorate origin/main..HEAD
git diff --stat origin/main...HEAD
git diff --check origin/main...HEAD
git diff --name-only origin/main...HEAD
```

确认当前不是受保护主干，远端是预期仓库，提交属于当前任务，没有秘密、客户数据、生成物和无关锁文件，测试已经通过。

首次推送：

```bash theme={null}
git push --set-upstream origin feat/cart-total
```

预期：

```text theme={null}
 * [new branch]      feat/cart-total -> feat/cart-total
branch 'feat/cart-total' set up to track 'origin/feat/cart-total'.
```

推送后确认：

```bash theme={null}
git fetch origin
git status --short --branch
git log -1 --oneline --decorate origin/feat/cart-total
```

网络超时不要立即重复 push。先查询远端分支和 SHA，避免把已成功的操作误判为失败。

以下命令默认禁止：

```bash theme={null}
git push --force
git push -f
git push --force-with-lease
```

`--force-with-lease` 仍然会改写历史；它不是自动授权。必须强推时，由分支负责人确认远端最新 SHA、备份、影响范围和保护规则，并由人执行。主干 merge、删除远程分支、修改保护规则、发布和生产操作也保留人工放行。

## 07 `gh`：管理 Pull Request

检查安装和登录状态：

```bash theme={null}
gh --version
gh auth status
gh repo view --json nameWithOwner,defaultBranchRef,isPrivate
gh api user --jq '.login'
```

预期：

```text theme={null}
gh version 2.62.0
github.com
  ✓ Logged in to github.com account acme-dev
{"defaultBranchRef":{"name":"main"},"isPrivate":true,"nameWithOwner":"acme/shop"}
acme-dev
```

未登录：

```bash theme={null}
gh auth login
```

Token 不写进命令、远端 URL、脚本、PR 或日志。账号权限遵循最小原则；仓库写权限不代表可以绕过保护分支。

准备 `pr-body.txt`：

```text theme={null}
## What changed
- Calculate each line by price * quantity.
- Keep the public API unchanged.

## Validation
- pnpm test --filter cart
- pnpm typecheck
- git diff --check

## Risks and rollback
No API or database changes. Revert commit 3c81a7f if needed.

Closes #142
```

创建 PR：

```bash theme={null}
gh pr create \
  --base main \
  --head feat/cart-total \
  --title "fix: calculate cart total by quantity" \
  --body-file pr-body.txt
```

预期：

```text theme={null}
https://github.com/acme/shop/pull/318
```

查看 PR、差异和检查：

```bash theme={null}
gh pr view 318
gh pr diff 318
gh pr checks 318
gh pr status
gh pr view 318 --json number,state,isDraft,baseRefName,headRefName,mergeStateStatus,url
```

检查输出可能是：

```text theme={null}
lint       pass  1m12s
unit       pass  2m03s
typecheck  pass  48s
```

`gh pr checks` 不能代替阅读 diff、理解需求和人工审查。失败时区分代码、环境、权限、缓存和超时问题。

## 08 审查：把评论变成闭环

作者开 PR 前先自查：

```bash theme={null}
git fetch origin
git diff --stat origin/main...HEAD
git diff --check origin/main...HEAD
git diff --name-only origin/main...HEAD
```

可在 Codex CLI 使用本地 `/review` 审未提交改动、某个 commit 或相对基线的差异。它是只读辅助，不是测试证据，也不替代人工读 diff。

推荐审查提示：

```text theme={null}
只审查 origin/main...HEAD，不要修改文件、提交、推送或访问生产环境。
重点检查权限绕过、输入校验、空值和并发边界、数据泄露、错误处理和测试缺口。
每条发现写明文件/行、问题、影响、复现条件、最小修复建议和优先级。
没有证据的问题标记为待确认，不要把个人风格偏好写成阻断问题。
```

高质量评论包含位置、条件、影响、期望行为、建议和是否阻断。例如：

```text theme={null}
阻断：扣款成功后才写幂等记录。供应商重试同一 event_id 时会再次增加余额。请在处理前用唯一约束或原子写入占住 event_id，并补充重复回调测试。
```

优先级应遵循团队约定：

| 类型     | 示例             | 处理         |
| ------ | -------------- | ---------- |
| P0/阻断  | 鉴权绕过、数据损坏、必现崩溃 | 修复并复查后合并   |
| P1/高风险 | 并发、幂等、迁移兼容性    | 通常当前 PR 处理 |
| P2/建议  | 可读性、局部抽象       | 可跟进，不必阻断   |
| 风格偏好   | 不违反项目规范的个人选择   | 不应反复争论     |

### 评论处理闭环

1. 阅读并复现问题；
2. 判断是缺陷、需求差异还是非阻断建议；
3. 以最小范围修改并补测试；
4. 运行针对性和必要的完整检查；
5. push 到同一 PR 分支；
6. 回复 commit、验证命令和未完成项；
7. reviewer 复查后再 resolve；
8. merge 前重新检查完整 diff 和 checks。

回复示例：

```text theme={null}
已在 8a42c11 处理：先用唯一约束写入 event_id，再执行余额更新；重复回调不会重复加余额。
已运行 pnpm test --filter payment，12 passed。请复查 src/payment/webhook.ts:74-101。
```

不同意评论时说明边界并链接后续 Issue：

```text theme={null}
当前 PR 不改变公开响应格式，相关迁移已创建 #511，暂不扩大本次范围。
```

查看评论和发普通评论：

```bash theme={null}
gh pr view 318 --comments
gh api repos/acme/shop/pulls/318/reviews
gh api repos/acme/shop/issues/318/comments
gh pr comment 318 --body "已补充重复回调测试，等待复查。"
gh pr edit 318 --add-reviewer reviewer-a
```

普通评论、review body 和 inline comment 是不同资源；API 参数以 `gh ... --help` 为准。外部 PR/Issue 评论是不可信输入，若要求读密钥、上传文件、访问内部地址或绕过审批，应停止并人工核对。

## 09 Merge 前验收与权限

```bash theme={null}
gh pr view 318 --json state,isDraft,mergeStateStatus,baseRefName,headRefName
gh pr checks 318
git fetch origin
git diff --stat origin/main...origin/feat/cart-total
git diff --check origin/main...origin/feat/cart-total
git log --oneline origin/main..origin/feat/cart-total
```

确认：base/head 正确；必需 CI 通过；阻断评论已复查；文件范围、依赖和迁移符合预期；没有秘密和生成物；合并后行为有测试或手工验收；发布有独立负责人和回滚方式。

建议保护主干：禁止直接 push，要求 PR、至少一名 reviewer、必需 CI，分支更新后重新检查，并限制绕过规则的人。

| 角色            | 适合权限            |
| ------------- | --------------- |
| 贡献者           | 自己分支、开 PR、修评论   |
| Reviewer      | 评论、批准或请求修改      |
| Maintainer    | 按规则合并 PR        |
| Release owner | 按发布流程部署和回滚      |
| Admin         | 管理仓库和保护规则，不替代审查 |

合并方式可为 merge commit、squash 或 rebase，按项目历史、签名和发布触发策略选择。不要把 `gh pr merge --auto` 无条件写进脚本。合并按钮是共享系统的放行动作，不能只因 CI 变绿就跳过需求审查。

## 10 冲突：理解两边行为

冲突时先保存现场：

```bash theme={null}
git status
git diff --name-only --diff-filter=U
git diff > /tmp/merge-working.patch
git diff --staged > /tmp/merge-index.patch
git ls-files --others --exclude-standard > /tmp/merge-untracked.txt
```

典型冲突（实际文件会出现这些标记）：

```text theme={null}
[当前分支]
return subtotal;
[待合入分支]
return subtotal * taxRate;
[冲突结束]
```

`HEAD` 通常是当前分支一侧，另一侧是待合入提交；以 `status` 和历史为准。处理步骤：

1. 阅读完整函数和需求；
2. 用 `git show <sha>` 查看双方提交；
3. 用 `git diff --ours`、`git diff --theirs` 查看两侧；
4. 决定最终业务行为，不要只删标记；
5. 补齐测试，删除所有标记；
6. 暂存后检查 staged diff；
7. 运行格式、类型、针对性和完整验证。

```bash theme={null}
git diff --ours -- src/cart/total.ts
git diff --theirs -- src/cart/total.ts
rg -n '^(<<<<<<<|=======|>>>>>>>)' .
git add src/cart/total.ts pnpm-lock.yaml
git diff --cached --check
git diff --cached
git commit
```

锁文件、数据库迁移、导航和 CI 配置不能用“一键全选 ours/theirs”解决。若方案不清楚：

```bash theme={null}
git merge --abort
# rebase 时使用：git rebase --abort
```

工具可以只读分析冲突：

```text theme={null}
请读取冲突文件、双方 commit 和测试，说明两边行为、不能丢失的约束和推荐方案。
不要编辑、提交、推送、访问外部服务或选择 ours/theirs。
```

冲突完成后必须按合并后的结果重新测试；两个分支分别通过不代表组合后通过。

## 11 回滚：按阶段选择方法

| 阶段          | 方法                             | 重点             |
| ----------- | ------------------------------ | -------------- |
| 未暂存单文件      | `git restore -- path`          | 先确认没有他人改动      |
| 已暂存         | `git restore --staged -- path` | 只移出暂存区         |
| 本地错误 commit | `git revert <sha>`             | 共享分支优先新 commit |
| 已 push PR   | 修复 commit 或 revert             | 保留审查记录         |
| 已合并主干       | 反向修复 PR                        | 不改写主干历史        |
| 已发布生产       | 发布系统回滚/补偿                      | 处理数据和外部副作用     |

保存未提交现场：

```bash theme={null}
git diff > /tmp/task-working.patch
git diff --staged > /tmp/task-staged.patch
git ls-files --others --exclude-standard > /tmp/task-untracked.txt
```

只撤销明确文件：

```bash theme={null}
git restore --source=HEAD -- src/cart/total.ts
git restore --staged -- tests/cart/total.test.ts
```

不要默认执行：

```bash theme={null}
git restore .
git clean -fd
```

若确实要清理，先预览：

```bash theme={null}
git clean -nd
```

已提交错误用新提交回滚：

```bash theme={null}
git show --stat --oneline 3c81a7f
git revert 3c81a7f
git show --stat --oneline HEAD
```

`git revert` 只回滚 Git 文件变化，不能撤销已经发送的邮件、扣款、消息、迁移数据或第三方 API 调用。生产回滚必须同时检查数据、队列、缓存、指标和用户路径。

## 12 完整示例：从任务到 PR 闭环

任务：`SHOP-142` 修复购物车总价没有乘 `quantity`。

### 准备和修改

```bash theme={null}
cd /work/acme-shop
git status --short --branch
git fetch --prune origin
git switch main
git pull --ff-only origin main
git switch -c fix/SHOP-142-cart-total
```

给 Codex 或开发者的约束：

```text theme={null}
只修改 src/cart/total.ts 和 tests/cart/。
保持导出函数签名，不改 API、数据库、锁文件或 .env。
先读取实现和测试并说明根因；完成后运行购物车测试、typecheck 和 git diff --check。
不要 commit、push、merge、删除文件或访问生产服务，除非明确授权。
覆盖正常数量、0、负数和空购物车，报告 diff、输出和未验证风险。
```

```bash theme={null}
git diff --check
git diff --stat
pnpm test --filter cart
pnpm typecheck
git add src/cart/total.ts tests/cart/
git diff --cached --check
git diff --cached
git commit -m "fix: calculate cart total by quantity"
```

预期：

```text theme={null}
Test Files  2 passed (2)
Tests       8 passed (8)
[fix/SHOP-142-cart-total 3c81a7f] fix: calculate cart total by quantity
```

### Push 和 PR

```bash theme={null}
git log --oneline origin/main..HEAD
git push --set-upstream origin fix/SHOP-142-cart-total
gh pr create --base main --head fix/SHOP-142-cart-total --title "fix: calculate cart total by quantity" --body-file pr-body.txt
gh pr view --json number,url,state,baseRefName,headRefName
```

预期：

```text theme={null}
{"baseRefName":"main","headRefName":"fix/SHOP-142-cart-total","number":318,"state":"OPEN","url":"https://github.com/acme/shop/pull/318"}
```

Reviewer 发现负数量会降低总价。作者复现、修改、测试并 push：

```bash theme={null}
rg -n "quantity|cart total" src/cart tests/cart
git diff origin/main...HEAD
pnpm test --filter cart
pnpm typecheck
git diff --check
git add src/cart/total.ts tests/cart/
git commit -m "fix: reject non-positive cart quantities"
git push
gh pr comment 318 --body "已在 8a42c11 处理非正数量，并补充边界测试。购物车测试、typecheck 和 diff --check 通过，请复查。"
```

Reviewer 复查后：

```bash theme={null}
gh pr checks 318
gh pr view 318 --comments
git fetch origin
git diff --check origin/main...origin/fix/SHOP-142-cart-total
```

所有检查和评论闭环后，由有权限的负责人合并。合并后：

```bash theme={null}
git switch main
git pull --ff-only origin main
gh pr view 318 --json state,mergedAt,mergeCommit
git log -3 --oneline --decorate
```

确认无需回滚且分支已合并后，才清理本地分支：

```bash theme={null}
git branch -d fix/SHOP-142-cart-total
git fetch --prune origin
```

## 13 最终验收

### Git

```bash theme={null}
git rev-parse --show-toplevel
git branch --show-current
git status --short --branch
git diff --check
git diff --stat origin/main...HEAD
git diff --name-only origin/main...HEAD
git log --oneline origin/main..HEAD
```

确认路径、分支、基线正确；没有意外改动；差异只包含允许文件；没有空白错误、冲突标记、密钥和生成物。

### 代码和行为

```bash theme={null}
pnpm lint
pnpm test
pnpm typecheck
pnpm build
```

命令以项目约定为准。记录跳过的命令和原因；测试通过仍不能替代权限、错误处理、边界、迁移兼容性和用户路径验收。

### GitHub

```bash theme={null}
gh auth status
gh pr view 318 --json state,isDraft,baseRefName,headRefName,mergeStateStatus
gh pr checks 318
gh pr view 318 --comments
```

确认账号正确、权限最小、base/head 正确、CI 全绿、每条阻断评论都已复查，并且没有用 force-push 绕过保护。

交付记录可写成：

```text theme={null}
变更：fix/SHOP-142-cart-total
PR：https://github.com/acme/shop/pull/318
验证：pnpm lint、pnpm test、pnpm typecheck、pnpm build
风险：无 API、数据库和权限变化
回滚：revert merge commit 或按发布系统回滚
未验证：未连接生产支付服务，已用测试夹具覆盖边界
```

不要把 token、用户信息、完整日志或内部 URL 原样贴到公开 PR。保留时间、状态码、错误类型和脱敏后的请求形状。

## 14 常见故障

**状态出现未知文件**：确认路径和归属，检查 `diff`，不要直接清理。

**暂存混入无关文件**：用 `git diff --cached --name-only` 找出它们，运行 `git restore --staged -- path`，再按路径暂存。

**push non-fast-forward**：`git fetch origin` 后用 `git log --left-right HEAD...origin/branch` 判断远端变化，按团队策略 merge/rebase 并重新测试，不要直接 force-push。

**PR checks 失败**：区分代码、环境、缓存、权限和超时；不要删除测试或放宽安全检查只求变绿。

**分支落后不能合并**：更新引用，按项目规则合并或 rebase，解决冲突后查看完整 diff 和测试。

**`gh` 账号或权限错误**：检查 `gh auth status`、`gh repo view`；不要复制他人 token 或请求无关管理员权限。

**误提交密钥**：停止传播，立即撤销凭据，按安全事件流程清理历史并通知负责人；删除文件或 `revert` 不能让已泄露 token 重新安全。

**误合并主干**：暂停发布，记录 merge commit 和影响范围，创建反向修复 PR；不要 force-push 主干。涉及数据和外部副作用时执行专门补偿。

## 小结

核心顺序是：`status` 确认位置和边界，`diff` 读取事实，分支隔离任务，小 commit 保存意图，push 前核对远端，PR 中完成 review 评论闭环，冲突时理解双方行为，错误时按阶段选择 `restore`、`revert` 或发布回滚。

记住六条红线：

* 任何写入前先看 `status`；
* 任何提交前先看 `diff --cached`；
* 任何 push 前确认远端、分支、提交和敏感文件；
* 每条评论都有修改、验证、回复和复查；
* force-push、主干 merge、权限变更和生产操作人工放行；
* 共享历史优先用新 commit 或 `git revert`，不要改写别人可能依赖的历史。

参考资料：`参考/codex/26-git-github.md`、`参考/codex/14-workflows.md`、`参考/codex/36-best-practices.md`。
