Merge pull request #3401 from gsd-build/claude/resolve-pr-3322-conflict-GQXIc

fix(worktree): resolve merge conflict for PR #3322 — adopt comprehensive pre-merge cleanup
This commit is contained in:
Tom Boucher 2026-04-01 11:31:31 -04:00 committed by GitHub
commit adaa661a87
2 changed files with 43 additions and 7 deletions

View file

@ -1566,14 +1566,17 @@ export function mergeMilestoneToMain(
// Non-fatal — proceed with merge; untracked files may block it
}
// 7c. Clean stale MERGE_HEAD before the squash merge (#2912).
// The native (libgit2) merge path or a prior interrupted merge may leave
// MERGE_HEAD in the git dir. `git merge --squash` refuses to run when
// MERGE_HEAD exists, so remove it preemptively.
// 7b. Clean up stale merge state before attempting squash merge (#2912).
// A leftover MERGE_HEAD (from a previous failed merge, libgit2 native path,
// or interrupted operation) causes `git merge --squash` to refuse with
// "fatal: You have not concluded your merge (MERGE_HEAD exists)".
// Defensively remove merge artifacts before starting.
try {
const gitDirPre = resolveGitDir(originalBasePath_);
const mergeHeadPre = join(gitDirPre, "MERGE_HEAD");
if (existsSync(mergeHeadPre)) unlinkSync(mergeHeadPre);
const gitDir_ = resolveGitDir(originalBasePath_);
for (const f of ["SQUASH_MSG", "MERGE_MSG", "MERGE_HEAD"]) {
const p = join(gitDir_, f);
if (existsSync(p)) unlinkSync(p);
}
} catch { /* best-effort */ }
// 8. Squash merge — auto-resolve .gsd/ state file conflicts (#530)

View file

@ -739,6 +739,39 @@ describe("auto-worktree-milestone-merge", { timeout: 300_000 }, () => {
);
});
test("#2912: stale SQUASH_MSG and MERGE_MSG are cleaned before squash merge", () => {
// Verifies that the pre-merge cleanup (step 7b) removes all three merge
// artifacts — not just MERGE_HEAD — so that `git merge --squash` never
// encounters leftover state from a prior interrupted operation.
const repo = freshRepo();
const wtPath = createAutoWorktree(repo, "M294");
addSliceToMilestone(repo, wtPath, "M294", "S01", "Feature C", [
{ file: "feature-c.ts", content: "export const c = true;\n", message: "add feature c" },
]);
const roadmap = makeRoadmap("M294", "Stale merge artifacts", [
{ id: "S01", title: "Feature C" },
]);
// Plant stale merge artifacts in the git dir to simulate a prior
// interrupted merge. The pre-merge cleanup must remove all of them.
const gitDir = join(repo, ".git");
writeFileSync(join(gitDir, "SQUASH_MSG"), "stale squash message\n");
writeFileSync(join(gitDir, "MERGE_MSG"), "stale merge message\n");
mergeMilestoneToMain(repo, "M294", roadmap);
assert.ok(
!existsSync(join(gitDir, "SQUASH_MSG")),
"#2912: stale SQUASH_MSG must be removed by pre-merge cleanup",
);
assert.ok(
!existsSync(join(gitDir, "MERGE_MSG")),
"#2912: stale MERGE_MSG must be removed by pre-merge cleanup",
);
});
test("#1906: codeFilesChanged=true when real code is merged", () => {
const repo = freshRepo();
const wtPath = createAutoWorktree(repo, "M190");