From f669191d7d1e67f08a54b0c11cf5683a9a391951 Mon Sep 17 00:00:00 2001 From: Yi-Cheng Wang <80525895+kirisame-wang@users.noreply.github.com> Date: Thu, 12 Feb 2026 11:30:25 +0800 Subject: [PATCH] fix: use unique local branch names for PR checkout to avoid conflicts (#931) The previous implementation used the PR's original branch name when fetching, which could conflict with existing local or remote branches of the same name. This caused checkout failures for PRs with common branch names like 'main' or 'feature/xyz'. Changes: - Use 'pr-{number}' format for local branch names (e.g., pr-385) - Preserve original branch name for logging purposes - Add detailed logging showing original -> local branch mapping This ensures uniqueness since PR numbers are unique per repository, while maintaining support for both same-repo and fork PRs via GitHub's pull/{number}/head refs. Fixes the issue introduced in #851 where fork PR support was added. Co-authored-by: Yi-Cheng Wang Co-authored-by: Claude Sonnet 4.5 --- src/github/operations/branch.ts | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/github/operations/branch.ts b/src/github/operations/branch.ts index a228a3d..9f31fe4 100644 --- a/src/github/operations/branch.ts +++ b/src/github/operations/branch.ts @@ -151,7 +151,10 @@ export async function setupBranch( // Handle open PR: Checkout the PR branch console.log("This is an open PR, checking out PR branch..."); - const branchName = prData.headRefName; + const originalBranchName = prData.headRefName; + // Use unique local branch name to avoid conflicts with existing branches + // Format: pr-{number} ensures uniqueness since PR numbers are unique per repo + const localBranchName = `pr-${entityNumber}`; // Determine optimal fetch depth based on PR commit count, with a minimum of 20 const commitCount = prData.commits.totalCount; @@ -160,22 +163,28 @@ export async function setupBranch( console.log( `PR #${entityNumber}: ${commitCount} commits, using fetch depth ${fetchDepth}`, ); + console.log( + `Fetching PR branch '${originalBranchName}' as local branch '${localBranchName}'`, + ); // Validate branch names before use to prevent command injection - validateBranchName(branchName); + validateBranchName(localBranchName); // Execute git commands to checkout PR branch (dynamic depth based on PR size) // Using execFileSync instead of shell template literals for security // Use GitHub's PR ref which works for both same-repo and fork PRs + // The local branch name uses pr-{number} format to ensure uniqueness execGit([ "fetch", "origin", `--depth=${fetchDepth}`, - `pull/${entityNumber}/head:${branchName}`, + `pull/${entityNumber}/head:${localBranchName}`, ]); - execGit(["checkout", branchName, "--"]); + execGit(["checkout", localBranchName, "--"]); - console.log(`Successfully checked out PR branch for PR #${entityNumber}`); + console.log( + `Successfully checked out PR #${entityNumber} as local branch '${localBranchName}'`, + ); // For open PRs, we need to get the base branch of the PR const baseBranch = prData.baseRefName; @@ -183,7 +192,7 @@ export async function setupBranch( return { baseBranch, - currentBranch: branchName, + currentBranch: localBranchName, }; } }