Skip to content

create_pull_request patch uses stale origin/branchName instead of merge-base with default branch #28955

Description

@mrjf

Problem

In generate_git_patch.cjs, the full-mode patch generation (used by create_pull_request) uses a stale origin/branchName as the base ref when it exists locally, instead of computing the merge-base with the default branch. This causes the patch to include commits the agent never made.

Root Cause

File: actions/setup/js/generate_git_patch.cjs, lines 178-224 (Strategy 1, Full Mode)

// FULL MODE (for create_pull_request):
debugLog(`Strategy 1 (full): Checking if origin/${branchName} exists`);
try {
    execGitSync(["show-ref", "--verify", "--quiet", `refs/remotes/origin/${branchName}`], { cwd });
    baseRef = `origin/${branchName}`;   // ← BUG: uses stale remote-tracking ref
    debugLog(`Strategy 1 (full): Using existing origin/${branchName} as baseRef`);
} catch {
    // Falls through to merge-base with default branch — this path is CORRECT
    baseRef = execGitSync(["merge-base", "--", `origin/${defaultBranch}`, branchName], { cwd }).trim();
}

When origin/branchName exists locally, line 182 short-circuits to using it as the base ref. The patch is then:

git format-patch origin/branchName..branchName --stdout

The problem: origin/branchName is a stale local ref — it was fetched at the start of the workflow run (during the checkout step with fetch: ["*"]). It points to wherever the remote branch was at that moment, not the state of the branch before the agent made changes.

In the autoloop scenario:

  1. Workflow checks out the repo with fetch: ["*"], fetching all branches including origin/autoloop/tsb-perf-evolve (which is 50 commits behind main, 0 ahead — fully merged)
  2. Git credentials are cleaned (clean_git_credentials.sh)
  3. Agent runs, fast-forwards the local branch to origin/main, commits 1 file (src/core/series.ts), pushes
  4. Agent calls create_pull_request MCP tool
  5. MCP tool checks: does refs/remotes/origin/autoloop/tsb-perf-evolve exist? Yes — it was fetched in step 1
  6. baseRef = origin/autoloop/tsb-perf-evolve — pointing to the old commit (50 behind main)
  7. Patch = git format-patch old-commit..current-branch --stdout — includes all ~50 commits between the old branch position and current branch tip

Result: the patch contains .github/workflows/*, README.md, assets/tsessebe-logo.png, and other files from those 50 commits that the agent never touched. Only src/core/series.ts was the agent's actual change.

The Fix

The base ref should be the state of the branch before the agent made changes — i.e., the merge-base with the default branch. In full mode, always use the merge-base path (line 217) instead of short-circuiting to origin/branchName (line 182).

The fallback path already does the right thing:

baseRef = execGitSync(["merge-base", "--", `origin/${defaultBranch}`, branchName], { cwd }).trim();

This correctly computes the common ancestor between origin/main and the local branch, capturing exactly what the agent changed and nothing else.

The origin/branchName shortcut only makes sense for incremental mode (which already has its own code path at lines 141-172). In full mode it's always wrong when the remote branch is stale.

Impact

This bug blocks all three autoloop programs on githubnext/tsessebe:

Example failing run: https://github.com/githubnext/tsessebe/actions/runs/25053867836

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions