jenkinsci / jenkinsci/github-branch-source-plugin

Merge-strategy PR builds fail with "not something we can merge" after PR base branch is retargeted (stale merge_commit_sha trusted)

Open
#1,534 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Java
Stars
217
Forks
398
Avg merge
30m
Merged PRs (30d)
1

Description

*Transparency: this issue was investigated and drafted with AI assistance
(Claude). All evidence was verified against a production Jenkins instance,
the GitHub REST API, and the plugin source on master*

## Environment

Jenkins 2.568, github-branch-source; the affected logic is unchanged on
current master (`GitHubSCMSource#createPullRequestSCMRevision`).

## Symptom

After changing a PR's base branch (e.g. develop → staging), the next
event-triggered build fails deterministically in checkout:

```
Merging remotes/origin/staging commit into PR head
stderr: merge: - not something we can merge
ERROR: Maximum checkout retry attempts reached, aborting
```

The fetch refspec already uses the NEW base ref, but the merge target hash
still belongs to the OLD base branch — unreachable via that refspec.

## Root cause

`createPullRequestSCMRevision()` prefers GitHub's cached `merge_commit_sha`
when `mergeable == true` and derives baseHash from its parents. GitHub
recomputes mergeability asynchronously after a retarget (documented API
behavior), so a webhook-triggered fetch arriving seconds after
`pull_request.edited` (with `changes.base`) receives the stale pre-retarget
test merge. Observed: `base_ref_changed` at 12:06:28Z; build at 12:06:46Z used
the identical `merge_commit_sha` as the last pre-retarget build and labeled
the old base branch's tip as `remotes/origin/`. The existing
fallback (`getRef("heads/" + pr.getBase().getRef())`) resolves correctly but
is only reached when the merge hash is absent or invalid.

## Proposed fix (for discussion)

a) Before trusting the merge-commit parents, verify the derived baseHash is
reachable from `heads/` (one compare API call); fall back to
the existing `getRef()` path otherwise. Costs one extra API call per
merge-PR resolution.

b) Alternatively, treat stale-looking mergeability data like
`mergeable == null` when the head's target has changed since the last
observed revision.

Happy to contribute a PR with test coverage once maintainers indicate a
preferred direction.

## Related (not duplicates)

#1191 (build merge_commit_sha directly), #1225 (merge-ref staleness,
different symptom), #991 / #1349 (rebuild triggering on base change).

## Workarounds

Repository re-scan after GitHub finishes recomputing mergeability, pushing a
commit to the PR head, or close/reopen the PR.

Contributor guide

Open the contributing guide

Research direction

Start in GitHubSCMSource#createPullRequestSCMRevision and trace how merge_commit_sha parents produce baseHash before the existing getRef("heads/" + pr.getBase().getRef()) fallback. Review the GitHub API behavior described for retargeted pull requests, then add regression coverage for a stale merge target and verify the corrected checkout behavior. Confirm the preferred approach with maintainers before implementing it.

Written by the indexing model from the issue text.

Assessment

Tech stack
github, java
Domain
api, backend, ci-cd
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.