From 197800e2d1a701ed0a537232567a089141175ab5 Mon Sep 17 00:00:00 2001 From: Dave Date: Sat, 24 Jan 2026 14:32:53 -0500 Subject: [PATCH] feat(git): add unified branching strategy option Add configurable git branching with three strategies: - none (default): commit to current branch - phase: create branch per phase (gsd/phase-{N}-{slug}) - milestone: create branch per milestone (gsd/{version}-{slug}) Changes: - planning-config.md: add git.branching_strategy and templates - execute-phase.md: handle branch creation based on strategy - settings.md: add branching strategy to settings UI - complete-milestone.md: handle branch merging at milestone end --- commands/gsd/settings.md | 19 ++- get-shit-done/references/planning-config.md | 84 +++++++++++++ get-shit-done/workflows/complete-milestone.md | 119 ++++++++++++++++++ get-shit-done/workflows/execute-phase.md | 75 +++++++++++ 4 files changed, 295 insertions(+), 2 deletions(-) diff --git a/commands/gsd/settings.md b/commands/gsd/settings.md index b4b14aa1c..f508e6066 100644 --- a/commands/gsd/settings.md +++ b/commands/gsd/settings.md @@ -34,6 +34,7 @@ Parse current values (default to `true` if not present): - `workflow.plan_check` — spawn plan checker during plan-phase - `workflow.verifier` — spawn verifier during execute-phase - `model_profile` — which model each agent uses (default: `balanced`) +- `git.branching_strategy` — branching approach (default: `"none"`) ## 3. Present Settings @@ -77,6 +78,16 @@ AskUserQuestion([ { label: "Yes", description: "Verify must-haves after execution" }, { label: "No", description: "Skip post-execution verification" } ] + }, + { + question: "Git branching strategy?", + header: "Branching", + multiSelect: false, + options: [ + { label: "None (Recommended)", description: "Commit directly to current branch" }, + { label: "Per Phase", description: "Create branch for each phase (gsd/phase-{N}-{name})" }, + { label: "Per Milestone", description: "Create branch for entire milestone (gsd/{version}-{name})" } + ] } ]) ``` @@ -95,6 +106,9 @@ Merge new settings into existing config.json: "research": true/false, "plan_check": true/false, "verifier": true/false + }, + "git": { + "branching_strategy": "none" | "phase" | "milestone" } } ``` @@ -116,6 +130,7 @@ Display: | Plan Researcher | {On/Off} | | Plan Checker | {On/Off} | | Execution Verifier | {On/Off} | +| Git Branching | {None/Per Phase/Per Milestone} | These settings apply to future /gsd:plan-phase and /gsd:execute-phase runs. @@ -130,7 +145,7 @@ Quick commands: - [ ] Current config read -- [ ] User presented with 4 settings (profile + 3 toggles) -- [ ] Config updated with model_profile and workflow section +- [ ] User presented with 5 settings (profile + 3 workflow toggles + git branching) +- [ ] Config updated with model_profile, workflow, and git sections - [ ] Changes confirmed to user diff --git a/get-shit-done/references/planning-config.md b/get-shit-done/references/planning-config.md index f55995b98..b0c65de88 100644 --- a/get-shit-done/references/planning-config.md +++ b/get-shit-done/references/planning-config.md @@ -7,6 +7,11 @@ Configuration options for `.planning/` directory behavior. "planning": { "commit_docs": true, "search_gitignored": false +}, +"git": { + "branching_strategy": "none", + "phase_branch_template": "gsd/phase-{phase}-{slug}", + "milestone_branch_template": "gsd/{milestone}-{slug}" } ``` @@ -14,6 +19,9 @@ Configuration options for `.planning/` directory behavior. |--------|---------|-------------| | `commit_docs` | `true` | Whether to commit planning artifacts to git | | `search_gitignored` | `false` | Add `--no-ignore` to broad rg searches | +| `git.branching_strategy` | `"none"` | Git branching approach: `"none"`, `"phase"`, or `"milestone"` | +| `git.phase_branch_template` | `"gsd/phase-{phase}-{slug}"` | Branch template for phase strategy | +| `git.milestone_branch_template` | `"gsd/{milestone}-{slug}"` | Branch template for milestone strategy | @@ -91,4 +99,80 @@ To use uncommitted mode: + + +**Branching Strategies:** + +| Strategy | When branch created | Branch scope | Merge point | +|----------|---------------------|--------------|-------------| +| `none` | Never | N/A | N/A | +| `phase` | At `execute-phase` start | Single phase | User merges after phase | +| `milestone` | At first `execute-phase` of milestone | Entire milestone | At `complete-milestone` | + +**When `git.branching_strategy: "none"` (default):** +- All work commits to current branch +- Standard GSD behavior + +**When `git.branching_strategy: "phase"`:** +- `execute-phase` creates/switches to a branch before execution +- Branch name from `phase_branch_template` (e.g., `gsd/phase-03-authentication`) +- All plan commits go to that branch +- User merges branches manually after phase completion +- `complete-milestone` offers to merge all phase branches + +**When `git.branching_strategy: "milestone"`:** +- First `execute-phase` of milestone creates the milestone branch +- Branch name from `milestone_branch_template` (e.g., `gsd/v1.0-mvp`) +- All phases in milestone commit to same branch +- `complete-milestone` offers to merge milestone branch to main + +**Template variables:** + +| Variable | Available in | Description | +|----------|--------------|-------------| +| `{phase}` | phase_branch_template | Zero-padded phase number (e.g., "03") | +| `{slug}` | Both | Lowercase, hyphenated name | +| `{milestone}` | milestone_branch_template | Milestone version (e.g., "v1.0") | + +**Checking the config:** + +```bash +# Get branching strategy (default: none) +BRANCHING_STRATEGY=$(cat .planning/config.json 2>/dev/null | grep -o '"branching_strategy"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*:.*"\([^"]*\)"/\1/' || echo "none") + +# Get phase branch template +PHASE_BRANCH_TEMPLATE=$(cat .planning/config.json 2>/dev/null | grep -o '"phase_branch_template"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*:.*"\([^"]*\)"/\1/' || echo "gsd/phase-{phase}-{slug}") + +# Get milestone branch template +MILESTONE_BRANCH_TEMPLATE=$(cat .planning/config.json 2>/dev/null | grep -o '"milestone_branch_template"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*:.*"\([^"]*\)"/\1/' || echo "gsd/{milestone}-{slug}") +``` + +**Branch creation:** + +```bash +# For phase strategy +if [ "$BRANCHING_STRATEGY" = "phase" ]; then + PHASE_SLUG=$(echo "$PHASE_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//') + BRANCH_NAME=$(echo "$PHASE_BRANCH_TEMPLATE" | sed "s/{phase}/$PADDED_PHASE/g" | sed "s/{slug}/$PHASE_SLUG/g") + git checkout -b "$BRANCH_NAME" 2>/dev/null || git checkout "$BRANCH_NAME" +fi + +# For milestone strategy +if [ "$BRANCHING_STRATEGY" = "milestone" ]; then + MILESTONE_SLUG=$(echo "$MILESTONE_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//') + BRANCH_NAME=$(echo "$MILESTONE_BRANCH_TEMPLATE" | sed "s/{milestone}/$MILESTONE_VERSION/g" | sed "s/{slug}/$MILESTONE_SLUG/g") + git checkout -b "$BRANCH_NAME" 2>/dev/null || git checkout "$BRANCH_NAME" +fi +``` + +**Use cases:** + +| Strategy | Best for | +|----------|----------| +| `none` | Solo development, simple projects | +| `phase` | Code review per phase, granular rollback, team collaboration | +| `milestone` | Release branches, staging environments, PR per version | + + + diff --git a/get-shit-done/workflows/complete-milestone.md b/get-shit-done/workflows/complete-milestone.md index 6a4d38ead..5f2d1a035 100644 --- a/get-shit-done/workflows/complete-milestone.md +++ b/get-shit-done/workflows/complete-milestone.md @@ -579,6 +579,125 @@ Progress: [updated progress bar] + + +Check if branching was used and offer merge options. + +**Check branching strategy:** + +```bash +# Get branching strategy from config +BRANCHING_STRATEGY=$(cat .planning/config.json 2>/dev/null | grep -o '"branching_strategy"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*:.*"\([^"]*\)"/\1/' || echo "none") +``` + +**If strategy is "none":** Skip to git_tag step. + +**For "phase" strategy — find phase branches:** + +```bash +PHASE_BRANCH_TEMPLATE=$(cat .planning/config.json 2>/dev/null | grep -o '"phase_branch_template"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*:.*"\([^"]*\)"/\1/' || echo "gsd/phase-{phase}-{slug}") + +# Extract prefix from template (before first variable) +BRANCH_PREFIX=$(echo "$PHASE_BRANCH_TEMPLATE" | sed 's/{.*//') + +# Find all phase branches for this milestone +PHASE_BRANCHES=$(git branch --list "${BRANCH_PREFIX}*" 2>/dev/null | sed 's/^\*//' | tr -d ' ') +``` + +**For "milestone" strategy — find milestone branch:** + +```bash +MILESTONE_BRANCH_TEMPLATE=$(cat .planning/config.json 2>/dev/null | grep -o '"milestone_branch_template"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*:.*"\([^"]*\)"/\1/' || echo "gsd/{milestone}-{slug}") + +# Extract prefix from template +BRANCH_PREFIX=$(echo "$MILESTONE_BRANCH_TEMPLATE" | sed 's/{.*//') + +# Find milestone branch +MILESTONE_BRANCH=$(git branch --list "${BRANCH_PREFIX}*" 2>/dev/null | sed 's/^\*//' | tr -d ' ' | head -1) +``` + +**If no branches found:** Skip to git_tag step. + +**If branches exist — present merge options:** + +``` +## Git Branches Detected + +Branching strategy: {phase/milestone} + +Branches found: +{list of branches} + +Options: +1. **Merge to main** — Merge branch(es) to main +2. **Delete without merging** — Branches already merged or not needed +3. **Keep branches** — Leave for manual handling +``` + +Use AskUserQuestion: + +``` +AskUserQuestion([ + { + question: "How should branches be handled?", + header: "Branches", + multiSelect: false, + options: [ + { label: "Merge to main", description: "Merge all branches to main sequentially" }, + { label: "Delete without merging", description: "Branches already merged or not needed" }, + { label: "Keep branches", description: "Leave branches for manual handling later" } + ] + } +]) +``` + +**If "Merge to main":** + +```bash +CURRENT_BRANCH=$(git branch --show-current) +git checkout main + +# For phase strategy - merge each phase branch +if [ "$BRANCHING_STRATEGY" = "phase" ]; then + for branch in $PHASE_BRANCHES; do + echo "Merging $branch..." + git merge --no-ff "$branch" -m "Merge branch '$branch' for v[X.Y]" + done +fi + +# For milestone strategy - merge milestone branch +if [ "$BRANCHING_STRATEGY" = "milestone" ]; then + echo "Merging $MILESTONE_BRANCH..." + git merge --no-ff "$MILESTONE_BRANCH" -m "Merge branch '$MILESTONE_BRANCH' for v[X.Y]" +fi + +git checkout "$CURRENT_BRANCH" +``` + +Report: "Merged branches to main" + +**If "Delete without merging":** + +```bash +if [ "$BRANCHING_STRATEGY" = "phase" ]; then + for branch in $PHASE_BRANCHES; do + git branch -d "$branch" 2>/dev/null || git branch -D "$branch" + done +fi + +if [ "$BRANCHING_STRATEGY" = "milestone" ]; then + git branch -d "$MILESTONE_BRANCH" 2>/dev/null || git branch -D "$MILESTONE_BRANCH" +fi +``` + +Report: "Deleted branches" + +**If "Keep branches":** + +Report: "Branches preserved for manual handling" + + + Create git tag for milestone: diff --git a/get-shit-done/workflows/execute-phase.md b/get-shit-done/workflows/execute-phase.md index 13aea74ba..f648aa9d8 100644 --- a/get-shit-done/workflows/execute-phase.md +++ b/get-shit-done/workflows/execute-phase.md @@ -65,6 +65,81 @@ git check-ignore -q .planning 2>/dev/null && COMMIT_PLANNING_DOCS=false ``` Store `COMMIT_PLANNING_DOCS` for use in git operations. + +**Load git branching config:** + +```bash +# Get branching strategy (default: none) +BRANCHING_STRATEGY=$(cat .planning/config.json 2>/dev/null | grep -o '"branching_strategy"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*:.*"\([^"]*\)"/\1/' || echo "none") + +# Get templates +PHASE_BRANCH_TEMPLATE=$(cat .planning/config.json 2>/dev/null | grep -o '"phase_branch_template"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*:.*"\([^"]*\)"/\1/' || echo "gsd/phase-{phase}-{slug}") +MILESTONE_BRANCH_TEMPLATE=$(cat .planning/config.json 2>/dev/null | grep -o '"milestone_branch_template"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*:.*"\([^"]*\)"/\1/' || echo "gsd/{milestone}-{slug}") +``` + +Store `BRANCHING_STRATEGY` and templates for use in branch creation step. + + + +Create or switch to appropriate branch based on branching strategy. + +**Skip if strategy is "none":** + +```bash +if [ "$BRANCHING_STRATEGY" = "none" ]; then + # No branching, continue on current branch + exit 0 +fi +``` + +**For "phase" strategy — create phase branch:** + +```bash +if [ "$BRANCHING_STRATEGY" = "phase" ]; then + # Get phase name from directory (e.g., "03-authentication" → "authentication") + PHASE_NAME=$(basename "$PHASE_DIR" | sed 's/^[0-9]*-//') + + # Create slug from phase name + PHASE_SLUG=$(echo "$PHASE_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//') + + # Apply template + BRANCH_NAME=$(echo "$PHASE_BRANCH_TEMPLATE" | sed "s/{phase}/$PADDED_PHASE/g" | sed "s/{slug}/$PHASE_SLUG/g") + + # Create or switch to branch + git checkout -b "$BRANCH_NAME" 2>/dev/null || git checkout "$BRANCH_NAME" + + echo "Branch: $BRANCH_NAME (phase branching)" +fi +``` + +**For "milestone" strategy — create/switch to milestone branch:** + +```bash +if [ "$BRANCHING_STRATEGY" = "milestone" ]; then + # Get current milestone info from ROADMAP.md + MILESTONE_VERSION=$(grep -oE 'v[0-9]+\.[0-9]+' .planning/ROADMAP.md | head -1 || echo "v1.0") + MILESTONE_NAME=$(grep -A1 "## .*$MILESTONE_VERSION" .planning/ROADMAP.md | tail -1 | sed 's/.*- //' | cut -d'(' -f1 | tr -d ' ' || echo "milestone") + + # Create slug + MILESTONE_SLUG=$(echo "$MILESTONE_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//') + + # Apply template + BRANCH_NAME=$(echo "$MILESTONE_BRANCH_TEMPLATE" | sed "s/{milestone}/$MILESTONE_VERSION/g" | sed "s/{slug}/$MILESTONE_SLUG/g") + + # Create or switch to branch (same branch for all phases in milestone) + git checkout -b "$BRANCH_NAME" 2>/dev/null || git checkout "$BRANCH_NAME" + + echo "Branch: $BRANCH_NAME (milestone branching)" +fi +``` + +**Report branch status:** + +``` +Branching: {strategy} → {branch_name} +``` + +**Note:** All subsequent plan commits go to this branch. User handles merging based on their workflow.