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.
This commit is contained in:
Mohamed Boudra
2026-09-14 23:02:45 +02:00
committed by GitHub
parent 77c5c8f17e
commit 13979e9c38
2 changed files with 50 additions and 3 deletions
@@ -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"], {
+20 -3
View File
@@ -1369,6 +1369,23 @@ async function getGitConfigValue(
}
}
async function resolveGitRemoteUrl({
cwd,
remote,
context,
}: {
cwd: string;
remote: string;
context?: CheckoutContext;
}): Promise<string | null> {
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 }),
]);
}
}