# Git Workflow

> Git 工作流安全助手。本技能应在需要执行分支管理、长期集成分支（long-lived integration branch）、Monorepo 安全合并、PR 创建/审查/合并、冲突处理、cherry-pick、安全回退、stale/已合并分支审计与清理（branch cleanup，含 squash/rebase merge 校验）、开 worktree 前 base 同步检查（防 main drift 致 PR not mergeable）、多 worktree 并行时 main worktree 占用处理时使用。不要用于：批量生成提交信息、项目任务分配、长期任务状态管理或本地多 Agent 会话编排。

- Skill: `cat-xierluo/git-workflow` (Agent Skill, multi-file: 10 files)
- Install (CLI): `npx skillmds@latest add cat-xierluo/git-workflow`
- Raw SKILL.md: https://api.skillmd.com/api/skills/cat-xierluo/git-workflow/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- License: MIT
- Author: cat-xierluo (https://skillmd.com/u/cat-xierluo)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/cat-xierluo/git-workflow

---


# Git 全流程工作流

## 触发场景

- 分支创建、切换、管理
- 长期集成分支及其子 PR、里程碑 PR 管理
- 合并代码到 main（特别是 Monorepo 仓库）
- 创建、审查、合并 PR
- 解决合并冲突
- Git 操作前的安全检查

## 1. Git 安全协议

以下操作**必须获得用户明确指示**才能执行：

| 禁止操作 | 原因 |
|:---------|:-----|
| `git push --force`（特别是 main/master） | 覆盖他人提交 |
| `git reset --hard` | 丢弃未提交的修改 |
| `git checkout .` / `git restore .` | 丢弃工作区改动 |
| `git clean -f` | 删除未跟踪文件 |
| `git branch -D` | 强制删除分支 |
| `--no-verify` 跳过 hooks | 绕过安全检查 |
| `--no-gpg-sign` 跳过签名 | 绕过完整性验证 |

**安全原则**：
- 永远创建新 commit，而非 amend 已有 commit（除非用户明确要求）
- 暂存文件时，优先按文件名 `git add <file>` 而非 `git add .`
- 检测到 lock 文件时，先调查持有进程而非直接删除
- 遇到 pre-commit hook 失败时，修复问题后创建新 commit，不跳过 hook

### Git 身份隔离与 push 前门禁

worktree 隔离文件和 HEAD，但同一仓库的 worktree 默认共享仓库级 `.git/config`。因此 worker **禁止**运行 `git config user.name ...`、`git config user.email ...` 或带 `--local` 的同类命令；这些写入会污染其他并发 worktree。只有项目已明确启用 `extensions.worktreeConfig` 且用户授权时，才可讨论 `git config --worktree`。默认用单次环境变量绑定本次提交身份：

```bash
GIT_AUTHOR_NAME="<name>" GIT_AUTHOR_EMAIL="<email>" \
GIT_COMMITTER_NAME="<name>" GIT_COMMITTER_EMAIL="<email>" \
  git commit -m "<title>" -m "<body>"
```

push 必须走身份绑定的 `safe-push.sh`，核验**完整 PR range**后只 push 已核验的 immutable OID；不得直接 `git push`，也不得只看 `git log -1` 或 HEAD：

```bash
# integration base 必须显式是远端跟踪 ref；不要用 HEAD~1 缩窄范围
bash scripts/safe-push.sh \
  --base origin/main \
  --remote origin \
  --branch feat/example \
  --expected-name "<name>" \
  --expected-email "<email>"

# 只读诊断可单独运行门禁；不替代 safe-push
bash scripts/check-outgoing-identities.sh \
  --base origin/main \
  --expected-name "<name>" \
  --expected-email "<email>"
```

门禁逐 commit 比较 author name/email 与 committer name/email，只接受当前 worktree HEAD 与远端跟踪 base。当前 feature branch 若已跟踪同名 `origin/feat/...`，自动 upstream 会隐藏已 push 的早期 commit，因此判为 ambiguous，必须显式传 PR base。以下任一情况均 fail-closed：base 不明或不是远端跟踪 ref、用 `HEAD~1`/本地 ref 任意缩窄范围、bad revision、base 不是 HEAD 祖先、range 为空、Git 命令出错、身份字段为空或任一 commit 身份不一致。`safe-push.sh` 刷新 integration base，核验当前 HEAD，确认核验期间 HEAD 未变化，再把该 OID 精确推到目标分支，使证据绑定实际 push 对象。

## 2. 分支管理

### 创建新分支

普通短分支从最新默认主干创建；项目若已显式声明长期集成目标，则 worker 短分支必须从最新远端集成目标创建，不能仍默认从 `main` 起步。

```bash
# 从最新 main 创建
git checkout main && git pull origin main
git checkout -b <type>/<short-description>

# 从长期集成目标创建 worker 短分支
git fetch origin
git switch -c <type>/<short-description> origin/<integration-branch>

# 命名规范
feat/add-ocr-support
fix/empty-description-retry
docs/update-readme
refactor/sync-logic
```

### 分支命名规范

分支名是远端协作和 PR 的公共标识，必须按任务语义命名，不按本地执行来源命名。不要在分支名前加 `tmux-`、`subagent-`、`team-`、`agentteam-` 等前缀；这些前缀属于本地 worktree 或 session 名称，由 `parallel-agent-workflow` 管理。

| 前缀 | 用途 | 示例 |
|:-----|:-----|:-----|
| `feat/` | 新功能 | `feat/batch-export` |
| `fix/` | Bug 修复 | `fix/null-pointer` |
| `docs/` | 文档 | `docs/api-guide` |
| `research/` | 调研/素材 | `research/issue-13-ch08-materials` |
| `refactor/` | 重构 | `refactor/parser` |
| `chore/` | 杂项 | `chore/update-deps` |

推荐示例：

```bash
docs/ch01-agent-intro
research/issue-13-ch08-materials
fix/agent-session-shell
```

反例：

```bash
tmux-ch01
subagent-fix-copy
team-feature-a
```

### 长期集成分支模式

仅当一个大型功能需要跨多个可独立验收的子 PR 或多个开发波次、但整体尚不应进入默认主干时，才显式建立长期集成分支。普通功能仍使用短分支直接向默认主干提 PR；不要把长期分支当作无门禁 WIP 仓库。

长期集成分支是单一功能线的阶段主干（mini-main），必须遵守与默认主干同等级的 review、测试、身份和 push 门禁：

- 默认主干保存项目稳定基线；长期集成分支只聚合该功能线；worker 短分支承载一次性子任务。
- 长期分支由固定集成者/PM Worktree 独占检出；worker 从最新 `origin/<integration-branch>` 建独立短分支和 Worktree，并显式把 PR base 指向该集成分支。
- 子 PR 经独立验收后 squash merge 到长期分支；达到预先命名且有退出条件的里程碑后，才由长期分支向默认主干提集成 PR。
- 默认主干的通用修复先进入默认主干，再在无待合并子 PR 的波次边界 merge 到长期分支；同步后冻结本波 base，避免 worker 基线漂移。
- 长期分支禁止 rebase、force-push 或随子 PR 删除；里程碑合入默认主干后也继续保留，直到功能线被明确关闭。
- 集成 PR（base=默认主干）只在里程碑达成时开；功能线推进期只开子 PR（base=长期分支）。head 分支被重置致 PR 自动 CLOSED、squash 重做的 CONFLICTING 与树等价验证，详见 references/long-lived-integration-branch.md 第 4/7 节。

执行建线、同步、PR、里程碑和清理时，读取 `references/long-lived-integration-branch.md`。项目专属的分支名、固定 Worktree 路径、任务字段和里程碑门禁留在项目规则中，不写入本通用 Skill。

### 分支清理

先区分生命周期，再决定清理范围：

- 一次性 `ephemeral-worker` 在交付、PR/head、expected tip、干净 Worktree 与 lifecycle settlement 全部绑定后，默认随单任务收口清理。
- `long-lived` 功能/集成分支及固定 Worktree 不进入单任务自动清理，也不进入常规 stale 批量候选；短 Worker 合入长期分支时只清理 head，绝不触碰 `integration_target`。
- 单任务收口结果必须是 `CLEANED`、`RETAINED_WITH_REASON` 或 `CLEANUP_PENDING`。交付已确认后的清理失败不得重放 push/merge，也不得被隐去。
- 批量审计必须组合 PR 状态、最后提交时间、Worktree/dirty 状态和分支身份，向用户展示候选并取得确认；不得仅凭 `--merged`、ahead/behind 或分支名删除。

完整的单 Worker 自动清理、squash/rebase expected-tip 删除、批量 stale 审计、长期功能线关闭与红线统一读取 `references/branch-lifecycle-and-cleanup.md`。

### Worktree（工作树）

#### 开 worktree 前的必做 3 查（防止 base 过期导致 PR 报 not mergeable）

**核心陷阱**：本地 `main` 可能落后于 `origin/main`（本地独有未 push 的 commit / fetch 滞后 / 别的 session 在 origin 推了新内容）。基于这种"过期 main"开的新 worktree 提 PR 时，GitHub 会报 `not mergeable: the merge commit cannot be cleanly created`，且 PR 的 base 不包含 origin/main 已合的内容——你不知道原来已经合了什么，DECISIONS 编号可能撞车、TASKS 已勾的项要重做。

**3 查清单**（开 worktree 前必跑，逐项确认）：

```bash
# 1. fetch 远端最新
git fetch origin

# 2. 看本地 main 与 origin/main 是否分叉
echo "本地 main:    $(git rev-parse --short main)"
echo "origin/main:  $(git rev-parse --short origin/main)"
echo "merge-base:   $(git merge-base main origin/main | head -c 12)"

# 3. 看本地是否有未推送独有 commit
git status --short
git log --oneline origin/main..main   # 本地 main 独有、未 push 的 commits
```

**判读规则**：

| 情况 | 现象 | 处理 |
|---|---|---|
| 本地 main = origin/main（无分叉） | merge-base = main = origin/main | 直接开 worktree，放心 |
| 本地 main 领先 origin/main | `git log origin/main..main` 有 commit（本地独有未 push） | **先 push 或 merge origin/main**，决定见下方"本地独有 commit 处理" |
| 本地 main 落后 origin/main | `git log main..origin/main` 有 commit（origin 已合，本地没 fetch） | **先 `git pull --no-rebase`（merge origin/main）再开 worktree** |
| 本地与 origin/main 双向分叉 | 双方各有独有 commit | **先 rebase 或 merge**，避免 PR 冲突 + 重新编号 |

**禁止** 基于"过期 main"开 worktree 后再补救。会引发：PR 报 not mergeable → 本地 rebase 解决 → 决策编号撞车（如 DECISIONS.md 在 main 与 PR 都有新增）→ 重新编号 + push `--force-with-lease`。一次性 3 查可避免。

#### 本地独有 commit 未 push 的处理（3 查清单的延续）

`git log origin/main..main` 显示本地独有 commit 时，三选一：

| 选项 | 适用场景 | 操作 |
|---|---|---|
| **A. Push 到 origin** | 独有 commit 是想让 origin 看的（如 docs 标记、版本号） | `git push origin main`（**禁止**直接 push main，先确认无保护规则；如保护则改 PR 流程） |
| **B. Merge origin/main 保留**（推荐） | 独有 commit 是本地工作，希望下次 main 上有 | `git merge origin/main --no-ff -m "merge: bring origin/main into local main + preserve <描述>"` |
| **C. 放弃独有 commit** | 独有 commit 已不需要或重复 | `git reset --hard origin/main`（**破坏性**，必须用户明确指示） |

**禁止** 擅自 `git reset --hard` 丢弃本地独有 commit（Git 安全协议 §1）。

#### 创建 worktree

当需要同时在多个分支上工作时，使用 worktree 避免频繁切换分支：

```bash
# 创建 worktree（自动创建新分支）
git worktree add ../pm-feature-ocr feat/ocr-support

# 在 worktree 中工作
cd ../pm-feature-ocr
# ... 编辑、提交 ...

# 完成后回到主工作目录
cd -

# 删除 worktree
git worktree remove ../pm-feature-ocr

# 查看所有 worktree
git worktree list
```

**使用场景**：
- 一个分支在跑耗时任务（训练/测试），同时需要在另一个分支工作
- 需要对比两个分支的代码
- Code review 时需要拉取 PR 分支到本地测试

**注意事项**：
- 同一分支不能同时被两个 worktree 检出
- worktree 中的修改是独立的，需要单独 push
- 删除 worktree 前确认已提交或推送改动

## 3. Monorepo 安全合并

### 核心规则

**禁止 `git merge` 直接合并 feature 分支到 main。** Feature 分支若从旧 commit 创建，直接合并会误删所有不在分支里的文件。

### 正确做法：目录级 checkout

```bash
git checkout main && git pull origin main
git checkout <feature-branch> -- <skill-directory>/
git diff --cached --stat   # 确认只改了目标目录
git commit -m "feat(<skill>): 描述"
```

### 多 Skill 合并

涉及多个 Skill 时逐个目录 checkout，每个目录一个提交：

```bash
git checkout main && git pull origin main
git checkout <feature-branch> -- skill-a/
git diff --cached --stat
git commit -m "feat(skill-a): 描述"

git checkout <feature-branch> -- skill-b/
git diff --cached --stat
git commit -m "feat(skill-b): 描述"
```

### 合并后验证

```bash
git diff HEAD~1 --stat    # 确认无误删
ls .gitignore .env 2>/dev/null  # 确认关键文件还在
```

### GitHub PR 合并

若用 GitHub PR 合并 Monorepo 中的某个 Skill 改动：

> 下列 rebase 流程只适用于普通短分支。已声明为长期集成分支的阶段主干禁止 rebase/force-push，改为按 `references/long-lived-integration-branch.md` 在波次边界 merge 最新默认主干；其 worker 短分支仍按项目策略处理。

1. **先 rebase** feature 分支到最新 main，确保 base commit 包含所有文件
2. 确认 PR diff 只涉及目标 Skill 目录
3. 使用 squash merge，commit 标题包含模块名和 PR 编号

```bash
# rebase feature 分支
git checkout <feature-branch>
git rebase origin/main
git push --force-with-lease  # rebase 后需要 force push
```

### Rebase 冲突时的恢复

`git pull --rebase` 遇到冲突时，**不要盲目接受远程的删除**。Monorepo 中远程 PR 误删文件是常见情况。

**判断原则**：
1. 如果冲突是"远程删除 vs 本地修改"，先确认远程的删除是否是有意为之
2. 如果该 Skill 目录在远程 main 仍存在但被删除，很可能是合并误删，应保留本地版本
3. 如果确认是误删，用 `git checkout <本地commit> -- <skill-directory>/` 恢复

**恢复流程**：

```bash
# 1. 先中止 rebase，回到安全状态
git rebase --abort

# 2. 获取 rebase 前的本地提交（通过 reflog）
git reflog | head -10

# 3. 从本地提交恢复被误删的目录
git checkout <本地commit-hash> -- <skill-directory>/

# 4. 单独提交恢复的文件
git diff --cached --stat   # 确认恢复的文件
git commit -m "feat(<skill>): 恢复被误删的文件"
git push origin main
```

**关键**：`git reflog` 保存了所有操作历史，即使 rebase 后本地提交也不会真正丢失。

## 4. PR 工作流

### 创建 PR

```bash
# 推送分支
git push -u origin <branch-name>

# 创建 PR（cwd 不在目标分支的 worktree 时必须显式 --head，否则 gh 以当前
# 分支为 head——在 main 仓库根目录执行会报 "No commits between main and main"）
gh pr create \
  --head <branch-name> \
  --title "feat(module): 简短描述" \
  --body "$(cat <<'EOF'
## 摘要
- 关键变更 1
- 关键变更 2

## 测试计划
- [ ] 验证项 1
- [ ] 验证项 2
EOF
)"
```

### PR 正文最低要求

创建或审查 PR 时，正文至少包含：

| 区块 | 要求 |
|------|------|
| 摘要 | 说明改了什么，避免只有“update files” |
| 测试计划 | 列出已运行或未能运行的验证；未运行要写原因 |
| Agent 归属 | 若由 Agent 完成，写明 Agent ID、Git author、触发来源 |
| 关联任务 | 关联 GitHub Issue、项目任务 ID 或用户指定任务 |
| 风险 | 涉及迁移、删除、权限、安全、跨模块改动时说明风险和回退方式 |

缺失「摘要」或「测试计划」时，不应 approve；缺失「Agent 归属」时，要求补齐后再合并。

### PR 标题格式

```
<类型>(<模块>): <描述>
```

与 commit 格式一致，多 Skill 仓库必须带模块名。

### 审查 PR

```bash
# 查看 PR 详情
gh pr view <number>

# 查看 PR 文件变更
gh pr diff <number>

# 提交 review
gh pr review <number> --approve --body "LGTM"
gh pr review <number> --request-changes --body "建议修改..."
```

### 合并 PR

合并默认采用 fail-closed 策略。只有在 diff 可读、review 结论明确、CI/checks 明确通过时，才允许自动或半自动合并。

合并前先做最小检查：

```bash
gh pr view <number> --json title,state,isDraft,mergeable,reviewDecision,headRefName,baseRefName
gh pr diff <number> --name-only
gh pr checks <number>
```

判断规则：
- `state` 不是 `OPEN` 或 `isDraft` 为 `true`：不合并
- `mergeable` 为 `UNKNOWN` / `CONFLICTING` / 空值：不合并，先更新分支或人工检查
- `reviewDecision` 为 `CHANGES_REQUESTED`，或应有 review 但没有明确通过：不合并
- `gh pr checks` 有失败、等待中、未知状态，或无法读取：不合并
- `gh pr diff --name-only` 显示跨模块污染、误删大量文件、敏感配置文件：不合并

### Monorepo PR Diff 检查清单

对 Monorepo 或多 Skill 仓库，合并前必须检查文件范围：

```bash
gh pr diff <number> --name-only
gh pr diff <number> --stat
```

阻断条件：
- PR 声称只改一个模块，但 diff 涉及多个无关目录。
- 出现大量 `deleted` 或目录整体删除，且 PR 正文没有解释。
- 改动包含 `.env`、`config/secrets.*`、`credentials.json`、私钥或 token 文件。
- lockfile、schema、迁移文件、生成物变化无法对应到 Summary / Test plan。
- `README.md`、Marketplace 清单、版本号、CHANGELOG 中的版本不一致。

处理方式：要求拆 PR、缩小 diff、补说明或补测试。不要用“看起来问题不大”替代文件级检查。

```bash
# Squash merge（推荐）
gh pr merge <number> --squash \
  --subject "feat(module): 描述 (#<number>)" \
  --body "关键变更说明"

# Merge commit
gh pr merge <number> --merge

# Rebase merge
gh pr merge <number> --rebase
```

**重要**：通过 API 执行 squash merge 时，`commit_title` 不会自动追加 `(#N)`，必须手动写入。

### 自 PR 自 review 限制（GitHub 强制）

GitHub **不允许 PR 作者自 approve 自已的 PR**：

```
gh pr review <N> --approve
# → failed to create review: GraphQL: Review Can not approve your own pull request (addPullRequestReview)
```

这是 GitHub 设计，无法绕过。但 **`gh pr merge --squash --delete-branch` 不需要 review approval**（前提：仓库无强制 review 的 branch protection）。常见场景：

| 场景 | 处理 |
|---|---|
| 无 branch protection 或不要求 review | `gh pr merge <N> --squash --delete-branch` 直接合 |
| 要求 ≥ 1 个 review | 找他人 review；或 admin override `gh pr merge <N> --squash --admin`（谨慎，记录原因） |
| 自 PR 自 review 完全禁止 | 用其他账号 review；或拆 PR 让别人创建 |

**常见坑**：`gh pr merge --delete-branch` 在 cleanup 阶段可能报 `'main' 已经被工作区 '<主仓库路径>' 使用`（多 worktree 场景，见 §10），这是 warning，不影响合并本身——`mergedAt` 时间戳写入 GitHub 即代表合并成功。

### 本地拉取 PR 到 main 的提交格式

当用户要求“拉取 PR 到主分支 / 把 PR 拉进 main / 合入这个 PR”时，默认目标是让 `main` 历史中能直接看出来源 PR。不要用 `git pull --ff-only origin pull/<N>/head` 作为最终合入方式，因为 fast-forward 会保留 PR 原提交标题，通常不会显示 `(#N)`。

默认使用 squash commit 方式在 `main` 上生成一个带 PR 编号的提交：

```bash
# 1. 更新 main
git checkout main
git pull --ff-only origin main

# 2. 检查 PR 状态与 diff
gh pr view <N> --json title,state,isDraft,mergeable,reviewDecision,headRefName,baseRefName,url
gh pr diff <N> --name-only
gh pr checks <N>

# 3. 拉取 PR head 并 squash 到暂存区
git fetch origin pull/<N>/head
git merge --squash FETCH_HEAD
git diff --cached --stat

# 4. 使用 PR 标题 + PR 编号提交
git commit -m "<PR 标题> (#<N>)" \
  -m "PR: <PR URL>"

# 5. 推送 main，并关闭原 PR（若 GitHub 未自动标记 merged）
git push origin main
gh pr close <N> --comment "已通过提交 <sha> 合入 main。"
```

提交标题示例：

```text
docs: 设定章节撰写默认使用 tmux Codex session (#7)
docs(ch01): 从 Chatbot 到 Agent (#10)
research(issue13): ch08 迭代解耦素材包 (#11)
```

若 PR 标题已经包含 `(#<N>)`，不要重复追加。若用户明确要求保留 PR 中多个原子 commit，不做 squash；但仍应提醒用户这种方式可能无法在每个 commit 标题中显示 PR 编号。

### Fail-Closed 合并门禁

以下任一情况出现时，不得自动合并，必须停下并让人类确认或先修复信号来源：

| 阻断条件 | 处理 |
|----------|------|
| `gh pr diff` 失败、diff 为空或不可读 | 不 approve，不 merge；先确认分支和权限 |
| CI/checks 失败、等待中、缺失或状态未知 | 不 merge；需要明确通过或用户显式确认 |
| review 结论缺失、互相矛盾或只是摘要没有 verdict | 不 merge；补一次明确 review |
| PR diff 超出声明范围，尤其是 Monorepo 误删文件 | 不 merge；先缩小 diff 或拆分 PR |
| 分支保护、required checks、linked issue 状态不清楚 | 不 merge；先查清仓库规则 |

`git-workflow` 只维护这些 Git 安全规则；任务状态仍由 `cross-agent-collab` 和项目任务源管理，本地 Agent 会话由 `parallel-agent-workflow` 管理。

### PR 状态检查

```bash
# 查看 CI 状态
gh pr checks <number>

# 查看所有 PR 列表
gh pr list --state open
```

### PR 创建后立即跑 mergeable 检查（强制）

Agent 在 `gh pr create` 返回 PR URL 后，**不要等用户/PM 拍板合并**，立即跑一次完整状态检查，捕获 base 落后或 mergeable 冲突：

```bash
gh pr view <N> --json state,mergeable,mergeStateStatus,baseRefName,headRefName,files
```

判读规则：

| `mergeable` | `mergeStateStatus` | 含义 | 处理 |
|---|---|---|---|
| `MERGEABLE` | `CLEAN` | 可直接合并 | 进入 review → 合并流程 |
| `UNKNOWN` | 空 | CI 还在跑或权限不足 | 等 CI / 确认权限后再查 |
| `CONFLICTING` | `DIRTY` | 有内容冲突 | **不要**直接 `gh pr update-branch`，按下方「base 落后 / 冲突处理决策表」选三选一方案 |
| `MERGEABLE` | `BLOCKED` / `BEHIND` | base 落后但无内容冲突 | `gh pr update-branch <N>` 拉 base；如果失败再走决策表 |

### base 落后 / 冲突处理决策表

当 PR 出现 base 落后、有冲突、或 update branch 失败时，按下表三选一：

| 情况 | 现象 | 推荐方案 |
|---|---|---|
| 冲突仅在 docs 同步文件（CHANGELOG / DECISIONS / TASKS） | `git diff main..HEAD -- docs/` 显示 diff 是 docs 同步段（版本号、DEC 编号、ISS 任务卡进度） | **方案 A：本地 rebase + 解决冲突**。接受 base 新内容，把 head 的 docs 段重新编号（如 DEC-026 → DEC-030）后 `git rebase --continue`；push 用 `--force-with-lease`。 |
| 冲突在共享代码 / 实质代码 | `git diff main..HEAD` 涉及 src/ src-tauri/ src/shared/ 等多文件 | **方案 B：关掉 PR + 重建**。`gh pr close <N> --delete-branch`；`git switch -C <branch> origin/main`；cherry-pick 实质代码 commit（跳过 docs 同步 commit）；重新写 docs 同步（使用最新 main 已占用的编号 +1）；push + new PR。 |
| 冲突极少 / 1-2 个文件 | `git diff main..HEAD` 改动小且冲突集中 | **方案 C：GitHub PR UI 手动解决**。在 PR 页面 "Resolve conflicts" → 编辑 → commit。 |

**禁止** `git push --force`（不带 `--force-with-lease`），可能在远端已有他人 push 时覆盖。

### PR 创建后：可选文档体检扩展

若当前项目明确配置了 `doc-curator` subagent 或同等文档体检流程，Agent 在 `gh pr create` 成功返回 PR URL 后，可以按项目协议触发一次文档体检；未配置时跳过，不影响本 Skill 的 Git 流程。

目的：在 PR 进入 review 前，发现当次变更是否引入文档膨胀、超出归档指针、违反硬性规则；如果有问题，由项目内的文档体检流程在 PR 自身或单独的 maintenance PR 内修正，不让膨胀项进入 main。

调用方式：

```bash
# 在 Agent 流程里，PR 创建完成后：
# 1. 调起项目配置的文档体检流程（如存在）
#    - 工作目录：仓库根
#    - 输入：刚 push 的 commit hash（可选）
#    - 期望输出：markdown 报告 + JSON 行

# 2. 解析报告（subagent 内部完成），按规则分支：
#    - 全部 ok → 不动作，继续 review 流程
#    - 软提示 → 把提示写入 PR 描述的"跟进事项"小节，不阻断
#    - 硬性 / 自适应告警 → 走 maintenance-pr.sh：
#      - 工作区干净 → 自动创建维护分支、提一个 maintenance PR
#      - 工作区不干净 → 仅报告，提示用户先清理

# 3. 不阻塞当前 PR：把 maintenance PR 链接追加到当前 PR 描述，让 review 知道"已发现 N 项"
```

约束：

- 这是 post-action 调起，不是 pre-PR 门禁（避免锁死 PR 创建流程）。
- 文档体检扩展不得改 `src/` / `src-tauri/` / `tests/`；改动仅限于 `docs/` 维护类动作。
- 文档体检扩展不写 `CHANGELOG.md`（CHANGELOG 由 `release-workflow` 或项目发布流程维护）。
- 当前 PR 已 push 但 review 还没合并时，maintenance PR 与当前 PR 并行存在；用户决定合并顺序。

### PR 合并后：可选文档体检扩展

若当前项目明确配置了 `doc-curator` subagent 或同等文档体检流程，Agent 在 `gh pr merge` 成功（或 squash 推送 main 完成）后，可以按项目协议触发一次完整体检；未配置时跳过。

目的：合并后文档库状态更新（新增 ISS 归档指针、DEC 编号推进、文件行数变化），基线可能漂移；及时发现新合并项是否引入膨胀，必要时自动提 maintenance PR。

调用方式：

```bash
# 在 Agent 流程里，PR 合并完成后：
# 1. 调起项目配置的文档体检流程跑体检（如存在）
# 2. 解析报告：
#    - 全部 ok → 不动作，结束
#    - 软提示 → 报告给用户，不自动 PR
#    - 硬性 / 自适应告警 → 走 maintenance-pr.sh：
#      - 工作区干净 → 自动提 maintenance PR（按项目协议）
#      - 工作区不干净 → 仅报告，让用户处理
# 3. 如果报告项触发了 state.json 的基线更新（adaptive 阈值漂移），下一次体检会按新基线判定
```

约束：

- 与"PR 创建后体检"互补：创建后体检关注"这次提交带来的变化"，合并后体检关注"main 整体健康度"。
- 合并后体检**不阻塞合并动作**：它发生在合并完成之后，只用于发现后续问题。
- 同一 PR 不重复触发两次（创建 + 合并各一次即可，不在中间 review 轮次再触发）。
- 文档体检扩展不会因为"发现 main 不健康"而尝试 revert 刚合入的 commit；它只做文档级维护，不动代码与决策。

### 总结：本 Skill 与文档体检扩展的关系

| 时机 | 谁调起 | 做什么 | 阻塞？ |
|:-----|:-------|:-------|:-------|
| `gh pr create` 成功 | 本 Skill（如项目配置） | 体检本次变更 | 不阻塞，输出报告 + 可选 maintenance PR |
| `gh pr merge` 成功 | 本 Skill（如项目配置） | 体检 main | 不阻塞，输出报告 + 可选 maintenance PR |
| 用户手动跑 `scan.sh` | 用户 | 体检 | 不阻塞 |
| SessionEnd / pre-commit | — | 不在本 Skill 范围 | — |

`git-workflow` 只负责说明可选体检时机；具体体检逻辑、维护动作、PR 生成全部由项目配置的文档体检流程负责。两者通过 subagent 或项目协议解耦：git-workflow 不直接执行文档 trim。

## 5. 合并冲突解决

### 检测冲突

```bash
# 尝试 merge，查看冲突文件
git merge <branch> --no-commit --no-ff
git diff --name-only --diff-filter=U   # 列出冲突文件
```

### 解决原则

1. **理解双方意图**：阅读冲突标记两侧的代码，理解各自修改的目的
2. **优先保留双方**：如果双方修改不矛盾，尽量都保留
3. **最小修改**：只修改冲突区域，不要顺便重构
4. **验证**：解决后运行编译/lint/测试

### 解决流程

```bash
# 1. 查看冲突文件列表
git diff --name-only --diff-filter=U

# 2. 逐个文件解决冲突
# 编辑文件，移除 <<<<<<< ======= >>>>>>> 标记

# 3. 标记为已解决
git add <resolved-file>

# 4. 验证
# 运行编译/lint/测试确保无破坏

# 5. 完成合并
git commit
```

### lock 文件冲突

`package-lock.json`、`pnpm-lock.yaml` 等锁文件冲突时：

不要默认删除 lock 文件并重装依赖。先理解冲突两侧的依赖变更，优先用包管理器支持的锁文件合并/重算流程；确需重新生成时，`npm install` / `pnpm install` 属于依赖安装与环境写入，必须先取得用户或项目规则对**精确命令**的明确授权，并由编排层记录授权来源。无授权或工具缺失时保持阻塞并报告，不得为完成验证自行安装。

## 6. 常用 Git 操作速查

### 撤销与回退

```bash
# 撤销工作区修改（未 add）
git restore <file>

# 撤销暂存（已 add，未 commit）
git restore --staged <file>

# 查看某个文件的修改历史
git log --oneline -- <file>

# 查看某次 commit 的内容
git show <commit-hash>
```

### 暂存工作

```bash
git stash save "描述"
git stash list
git stash pop        # 恢复最近的 stash
git stash pop stash@{2}  # 恢复指定 stash
```

### Cherry-pick

Cherry-pick 用于把某个已存在 commit 回补到当前分支。它容易把无关文件一起带入，必须先确认范围。

安全流程：

```bash
# 1. 工作区必须干净
git status --short

# 2. 先看 commit 内容和影响范围
git show --stat --oneline <commit-hash>

# 3. 回补完整 commit，并保留来源记录
git cherry-pick -x <commit-hash>

# 4. 回补后确认范围
git diff HEAD~1 --stat
```

Monorepo 或只需要部分文件时，不直接 cherry-pick 整个 commit，改用目录级提取：

```bash
git checkout <commit-hash> -- <directory>/
git diff --cached --stat
git commit -m "fix(<module>): 回补指定改动"
```

关键规则：
- 跨分支 backport 默认使用 `git cherry-pick -x`，保留来源 commit。
- 不直接 cherry-pick merge commit；确需处理时，必须明确父提交并使用 `git cherry-pick -m <parent-number> -x <merge-commit>`。
- 冲突后若范围变大、意图不清或出现跨模块污染，先 `git cherry-pick --abort` 回到安全状态。
- 冲突解决后必须重新查看 `git diff --stat`，确认只包含目标改动。
- 不把 cherry-pick 当作批量同步工具；多个无关 commit 应逐个处理和验证。

### 查看状态

```bash
git status
git log --oneline -20    # 最近 20 条
git diff --stat           # 概览变更文件
git blame <file>          # 查看每行的修改者
git remote prune origin   # 清理已不存在的远端 ref（合并后清理 stale ref）
git push origin --delete <stale-branch>  # 手动删某个远端分支
# 集中审计 squash/rebase merge 后未清理的分支 → 见 §2「批量审计：已合并分支清理」
```

### Tag 管理

```bash
git tag v1.0.0
git push origin v1.0.0
git tag -d v1.0.0        # 删除本地 tag
git push origin --delete v1.0.0  # 删除远程 tag
```

## 7. Issue 与 PR 命名规范

详细规范见 `references/issue-pr-format.md`，此处为速查。

本节只管理 GitHub Issue / PR 的命名和合并提交格式。项目常规任务状态、依赖和可领取判断仍由 `cross-agent-collab` 基于项目任务源维护。

### Issue 格式

```
<类型>: <描述>
```

| 类型 | 示例 |
|:-----|:-----|
| `feat` | `feat: skill-manager 支持版本检查` |
| `bug` | `bug: 解析空文件时崩溃` |
| `enhancement` | `enhancement: 添加批量导出` |
| `docs` | `docs: 更新使用说明` |
| `question` | `question: 能接入 xxx 吗` |

关闭时添加状态标记：`[done]`（自己）、`[resolved]`（外部）、`[wontfix]`、`[duplicate]`。

### PR 格式

```
<类型>(<模块>): <描述>
```

多 Skill 仓库必须带模块名：
```
feat(skill-manager): 添加版本检查功能
fix(pdf-processor): 修复大文件解析崩溃
docs(litigation-analysis): 更新模板文档
```

### PR 合并 Commit 格式

```
<类型>(<模块>): <描述> (#<PR编号>)
```

通过 API 执行 squash merge 时，`commit_title` 不会自动追加 `(#N)`，必须手动写入。

### 直接解决 Issue 的 Commit 格式

不是每个 Issue 都会通过“分支 + PR”解决。若用户要求直接在当前分支或 `main` 上修复/关闭某个 Issue，提交标题也必须显式带 Issue 编号，让 `git log --oneline` 能直接看出来源：

```text
<类型>(<模块>): <描述> (#<Issue编号>)
```

提交正文用关闭关键字绑定 GitHub Issue：

```text
Closes #<Issue编号>

- 关键变更 1
- 关键变更 2
```

示例：

```text
docs: 清理过期待定事项 (#1)

Closes #1

- 删除过期决策记录
- 清理不再需要的待定项
```

如果编号来自项目本地任务源，而不是 GitHub Issue，不要使用 `Closes #N` 误关 GitHub Issue；改用正文标注：

```text
Refs: project-task Issue #13
```

## 8. 提交规范

提交信息使用英文类型前缀 + 中文内容。每个 commit 必须有正文，不能只有标题。

### 与 git-batch-commit 的职责边界

`git-batch-commit` 是显式调用的提交快捷按钮，适合用户要求“git 提交 / 批量提交 / 拆分提交 / 整理提交”时，把已暂存变更按类型或模块拆成多个 commit。它可以把 GitHub Issue 写成标题后缀 `(#N)`，也可以在正文写 `Refs #N` 或本地任务引用。

`git-workflow` 是 Git 规则层，负责分支、PR、push、merge、安全门禁和 Issue 关闭语义。凡涉及“合并 PR”“拉 PR 到 main”“推送到远端”“关闭 Issue”“是否使用 `Closes #N`”，都以本 Skill 为准。

### Commit 格式

```text
<类型>: <标题>

- 关键变更 1
- 关键变更 2
```

### 支持类型

| 类型 | 用途 |
|------|------|
| `docs` | 文档变更 |
| `feat` | 新功能 |
| `fix` | Bug 修复 |
| `refactor` | 代码重构 |
| `style` | 代码风格变更 |
| `chore` | 构建工具、依赖、工具链 |
| `test` | 测试添加或修改 |
| `config` | 配置变更 |
| `license` | License 文件更新 |

### 多 Skill / 多模块规则

多 Skill 仓库必须在标题中写明模块名：

```text
feat(skill-name): 添加批量导出

- 新增导出入口
- 补充参数校验
```

一次修改涉及多个独立 Skill 或模块时，应拆成多个 commit。每个 commit 只表达一个目的。

## 10. 多 worktree 并行与 main worktree 占用

### 场景

并行推进多个任务时，主仓库目录（默认 attach 到 `main` 分支）与多个 PR worktree 同时存在。`gh pr merge` 在某些情况下会报 `'main' 已经被工作区 '<主仓库路径>' 使用`，原因是 gh CLI 检测到 `main` 分支被某个本地 worktree 检出（主仓库 attach 到 main）。这条 warning 常见于 cleanup 阶段，**不影响合并本身**（`mergedAt` 时间戳写入即成功）。

判断方法：

```bash
# 哪个 worktree 占用了 main？
git worktree list
# 输出示例：
# /path/to/main-repo           abc1234 [main]              ← 主仓库 attach 到 main
# /path/to/pr-45-worktree      def5678 [feat/xxx]         ← PR worktree 没事
# /path/to/main-worktree       9990000 [main]              ← 另一个 worktree 也 attach 到 main
```

### 解决方案三选一

#### 方案 A：主仓库不 attach 到 main（推荐）

让主仓库 attach 到一个长期开发分支（如 `develop`）或 detached，避免占用 main：

```bash
# 主仓库切到 develop
git checkout develop

# gh pr merge 在任意位置跑都不再受 main 占用影响
gh pr merge <N> --squash --delete-branch
```

**适用**：日常开发主仓库不直接在 main 上工作。

#### 方案 B：用 `git worktree add` 给 main 单独一个 worktree

主仓库 detached，专门开一个 `main` worktree：

```bash
# 主仓库 detached（不 attach 任何分支）
git checkout --detach HEAD

# main 用专门 worktree
git worktree add ~/.config/superpowers/worktrees/main main

# gh pr merge 在主仓库跑：main reference 现在属于独立 worktree，不冲突
gh pr merge <N> --squash --delete-branch
```

**适用**：希望保留 main 在本地随时可见，但要避免主仓库占用。

#### 方案 C：先释放 main 再合并

临时操作，merge 完恢复：

```bash
# 主仓库暂时切到其他分支（或 detached）
git checkout --detach HEAD

# 合并 PR
gh pr merge <N> --squash --delete-branch

# merge 完成后回到 main（如需要）
git checkout main
```

**适用**：一次性操作，不愿长期改动主仓库 attach 状态。

### 推荐

**方案 A 最简单**：让主仓库 attach 到长期分支（`develop` / `main-next` 等），`gh pr merge` 不再受 main 占用影响。`git worktree list` 命令随时可查 worktree 占用情况。

`gh pr merge` cleanup warning 时的快速判断流程：

```
1. 看 gh pr view <N> --json state,mergedAt,mergeCommit
   - state == "MERGED" + mergedAt 有值 + mergeCommit 有 oid → 合并成功，warning 可忽略
   - state != "MERGED" → 合并真失败，需重新执行

2. 看主仓库 git status --short
   - 干净 → 真合了
   - 有冲突标记 → 合并中途退出，需手工恢复
```

## 参考资源

- `references/branch-lifecycle-and-cleanup.md` — 一次性/长期分支判定、单 Worker 自动清理、批量 stale 审计与长期功能线关闭
- `references/long-lived-integration-branch.md` — 长期集成分支的适用条件、拓扑、同步方向、波次与里程碑门禁
- `references/issue-pr-format.md` — Issue 与 PR 命名详细规范
- `references/gh-cli-quickref.md` — gh CLI 常用命令速查
- `scripts/check-outgoing-identities.sh` — feature/PR push 前完整 PR range 的 author/committer 身份门禁
- `scripts/safe-push.sh` — 把身份核验绑定实际 immutable OID push
- `scripts/test-check-outgoing-identities.sh` — 身份门禁故障注入测试

