From 60a21ff133573aaaf980afdbc864b49fd274480d Mon Sep 17 00:00:00 2001 From: zerone0x Date: Sun, 29 Mar 2026 15:17:04 +0800 Subject: [PATCH 1/2] fix: fetch remote base branch before PR range context to prevent stale diffs When creating a pull request, the range context (commit log and diff) was computed against the local base branch ref (e.g. `main`), which can be stale if the user hasn't explicitly fetched or pulled it. This caused previously merged PR commits to appear in the range, leading to PR titles and bodies that included old, already-merged content. Now, `runPrStep` fetches the remote tracking ref for the base branch (e.g. `origin/main`) before computing the range context, ensuring the diff only includes commits not yet present upstream. Fixes #1487 Co-Authored-By: Claude Opus 4.6 --- apps/server/src/git/Layers/GitManager.ts | 35 +++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/apps/server/src/git/Layers/GitManager.ts b/apps/server/src/git/Layers/GitManager.ts index dc082674b7..ea89b4b6b6 100644 --- a/apps/server/src/git/Layers/GitManager.ts +++ b/apps/server/src/git/Layers/GitManager.ts @@ -902,7 +902,40 @@ export const makeGitManager = Effect.fn("makeGitManager")(function* () { } const baseBranch = yield* resolveBaseBranch(cwd, branch, details.upstreamRef, headContext); - const rangeContext = yield* gitCore.readRangeContext(cwd, baseBranch); + + // Fetch the remote tracking ref for the base branch so that the range + // context reflects any upstream changes (e.g. previously merged PRs). + // Without this fetch the local ref can be stale, causing readRangeContext + // to include commits/diffs that were already merged upstream (#1487). + const rangeRef = yield* Effect.gen(function* () { + // If baseBranch already contains a remote prefix, use it as-is. + if (baseBranch.includes("/")) { + return baseBranch; + } + // Try to fetch the base branch from the primary remote (usually "origin") + // and use the remote tracking ref for range computation. + const remoteName = "origin"; + const remoteRef = `${remoteName}/${baseBranch}`; + const fetched = yield* gitCore + .execute({ + operation: "runPrStep.fetchBaseBranch", + cwd, + args: [ + "fetch", + "--quiet", + "--no-tags", + remoteName, + `+refs/heads/${baseBranch}:refs/remotes/${remoteRef}`, + ], + timeoutMs: 30_000, + }) + .pipe( + Effect.map(() => true), + Effect.catch(() => Effect.succeed(false)), + ); + return fetched ? remoteRef : baseBranch; + }); + const rangeContext = yield* gitCore.readRangeContext(cwd, rangeRef); const generated = yield* textGeneration.generatePrContent({ cwd, From a00a0dc23ed6fbf742ff32bd7bbade66635a0ddf Mon Sep 17 00:00:00 2001 From: zerone0x Date: Sun, 29 Mar 2026 15:48:37 +0800 Subject: [PATCH 2/2] fix: use headContext.remoteName and verify remote ref after fetch Improve the base branch fetch logic by: - Using headContext.remoteName instead of hardcoding "origin" to support fork remotes correctly - Adding allowNonZeroExit to the fetch call for robustness - Verifying the remote ref exists with rev-parse after fetch - Wrapping the entire block in Effect.catch to fall back gracefully Co-Authored-By: Claude Opus 4.6 --- apps/server/src/git/Layers/GitManager.ts | 49 +++++++++++++----------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/apps/server/src/git/Layers/GitManager.ts b/apps/server/src/git/Layers/GitManager.ts index ea89b4b6b6..2611f85f97 100644 --- a/apps/server/src/git/Layers/GitManager.ts +++ b/apps/server/src/git/Layers/GitManager.ts @@ -912,29 +912,34 @@ export const makeGitManager = Effect.fn("makeGitManager")(function* () { if (baseBranch.includes("/")) { return baseBranch; } - // Try to fetch the base branch from the primary remote (usually "origin") - // and use the remote tracking ref for range computation. - const remoteName = "origin"; + // Try to fetch the base branch from the remote and use the remote + // tracking ref for range computation. Prefer the head context's + // remote when available (e.g. fork remotes) and fall back to "origin". + const remoteName = headContext.remoteName ?? "origin"; const remoteRef = `${remoteName}/${baseBranch}`; - const fetched = yield* gitCore - .execute({ - operation: "runPrStep.fetchBaseBranch", - cwd, - args: [ - "fetch", - "--quiet", - "--no-tags", - remoteName, - `+refs/heads/${baseBranch}:refs/remotes/${remoteRef}`, - ], - timeoutMs: 30_000, - }) - .pipe( - Effect.map(() => true), - Effect.catch(() => Effect.succeed(false)), - ); - return fetched ? remoteRef : baseBranch; - }); + yield* gitCore.execute({ + operation: "runPrStep.fetchBaseBranch", + cwd, + args: [ + "fetch", + "--quiet", + "--no-tags", + remoteName, + `+refs/heads/${baseBranch}:refs/remotes/${remoteRef}`, + ], + allowNonZeroExit: true, + timeoutMs: 30_000, + }); + // Verify the remote ref exists after fetch; if it does, prefer it. + const verifyResult = yield* gitCore.execute({ + operation: "runPrStep.verifyRemoteRef", + cwd, + args: ["rev-parse", "--verify", remoteRef], + allowNonZeroExit: true, + }); + return verifyResult.code === 0 ? remoteRef : baseBranch; + }).pipe(Effect.catch(() => Effect.succeed(baseBranch))); + const rangeContext = yield* gitCore.readRangeContext(cwd, rangeRef); const generated = yield* textGeneration.generatePrContent({