☰
get-shit-done 的 /gsd:pr-branch 工作流:用 git cherry-pick 构建无规划噪音的纯净 PR 分支
2026/10/3 22:22:21 网站建设 项目流程

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 定义了命令契约:

字段值
namegsd:pr-branch
descriptionCreate a clean PR branch by filtering out.planning/commits — ready for code review
argument-hint[target branch, default: main]
allowed-toolsBash、Read、AskUserQuestion
requiresreview

命令通过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 develop

docs/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 fi

git rev-list --count统计TARGET..CURRENT_BRANCH区间内的提交数,如果为 0,说明没有可过滤的提交,工作流直接优雅退出。通过后会打印操作横幅与分支信息:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ GSD ► PR BRANCH ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Branch: {CURRENT_BRANCH} Target: {TARGET} Commits: {AHEAD} ahead

Step 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/这些合入后必须保留的仓库规划状态**,导致合并后规划状态丢失。

该测试对工作流文档做了五组断言,正是当前实现的事实契约:

  1. 区分结构性 vs 瞬时规划提交:文档必须包含区分二者的语言(匹配structural、milestone archive、STATE.md INCLUDE等);
  2. 列出 STATE.md 与 ROADMAP.md 为需保留的结构性文件:文档正文必须包含这两个文件名;
  3. 列出 MILESTONES.md 或 milestones/ 为需保留的结构性文件:二者至少出现其一;
  4. 存在第四类"结构性提交":在原有的三类(代码 / 纯规划 / 混合)之外,必须有INCLUDE ... STATE.md之类的结构性提交归类表述;
  5. 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 闭环:

  1. 初始化:通过gsd-sdk query init.phase-op解析 phase 参数,读取branching_strategy、git.base_branch等配置;
  2. 预检:校验 VERIFICATION.md 状态为pass、工作树干净、位于特性分支、配置了origin远端、ghCLI 可用并已认证;
  3. 推送分支:git push origin ${CURRENT_BRANCH}(必要时--set-upstream);
  4. 生成 PR 正文:从 ROADMAP.md、SUMMARY.md、VERIFICATION.md、REQUIREMENTS.md、STATE.md 等规划产物自动组装 Summary / Changes / Requirements Addressed / Verification / Key Decisions 五大核心章节,并支持通过ship.pr_body_sections配置追加团队自定义 PRD 章节;
  5. 创建 PR:gh pr create --body-file(写入临时文件避免 shell 参数上限);
  6. 可选评审:支持配置外部评审命令workflow.code_review_command进行自动评审,也支持人工评审选项;
  7. 状态回写:更新 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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询