fix(lanes): mergeSync commits a fully rerere-auto-resolved merge instead of rethrowing (E2)

Root cause: the catch block only fell through to the auto-commit path when
`unmergedFiles().length && MERGE_HEAD exists` — but a merge rerere resolved
completely has ZERO unmerged files (git already staged the resolution), so
that guard was always false and the raw git error was rethrown instead.
Found via an audit against the Shipyard source this was ported from.

Fixed by checking MERGE_HEAD first (unconditionally — its absence means the
merge never started, a real failure), then branching on whether any files
are still unmerged. Added a real rerere fixture test (teach a resolution,
recreate the identical conflict, confirm mergeSync auto-commits) — the
existing test suite had no coverage for this path.
This commit is contained in:
2026-08-05 14:39:02 +07:00
parent 02b90292c0
commit d2327408fb
2 changed files with 82 additions and 9 deletions
+5 -9
View File
@@ -259,18 +259,14 @@ async function mergeSync(lane, profile, branchArg) {
try {
await git(lane.cwd, ["merge", "--no-edit", `origin/${INTEGRATION_BRANCH}`]);
} catch (err) {
const conflicted = await unmergedFiles(lane.cwd);
const mergeHeadPath = path.join(await gitDir(lane.cwd), "MERGE_HEAD");
if (conflicted.length && fs.existsSync(mergeHeadPath)) {
if (!fs.existsSync(mergeHeadPath)) throw err; // merge never started — a real git failure
const conflicted = await unmergedFiles(lane.cwd);
if (conflicted.length) {
return { code: 4, conflictedFiles: conflicted };
}
throw err;
}
// rerere may have auto-resolved every conflict but left the merge
// uncommitted — finish it.
const mergeHeadPath = path.join(await gitDir(lane.cwd), "MERGE_HEAD");
if (fs.existsSync(mergeHeadPath) && !(await unmergedFiles(lane.cwd)).length) {
// rerere auto-resolved every conflict but left the merge uncommitted — finish it.
await git(lane.cwd, ["commit", "--no-edit"]);
}