Skip to content

Commit fc5edd3

Browse files
t3dotggclaudejuliusmarminge
authored
fix(server): worktrees cut from a non-default base no longer show the base's PR (#22861)
Co-authored-by: Claude Opus 5.5 <[email protected]> Co-authored-by: Julius Marminge <[email protected]>
1 parent f697d4d commit fc5edd3

2 files changed

Lines changed: 49 additions & 8 deletions

File tree

‎apps/server/src/git/GitManager.test.ts‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2630,6 +2630,46 @@ it.layer(layerGitManagerTest)("GitManager", (it) => {
26302630
}),
26312631
);
26322632

2633+
it.effect(
2634+
"status does not inherit a release PR from a worktree's non-default base upstream",
2635+
() =>
2636+
Effect.gen(function* () {
2637+
const repoDir = yield* makeTempDir("t3code-git-manager-");
2638+
yield* initRepo(repoDir);
2639+
const remoteDir = yield* createBareRemote();
2640+
yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]);
2641+
yield* runGit(repoDir, ["push", "-u", "origin", "main"]);
2642+
yield* runGit(repoDir, ["push", "origin", "main:dev"]);
2643+
yield* runGit(repoDir, ["fetch", "origin"]);
2644+
yield* runGit(repoDir, ["remote", "set-head", "origin", "main"]);
2645+
yield* runGit(repoDir, ["checkout", "-b", "feature/from-dev", "origin/dev"]);
2646+
yield* runGit(repoDir, ["config", "branch.feature/from-dev.t3-merge-base", "dev"]);
2647+
2648+
const { manager, providerCalls } = yield* makeManager({
2649+
providerScenario: {
2650+
prListByHeadSelector: {
2651+
dev: [
2652+
{
2653+
number: 49,
2654+
title: "Release dev to main",
2655+
url: "https://github.com/pingdotgg/codething-mvp/pull/49",
2656+
baseRefName: "main",
2657+
headRefName: "dev",
2658+
state: "merged",
2659+
updatedAt: "2026-08-20T10:00:00Z",
2660+
},
2661+
],
2662+
},
2663+
},
2664+
});
2665+
2666+
const status = yield* manager.status({ cwd: repoDir });
2667+
expect(status.refName).toBe("feature/from-dev");
2668+
expect(status.pr).toBeNull();
2669+
expect(listedSelectors(providerCalls).includes("dev")).toBe(false);
2670+
}),
2671+
);
2672+
26332673
it.effect("status finds a PR pushed under the branch's own name despite a default upstream", () =>
26342674
Effect.gen(function* () {
26352675
const repoDir = yield* makeTempDir("t3code-git-manager-");

‎apps/server/src/git/GitManager.ts‎

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1787,7 +1787,7 @@ export const make = Effect.gen(function* () {
17871787
return remoteUrl === null ? null : remoteName;
17881788
});
17891789

1790-
// worktree add can set origin/main as upstream; this base is not the branch's published PR head.
1790+
// worktree add can set the branch's base as upstream; that is not its published PR head.
17911791
const resolveLookupHeadContext = Effect.fn("resolveLookupHeadContext")(function* (
17921792
cwd: string,
17931793
details: {
@@ -1809,15 +1809,16 @@ export const make = Effect.gen(function* () {
18091809
};
18101810
}
18111811
}
1812-
const upstreamHeadIsDefault =
1812+
if (headContext.headBranch === details.branch || headContext.isCrossRepository) {
1813+
return { headContext, lookup: true };
1814+
}
1815+
const upstreamHeadIsBase =
18131816
headContext.headBranch === details.defaultBranch ||
18141817
(details.defaultBranch === null &&
1815-
(headContext.headBranch === "main" || headContext.headBranch === "master"));
1816-
if (
1817-
headContext.headBranch === details.branch ||
1818-
!upstreamHeadIsDefault ||
1819-
headContext.isCrossRepository
1820-
) {
1818+
(headContext.headBranch === "main" || headContext.headBranch === "master")) ||
1819+
headContext.headBranch ===
1820+
(yield* gitCore.readBranchBase(cwd, details.branch).pipe(Effect.orElseSucceed(() => null)));
1821+
if (!upstreamHeadIsBase) {
18211822
return { headContext, lookup: true };
18221823
}
18231824
const remoteName = yield* findRemoteTrackingRemote(

0 commit comments

Comments
 (0)