Repository navigation
Delete a merged PR's branch only in its own repository - #2086
Merged
Merged
Conversation
ppXD
force-pushed
the
fix/delete-a-merged-branch-only-in-its-own-repository
branch
from
October 7, 2026 19:18
59fb645 to
ad1a095
Compare
GitHub never deletes a head branch on merge, so the provider deleted
heads/{head.ref} itself, in the base repository. For a fork's pull
request that ref names a different branch: an outsider who named their
fork branch after a real base branch (release, a teammate's feature) got
that base branch deleted when the pull request was merged with
deleteSourceBranch. A refused delete was swallowed, and a failure to
read the pull request back after the merge threw, which read as a
failed merge.
The provider now deletes only when the head repository id equals the
base repository id. A fork's head, or one whose repository GitHub no
longer reports, is kept. What happened is reported on the merge result
and in git.merge_pr's outputs (sourceBranchDeletion and
sourceBranchDetail) instead of being swallowed or thrown. A refused
delete of a branch that is already gone (a retried delete that landed,
or the repository's own auto-delete) reads as deleted, so a lost answer
is not reported as a failure. Only a 404 on the branch read-back counts
as gone: a read that fails proves nothing, so the refusal stands and a
protected branch is never reported deleted.
A cancel during the cleanup stops it and is reported as Failed, with
the merge result still returned. Letting it escape dropped a merge that
had landed, and a refused delete arriving while the cancel was pending
escaped as a provider error that git.merge_pr describes as a failed
merge. A delete in flight when the cancel lands is not confirmed either
way, so the detail says cancelled rather than not deleted.
GitLab does not share the defect: should_remove_source_branch is acted
on server-side against the merge request's own source project, so the
provider reports it as requested rather than deleted.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
git.merge_prwithdeleteSourceBranchon GitHub used to deleteheads/{head.ref}in the base repository even when the pull request's head lived in a fork. An outsider who named a fork branch after a real base branch (release, a teammate's feature) got that base branch deleted on merge. The provider now deletes only when the head repository id equals the base repository id (GitHubRepositoryProvider.IsHeadInBaseRepository). A fork's head, or a head whose fork has since been deleted, is kept and reported asSkippedFork.RemotePullRequestMergeResultand in the node's outputs assourceBranchDeletion(NotRequested/Deleted/Requested/SkippedFork/Failed) andsourceBranchDetail, whether the delete was refused (for example a protected branch), a read failed, or the cleanup was cancelled. A refused delete counts as deleted only when a branch read answers 404. A failed read leaves the refusal standing. A cancel during the cleanup returns the merge withFailed: the detail says "cancelled before GitHub confirmed it" and does not claim the branch was or was not deleted. A refusal that arrives while a cancel is pending is still reported, not raised as a failed merge.should_remove_source_branchis applied server-side to the merge request's own source project. The provider now reports it asRequested.Test plan
MergeSourceBranchTests: real Octokit/NGitLab provider against a loopback forge (StubProviderHost). Rows: same-repository delete (4 DELETE answers); fork kept, with the fork still present and with it deleted; refused delete surfaced; PR read-back failure surfaced; refused delete with the branch read failing (502, 403) reported Failed, not Deleted; cleanup cancelled during the DELETE (answer lost, and refused), with the merge returned and no second DELETE; nothing read or deleted when no delete was asked for; GitLab sends the flag and deletes nothing itselfGitMergePullRequestNodeTests: output shape for everySourceBranchDeletionvalue; the output schema enum matchesSourceBranchDeletionGitHubWriteRetryTests: a retried merge still deletes and reportsDeletedcatch (NotFoundException)toApiException, or dropping the cancellation clause, turns the new rows redPullRequestMergeSourceBranchFlowTests(Postgres): 2/2dotnet build: 0 errors