get-shit-done 的 /gsd:pr-branch 工作流:用 git cherry-pick 构建无规划噪音的纯净 PR 分支
【免费下载链接】get-shit-doneA light-weight and powerful meta-prompting, context engineering and spec-driven development system for Claude Code by TÂCHES.项目地址: https://gitcode.com/GitHub_Trending/getshi/get-shit-done
导读
在 get-shit-done(GSD)的 spec-driven 开发流程中,每个 phase 的执行都会在.planning/目录下沉淀大量中间产物——PLAN.md、SUMMARY.md、CONTEXT.md、RESEARCH.md 等。如果直接把这些提交推上 PR,reviewer 看到的 diff 会被规划噪音淹没,真正的代码变更反而难以聚焦。本文讲解 GSD 的pr-branch工作流:它如何通过git cherry-pick加路径过滤,从目标分支重建一条仅包含代码变更与结构性规划状态的干净分支,让 reviewer 只看到值得评审的内容。读完本文,你将掌握该工作流的完整执行流程、提交四分类判定规则、关键 git 命令用法,以及它在仓库中的实现与回归测试证据。
一、问题背景:.planning/产物为何会污染 PR
GSD 的规划驱动开发会在.planning/下生成两类性质完全不同的文件:
- 结构性规划状态:
.planning/STATE.md、.planning/ROADMAP.md、.planning/MILESTONES.md、.planning/PROJECT.md、.planning/REQUIREMENTS.md、.planning/milestones/**,它们描述仓库的规划全貌,合入后需要保留; - 瞬时规划产物:
.planning/phases/**(PLAN.md、SUMMARY.md、CONTEXT.md、RESEARCH.md 等)、.planning/quick/**、.planning/research/**、.planning/threads/**、.planning/todos/**、.planning/debug/**、.planning/seeds/**、.planning/codebase/**、.planning/ui-reviews/**,它们只是执行过程的中间痕迹,对代码评审毫无价值。
如果直接以特性分支发起 PR,上述两类文件都会进入 diff,reviewer 被迫在大量规划文档中寻找代码改动。pr-branch工作流正是为解决这一噪音问题而设计——但它的处理并非一刀切:结构性规划状态必须保留(否则合入后仓库的规划状态会丢失),只有瞬时产物被过滤。
二、命令入口:/gsd:pr-branch
工作流由 slash 命令 commands/gsd/pr-branch.md 触发,其 frontmatter 定义了命令契约:
| 字段 | 值 |
|---|---|
name | gsd:pr-branch |
description | Create a clean PR branch by filtering out.planning/commits — ready for code review |
argument-hint | [target branch, default: main] |
allowed-tools | Bash、Read、AskUserQuestion |
requires | review |
命令通过execution_context引用@~/.claude/get-shit-done/workflows/pr-branch.md(即仓库中的 get-shit-done/workflows/pr-branch.md),并指示"Execute end-to-end"——即按工作流文档的 step 顺序完整执行。
参数与用法
工作流接受一个可选参数:目标分支(target branch),默认main。仓库文档 docs/COMMANDS.md 给出了两个典型调用:
/gsd-pr-branch # Filter against main /gsd-pr-branch develop # Filter against developdocs/FEATURES.md的 "PR Branch Filtering"(第 45 节)为该功能定义了三条需求,可作为验收依据:
- REQ-PRBRANCH-01:系统 MUST 识别只修改
.planning/文件的提交; - REQ-PRBRANCH-02:系统 MUST 创建过滤掉规划提交的新分支;
- REQ-PRBRANCH-03:代码变更 MUST 与提交时完全一致地被保留。
三、执行流程:四步构建干净 PR 分支
pr-branch工作流的核心思路是:不直接改写当前分支历史,而是从目标分支新建一条{current}-pr分支,用git cherry-pick按提交逐个重建,只搬入应包含的提交。整个流程分为四个 step。
Step 1:状态检测(detect_state)
首先解析参数并确定当前分支与目标分支:
CURRENT_BRANCH=$(git branch --show-current) TARGET=${1:-main}前置条件检查:
- 必须位于特性分支(不能在 main/master 上);
- 当前分支必须领先于目标分支。
AHEAD=$(git rev-list --count "$TARGET".."$CURRENT_BRANCH" 2>/dev/null) if [ "$AHEAD" = "0" ]; then echo "No commits ahead of $TARGET — nothing to filter." exit 0 figit rev-list --count统计TARGET..CURRENT_BRANCH区间内的提交数,如果为 0,说明没有可过滤的提交,工作流直接优雅退出。通过后会打印操作横幅与分支信息:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ GSD ► PR BRANCH ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Branch: {CURRENT_BRANCH} Target: {TARGET} Commits: {AHEAD} aheadStep 2:提交分析(analyze_commits)
先列出目标分支之后的所有非合并提交:
git log --oneline "$TARGET".."$CURRENT_BRANCH" --no-merges然后对每个提交逐一检查其触达的文件,用三个计数完成分类:
# For each commit hash FILES=$(git diff-tree --no-commit-id --name-only -r $HASH) NON_PLANNING=$(echo "$FILES" | grep -v "^\.planning/" | wc -l) STRUCTURAL=$(echo "$FILES" | grep -E "^\.planning/(STATE|ROADMAP|MILESTONES|PROJECT|REQUIREMENTS)\.md|^\.planning/milestones/" | wc -l) TRANSIENT_ONLY=$(echo "$FILES" | grep "^\.planning/" | grep -vE "^\.planning/(STATE|ROADMAP|MILESTONES|PROJECT|REQUIREMENTS)\.md|^\.planning/milestones/" | wc -l)git diff-tree --name-only -r输出提交修改的文件清单;三条grep -v/grep -E管道分别统计"非规划文件数"、"结构性规划文件数"、"纯瞬时规划文件数"。基于这些计数,提交被归入四类:
| 类别 | 判定规则 | 处理 |
|---|---|---|
| 代码提交 | 至少触达一个非.planning/文件 | ✅ INCLUDE |
| 结构性规划提交 | 只触达结构性文件(STATE/ROADMAP/MILESTONES/PROJECT/REQUIREMENTS.md、milestones/**) | ✅ INCLUDE |
| 瞬时规划提交 | 只触达瞬时规划文件(phases/、quick/、research/ 等) | ❌ EXCLUDE |
| 混合提交 | 同时触达代码与任意规划文件 | ✅ INCLUDE(瞬时规划变更随行带入,可接受) |
分类完成后展示分析汇总:
Commits to include: {N} (code changes + structural planning) Commits to exclude: {N} (transient planning-only) Mixed commits: {N} (code + planning — included) Structural planning commits: {N} (STATE/ROADMAP/milestone updates — included)这一"结构性 vs 瞬时"的区分是整个工作流的关键设计——它来自对 bug #2004 的修复,下文第五节详述。
Step 3:创建 PR 分支(create_pr_branch)
从目标分支切出新分支,命名规则为{当前分支名}-pr:
PR_BRANCH="${CURRENT_BRANCH}-pr" # Create PR branch from target git checkout -b "$PR_BRANCH" "$TARGET"然后按原始顺序对"代码提交 + 结构性规划提交"逐一 cherry-pick:
for HASH in $CODE_AND_STRUCTURAL_COMMITS; do git cherry-pick "$HASH" --no-commit # Remove only transient .planning/ subdirectories that came along in mixed commits. # DO NOT remove structural files (STATE.md, ROADMAP.md, MILESTONES.md, PROJECT.md, # REQUIREMENTS.md, milestones/) — these must survive into the PR branch. for dir in phases quick research threads todos debug seeds codebase ui-reviews; do git rm -r --cached ".planning/$dir/" 2>/dev/null || true done git commit -C "$HASH" done这里有几个值得细读的工程细节:
--no-commit:先把提交内容应用到暂存区但不产生提交,便于在提交前做路径清理;git rm -r --cached只针对瞬时子目录:--cached表示只从索引移除、不动工作区文件;目录清单phases quick research threads todos debug seeds codebase ui-reviews与 Step 2 的瞬时分类完全对应。注释明确强调:绝不删除结构性文件(STATE.md、ROADMAP.md、MILESTONES.md、PROJECT.md、REQUIREMENTS.md、milestones/),它们必须存活到 PR 分支;2>/dev/null || true:某些瞬时目录在本次 cherry-pick 中可能不存在,git rm报错被静默吞掉,保证循环不中断;git commit -C "$HASH":-C复用原提交的 author 与 message,确保提交信息从原始提交原样保留;- 循环结束后
git checkout "$CURRENT_BRANCH"回到原分支,当前分支历史不受任何影响。
Step 4:验证(verify)
创建完成后做三项自检,确认 PR 分支确实"干净":
# Verify no .planning/ files in PR branch PLANNING_FILES=$(git diff --name-only "$TARGET".."$PR_BRANCH" | grep "^\.planning/" | wc -l) TOTAL_FILES=$(git diff --name-only "$TARGET".."$PR_BRANCH" | wc -l) PR_COMMITS=$(git rev-list --count "$TARGET".."$PR_BRANCH")PLANNING_FILES:PR 分支 diff 中残留的.planning/文件数,应为 0;TOTAL_FILES:PR 分支实际携带的文件总数;PR_COMMITS:PR 分支相对目标分支的提交数(应小于等于原领先数)。
最后展示结果与后续操作指引:
✅ PR branch created: {PR_BRANCH} Original: {AHEAD} commits, {ORIGINAL_FILES} files PR branch: {PR_COMMITS} commits, {TOTAL_FILES} files Planning files: {PLANNING_FILES} (should be 0) Next steps: git push origin {PR_BRANCH} gh pr create --base {TARGET} --head {PR_BRANCH} Or use /gsd:ship to create the PR automatically.成功标准
工作流文档末尾定义了五条 success criteria,也是判定执行是否完整的清单:
- PR 分支已从目标分支创建
- 纯规划提交已被排除
- PR 分支 diff 中无
.planning/文件 - 提交信息从原始提交保留
- 用户已看到后续操作指引
四、落地到提交:cherry-pick 在仓库中的佐证
pr-branch使用的git cherry-pick重建历史手法,在仓库其他发布流程中也有对应实现,可作为理解该机制原理的旁证。例如 tests/bug-2964-release-sdk-empty-cherry-pick.test.cjs 展示了 cherry-pick 在遇到空提交时的行为:git cherry-pick -x在空提交上会以非零退出("The previous cherry-pick is now empty"),因此发布流程需要--allow-empty --keep-redundant-commits标志兜底;而 tests/bug-2966-cherry-pick-context-missing.test.cjs 则处理 cherry-pick 失败后的冲突分类与git cherry-pick --skip跳过路径。这些测试证明:cherry-pick 并非无脑可用,失败场景(空提交、冲突)需要显式处理——pr-branch之所以使用--no-commit+git commit -C的"先暂存、清理、再提交"三步式,正是为了避开直接 cherry-pick 的中间状态陷阱,让路径清理可控。
五、源码级设计验证:bug #2004 回归测试
pr-branch的"结构性 / 瞬时"二元划分并非一开始就有。仓库中的回归测试 tests/bug-2004-pr-branch-milestone.test.cjs 记录了这一演进:旧实现会过滤掉所有仅含.planning/的提交,包括 STATE.md、ROADMAP.md、MILESTONES.md、milestones/这些合入后必须保留的仓库规划状态**,导致合并后规划状态丢失。
该测试对工作流文档做了五组断言,正是当前实现的事实契约:
- 区分结构性 vs 瞬时规划提交:文档必须包含区分二者的语言(匹配
structural、milestone archive、STATE.md INCLUDE等); - 列出 STATE.md 与 ROADMAP.md 为需保留的结构性文件:文档正文必须包含这两个文件名;
- 列出 MILESTONES.md 或 milestones/ 为需保留的结构性文件:二者至少出现其一;
- 存在第四类"结构性提交":在原有的三类(代码 / 纯规划 / 混合)之外,必须有
INCLUDE ... STATE.md之类的结构性提交归类表述; create_pr_branch不得无差别rm -r --cached .planning/:用正则断言git rm -r --cached .planning/必须是限定到瞬时子目录的形式(如.planning/phases/、.planning/quick/),不允许出现不带目录限定的整体删除——这正是 bug #2004 的根因所在。
对照 get-shit-done/workflows/pr-branch.md 正文可以看到,Step 3 的删除循环确实只遍历phases quick research threads todos debug seeds codebase ui-reviews这九个瞬时目录,与测试要求的限定式rm完全吻合。
六、衔接/gsd:ship:从干净分支到合并闭环
pr-branch的输出是"可以发起评审的分支",而真正把工作合入 PR 的流程由/gsd:ship承接(命令入口见 commands/gsd/ship.md,工作流见 get-shit-done/workflows/ship.md)。ship 工作流完成 plan → execute → verify → ship 闭环:
- 初始化:通过
gsd-sdk query init.phase-op解析 phase 参数,读取branching_strategy、git.base_branch等配置; - 预检:校验 VERIFICATION.md 状态为
pass、工作树干净、位于特性分支、配置了origin远端、ghCLI 可用并已认证; - 推送分支:
git push origin ${CURRENT_BRANCH}(必要时--set-upstream); - 生成 PR 正文:从 ROADMAP.md、SUMMARY.md、VERIFICATION.md、REQUIREMENTS.md、STATE.md 等规划产物自动组装 Summary / Changes / Requirements Addressed / Verification / Key Decisions 五大核心章节,并支持通过
ship.pr_body_sections配置追加团队自定义 PRD 章节; - 创建 PR:
gh pr create --body-file(写入临时文件避免 shell 参数上限); - 可选评审:支持配置外部评审命令
workflow.code_review_command进行自动评审,也支持人工评审选项; - 状态回写:更新 STATE.md 的 Last Activity 与 Status。
对照关系很清晰:pr-branch负责"让 PR 干净",ship负责"让 PR 完整"——两者配合,reviewer 看到的是没有规划噪音、但带有完整规划上下文摘要的最终 PR。
七、使用建议与注意事项
- 分支命名约定:PR 分支固定为
{当前分支名}-pr,与原始特性分支一一对应,不会覆盖原分支历史; - 混合提交的处理:同时含代码与瞬时规划文件的提交会整体保留(瞬时变更随行),这是"重代码轻噪音"的权衡——避免为剥离规划文件而拆散原子提交;
- 结构性状态必须保留:合入 PR 分支的 STATE.md、ROADMAP.md、MILESTONES.md、milestones/** 会在 merge 后继续为 GSD 的规划流程提供状态依据,删除它们等同于丢失仓库的规划记忆;
- 验证指标:执行完毕后应确认
Planning files: 0、提交信息完整(git commit -C保证)、原分支未被动过; - 适用范围:该工作流假定
.planning/目录约定存在(GSD 项目均满足),且目标分支是相对干净的分支(如main/develop);若目标分支本身已包含大量.planning/历史,diff 基线的选择会直接影响过滤效果,此时建议明确传入正确的目标分支参数。
总结
pr-branch工作流通过"提交四分类 + cherry-pick 路径重建"的组合拳,在不改写原分支历史、不丢失结构性规划状态的前提下,为 reviewer 产出一条只含代码变更的纯净 PR 分支。其设计要点可概括为三条:结构性规划文件是仓库资产,必须保留;瞬时规划文件是评审噪音,必须剔除;混合提交以代码为准,整体带入。这套机制既有 get-shit-done/workflows/pr-branch.md 的可执行步骤,又有 tests/bug-2004-pr-branch-milestone.test.cjs 的回归测试背书,是 spec-driven 开发流程中"评审友好度"与"规划状态完整性"二者兼得的实用范本。
【免费下载链接】get-shit-doneA light-weight and powerful meta-prompting, context engineering and spec-driven development system for Claude Code by TÂCHES.项目地址: https://gitcode.com/GitHub_Trending/getshi/get-shit-done
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考