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 <old-develop-tip> into PR head <head-sha>
stderr: merge: <old-develop-tip> - 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/<new-base>. 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/<base.ref> (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.
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:
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 cachedmerge_commit_shawhen
mergeable == trueand derives baseHash from its parents. GitHubrecomputes mergeability asynchronously after a retarget (documented API
behavior), so a webhook-triggered fetch arriving seconds after
pull_request.edited(withchanges.base) receives the stale pre-retargettest merge. Observed:
base_ref_changedat 12:06:28Z; build at 12:06:46Z usedthe identical
merge_commit_shaas the last pre-retarget build and labeledthe old base branch's tip as
remotes/origin/<new-base>. The existingfallback (
getRef("heads/" + pr.getBase().getRef())) resolves correctly butis 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/<base.ref>(one compare API call); fall back tothe existing
getRef()path otherwise. Costs one extra API call permerge-PR resolution.
b) Alternatively, treat stale-looking mergeability data like
mergeable == nullwhen the head's target has changed since the lastobserved 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.