From c9a05208bd8e9f434af6a50011d3fbd091b2d4d2 Mon Sep 17 00:00:00 2001 From: User Date: Mon, 23 Feb 2026 05:42:39 +0800 Subject: [PATCH] fix: handle fork PRs by fetching via refs/pull/N/head (#962) When a PR originates from a fork, `git fetch origin ` fails because the branch only exists on the fork's remote. Fix: detect cross-repository PRs via the `isCrossRepository` GraphQL field and fetch using `pull//head:` refspec instead, which is the standard GitHub mechanism for accessing fork PR branches. Changes: - Add `isCrossRepository` and `headRepository` to PR GraphQL query - Add corresponding fields to GitHubPullRequest type - Branch checkout uses pull ref for fork PRs - Update test fixtures with new fields --- src/github/api/queries/github.ts | 7 +++++++ src/github/operations/branch.ts | 20 +++++++++++++++++--- src/github/types.ts | 7 +++++++ test/create-prompt.test.ts | 2 ++ test/data-fetcher.test.ts | 2 ++ test/data-formatter.test.ts | 2 ++ test/pull-request-target.test.ts | 2 ++ 7 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/github/api/queries/github.ts b/src/github/api/queries/github.ts index 7bceb8f9d..08dc25b58 100644 --- a/src/github/api/queries/github.ts +++ b/src/github/api/queries/github.ts @@ -12,6 +12,13 @@ export const PR_QUERY = ` baseRefName headRefName headRefOid + isCrossRepository + headRepository { + owner { + login + } + name + } createdAt updatedAt lastEditedAt diff --git a/src/github/operations/branch.ts b/src/github/operations/branch.ts index 86197da96..e4aea1942 100644 --- a/src/github/operations/branch.ts +++ b/src/github/operations/branch.ts @@ -164,9 +164,23 @@ export async function setupBranch( // Validate branch names before use to prevent command injection validateBranchName(branchName); - // Execute git commands to checkout PR branch (dynamic depth based on PR size) - // Using execFileSync instead of shell template literals for security - execGit(["fetch", "origin", `--depth=${fetchDepth}`, branchName]); + // For cross-repository (fork) PRs, fetch via the pull ref since the + // branch only exists on the fork's remote, not on origin. + if (prData.isCrossRepository) { + console.log( + `PR #${entityNumber} is from a fork, fetching via refs/pull/${entityNumber}/head...`, + ); + execGit([ + "fetch", + "origin", + `--depth=${fetchDepth}`, + `pull/${entityNumber}/head:${branchName}`, + ]); + } else { + // Execute git commands to checkout PR branch (dynamic depth based on PR size) + // Using execFileSync instead of shell template literals for security + execGit(["fetch", "origin", `--depth=${fetchDepth}`, branchName]); + } execGit(["checkout", branchName, "--"]); console.log(`Successfully checked out PR branch for PR #${entityNumber}`); diff --git a/src/github/types.ts b/src/github/types.ts index d982620da..6ed41e39d 100644 --- a/src/github/types.ts +++ b/src/github/types.ts @@ -57,6 +57,13 @@ export type GitHubPullRequest = { baseRefName: string; headRefName: string; headRefOid: string; + isCrossRepository: boolean; + headRepository: { + owner: { + login: string; + }; + name: string; + } | null; createdAt: string; updatedAt?: string; lastEditedAt?: string; diff --git a/test/create-prompt.test.ts b/test/create-prompt.test.ts index 0a5ea8253..afae08396 100644 --- a/test/create-prompt.test.ts +++ b/test/create-prompt.test.ts @@ -23,6 +23,8 @@ describe("generatePrompt", () => { baseRefName: "main", headRefName: "feature-branch", headRefOid: "abc123", + isCrossRepository: false, + headRepository: { owner: { login: "testowner" }, name: "testrepo" }, commits: { totalCount: 2, nodes: [ diff --git a/test/data-fetcher.test.ts b/test/data-fetcher.test.ts index 98f52e572..c4742646a 100644 --- a/test/data-fetcher.test.ts +++ b/test/data-fetcher.test.ts @@ -1006,6 +1006,8 @@ describe("fetchGitHubData integration with time filtering", () => { baseRefName: "main", headRefName: "feature", headRefOid: "abc123", + isCrossRepository: false, + headRepository: { owner: { login: "testowner" }, name: "testrepo" }, createdAt: "2024-01-15T10:00:00Z", updatedAt: "2024-01-15T12:30:00Z", // Edited after trigger lastEditedAt: "2024-01-15T12:30:00Z", // Edited after trigger diff --git a/test/data-formatter.test.ts b/test/data-formatter.test.ts index 4c6b150dd..375bc5889 100644 --- a/test/data-formatter.test.ts +++ b/test/data-formatter.test.ts @@ -24,6 +24,8 @@ describe("formatContext", () => { baseRefName: "main", headRefName: "feature/test", headRefOid: "abc123", + isCrossRepository: false, + headRepository: { owner: { login: "testowner" }, name: "testrepo" }, createdAt: "2023-01-01T00:00:00Z", additions: 50, deletions: 30, diff --git a/test/pull-request-target.test.ts b/test/pull-request-target.test.ts index 59c747f28..187b1b409 100644 --- a/test/pull-request-target.test.ts +++ b/test/pull-request-target.test.ts @@ -17,6 +17,8 @@ describe("pull_request_target event support", () => { baseRefName: "main", headRefName: "feature-branch", headRefOid: "abc123", + isCrossRepository: false, + headRepository: { owner: { login: "testowner" }, name: "testrepo" }, commits: { totalCount: 2, nodes: [