From 13979e9c386ec2b403562e0846ef18f175fcae14 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Mon, 14 Sep 2026 23:02:45 +0200 Subject: [PATCH] fix(server): discover fork PRs with URL-valued branch remotes (#4862) Resolve direct repository URLs as well as named remotes when deriving checkout PR identity, preserving fork-owner filtering and batched polling. --- .../server/src/utils/checkout-git.test.ts | 30 +++++++++++++++++++ packages/server/src/utils/checkout-git.ts | 23 ++++++++++++-- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/packages/server/src/utils/checkout-git.test.ts b/packages/server/src/utils/checkout-git.test.ts index f83ee46ae5..b5f505ea2d 100644 --- a/packages/server/src/utils/checkout-git.test.ts +++ b/packages/server/src/utils/checkout-git.test.ts @@ -2991,6 +2991,36 @@ const x = 1; }); }); + it.each([ + "git@github.com:contributor/paseo.git", + "https://github.com/contributor/paseo.git", + "ssh://git@github.com/contributor/paseo.git", + ])("preserves fork PR identity with branch.remote=%s", async (branchRemote) => { + execFileSync("git", ["remote", "add", "origin", "git@github.com:getpaseo/paseo.git"], { + cwd: repoDir, + }); + execFileSync("git", ["checkout", "-b", "topic"], { cwd: repoDir }); + execFileSync("git", ["config", "branch.topic.remote", branchRemote], { + cwd: repoDir, + }); + execFileSync("git", ["config", "branch.topic.pushRemote", branchRemote], { + cwd: repoDir, + }); + execFileSync("git", ["config", "branch.topic.merge", "refs/heads/topic"], { + cwd: repoDir, + }); + const headSha = execFileSync("git", ["rev-parse", "HEAD"], { + cwd: repoDir, + encoding: "utf8", + }).trim(); + + expect(await readPullRequestLookupTargetFromFacts(repoDir, paseoHome)).toEqual({ + headRef: "topic", + headRepositoryOwner: "contributor", + headSha, + }); + }); + it("does not attach an owner when the tracked remote is the same GitHub repository", async () => { execFileSync("git", ["checkout", "-b", "local-feature"], { cwd: repoDir }); execFileSync("git", ["remote", "add", "origin", "git@github.com:getpaseo/paseo.git"], { diff --git a/packages/server/src/utils/checkout-git.ts b/packages/server/src/utils/checkout-git.ts index 8e2a89f0bd..e1246a90a5 100644 --- a/packages/server/src/utils/checkout-git.ts +++ b/packages/server/src/utils/checkout-git.ts @@ -1369,6 +1369,23 @@ async function getGitConfigValue( } } +async function resolveGitRemoteUrl({ + cwd, + remote, + context, +}: { + cwd: string; + remote: string; + context?: CheckoutContext; +}): Promise { + const configuredUrl = await getGitConfigValue(cwd, `remote.${remote}.url`, context); + if (configuredUrl) { + return configuredUrl; + } + // Branch remote settings accept repository URLs as well as configured remote names. + return parseGitRemoteLocation(remote) ? remote : null; +} + async function getGitRemotePushUrl( cwd: string, remoteName: string, @@ -1433,7 +1450,7 @@ async function resolvePullRequestStatusLookupTarget( } const [branchRemoteUrl, originRemoteUrl, resolvedBaseRef] = await Promise.all([ - branchRemoteName ? getGitConfigValue(cwd, `remote.${branchRemoteName}.url`, context) : null, + branchRemoteName ? resolveGitRemoteUrl({ cwd, remote: branchRemoteName, context }) : null, getGitConfigValue(cwd, "remote.origin.url", context), getResolvedBaseRefForCwd(cwd, context), ]); @@ -1950,7 +1967,7 @@ async function resolvePullRequestLookupTargetFromPushConfig( const [pushRefspec, pushRemoteUrl, originRemoteUrl, resolvedBaseRef] = await Promise.all([ getGitConfigValue(cwd, `remote.${pushRemoteName}.push`, context), - getGitConfigValue(cwd, `remote.${pushRemoteName}.url`, context), + resolveGitRemoteUrl({ cwd, remote: pushRemoteName, context }), knownOriginRemoteUrl === null ? getGitConfigValue(cwd, "remote.origin.url", context) : null, knownResolvedBaseRef === null ? getResolvedBaseRefForCwd(cwd, context) : null, ]); @@ -2058,7 +2075,7 @@ export async function getCheckoutSnapshotFacts( getGitConfigValue(cwd, `branch.${inspected.currentBranch}.merge`, context), branchRemoteName === "origin" ? inspected.remoteUrl - : getGitConfigValue(cwd, `remote.${branchRemoteName}.url`, context), + : resolveGitRemoteUrl({ cwd, remote: branchRemoteName, context }), ]); } }