fix: worktree created from integration branch, not main (#606) (#607)

This commit is contained in:
Tom Boucher 2026-03-16 08:35:28 -04:00 committed by GitHub
parent 7e0cdec672
commit 27cfababdb
2 changed files with 19 additions and 9 deletions

View file

@ -17,6 +17,7 @@ import {
import { detectWorktreeName } from "./worktree.js";
import {
MergeConflictError,
readIntegrationBranch,
} from "./git-service.js";
import { parseRoadmap } from "./files.js";
import { loadEffectiveGSDPreferences } from "./preferences.js";
@ -91,7 +92,12 @@ export function autoWorktreeBranch(milestoneId: string): string {
*/
export function createAutoWorktree(basePath: string, milestoneId: string): string {
const branch = autoWorktreeBranch(milestoneId);
const info = createWorktree(basePath, milestoneId, { branch });
// Use the integration branch recorded in META.json as the start point.
// This ensures the worktree branch is created from the branch the user
// was on when they started the milestone (e.g. f-setup-gsd-2), not main.
const integrationBranch = readIntegrationBranch(basePath, milestoneId) ?? undefined;
const info = createWorktree(basePath, milestoneId, { branch, startPoint: integrationBranch });
// Copy .gsd/ planning artifacts from the source repo into the new worktree.
// Worktrees are fresh git checkouts — untracked files don't carry over.
@ -301,11 +307,12 @@ export function mergeMilestoneToMain(
const previousCwd = process.cwd();
process.chdir(originalBasePath_);
// 4. Resolve main branch from preferences
// 4. Resolve integration branch — prefer milestone metadata, fall back to preferences / "main"
const prefs = loadEffectiveGSDPreferences()?.preferences?.git ?? {};
const mainBranch = prefs.main_branch || "main";
const integrationBranch = readIntegrationBranch(originalBasePath_, milestoneId);
const mainBranch = integrationBranch ?? prefs.main_branch ?? "main";
// 5. Checkout main
// 5. Checkout integration branch
nativeCheckoutBranch(originalBasePath_, mainBranch);
// 6. Build rich commit message

View file

@ -94,7 +94,7 @@ export function worktreeBranchName(name: string): string {
*
* @param opts.branch override the default `worktree/<name>` branch name
*/
export function createWorktree(basePath: string, name: string, opts: { branch?: string } = {}): WorktreeInfo {
export function createWorktree(basePath: string, name: string, opts: { branch?: string; startPoint?: string } = {}): WorktreeInfo {
// Validate name: alphanumeric, hyphens, underscores only
if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
throw new Error(`Invalid worktree name "${name}". Use only letters, numbers, hyphens, and underscores.`);
@ -114,9 +114,12 @@ export function createWorktree(basePath: string, name: string, opts: { branch?:
// Prune any stale worktree entries from a previous removal
nativeWorktreePrune(basePath);
// Use the explicit start point (e.g. integration branch) if provided,
// otherwise fall back to the repo's detected main branch.
const startPoint = opts.startPoint ?? nativeDetectMainBranch(basePath);
// Check if the branch already exists (leftover from a previous worktree)
const branchAlreadyExists = nativeBranchExists(basePath, branch);
const mainBranch = nativeDetectMainBranch(basePath);
if (branchAlreadyExists) {
// Check if the branch is actively used by an existing worktree.
@ -130,11 +133,11 @@ export function createWorktree(basePath: string, name: string, opts: { branch?:
);
}
// Reset the stale branch to current main, then attach worktree to it
nativeBranchForceReset(basePath, branch, mainBranch);
// Reset the stale branch to the start point, then attach worktree to it
nativeBranchForceReset(basePath, branch, startPoint);
nativeWorktreeAdd(basePath, wtPath, branch);
} else {
nativeWorktreeAdd(basePath, wtPath, branch, true, mainBranch);
nativeWorktreeAdd(basePath, wtPath, branch, true, startPoint);
}
return {