Fix remote branch deletion for owner case mismatch - #14429
Conversation
Use case-insensitive owner comparison so casing differences in a Git remote do not make same-repository pull requests appear cross-repository. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Copilot review overview
🟢 Approval recommended
The focused fix matches established repository semantics and has appropriate regression coverage.
Review tier: Balanced
Findings: None
What changed in this PR
Fixes remote branch deletion when owner casing differs between Git remotes and GitHub’s canonical login.
Changes:
- Uses case-insensitive owner comparison.
- Adds regression coverage for casing-only mismatches.
| File | Description |
|---|---|
pkg/cmd/pr/merge/merge.go |
Corrects cross-repository detection. |
pkg/cmd/pr/merge/merge_test.go |
Verifies remote deletion occurs despite owner-case differences. |
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Rely on the pull request relationship reported by GitHub instead of comparing owner login strings, and strengthen the regression test around the emitted branch deletion request. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Exercise the normal same-repository --delete-branch path and verify that gh removes both local and remote refs while repository-side automatic deletion is disabled. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Rely on the fresh isolated repository's default branch-deletion setting instead of modifying repository-global state. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Renders the header and the collapsed description from the normalized model, so the information architecture can finally be judged against real pull requests rather than argued about. Check rows lead with what needs acting on. Ten of the twenty-one names on cli/cli#14429 report stale, from bot workflows that run on pull_request_target rather than on every push, so ordering by name puts the row that matters below the fold. The derivation sits in core rather than beside the components. It reads the model and returns plain data, which is what the extension needs too. A check is known only from a run that was seen. Never-run therefore means a run that registered and never started, not a check the branch expects and never got; nothing in the response lists the second kind. Collapsed lines render one per row with wrapping off. A wrapped line claims rows the count never budgeted for, and a box that clips them draws the overflow over its neighbours instead of hiding it.
|
Reading through this fix, switching the same-repo check from a login string comparison to IsCrossRepository looks right — GitHub logins are case-insensitive but the old Go comparison was not, so SomeCoolProject vs somecoolproject took the fork path and tried to delete a branch that was never there. One thing I checked while here: the finder field list now requests isCrossRepository, and the new test pins ExpectFields accordingly, which is consistent. Worth a quick grep for other MockFinder setups that still distinguish same/cross-repo only via HeadRepositoryOwner login — with the new predicate those would now read as same-repo (zero value false) unless IsCrossRepository is set, as you did in the onlyLocally case. From what I can see the remaining callers set it, so no behavior change beyond the intended one. |
Fixes #14404
Description
The repository owner parsed from a Git remote can preserve user-supplied casing while GitHub returns the canonical login casing. The merge command compared these values case-sensitively, incorrectly treating a same-repository pull request as cross-repository and silently skipping remote branch deletion.
This now requests and trusts GitHub's
isCrossRepositoryvalue rather than deriving the relationship from owner login strings. It also adds regression coverage for a casing-only mismatch.How did you test this change?
I created a private test repository with automatic head-branch deletion disabled, set
prompttodisabled, changed only the owner casing inremote.origin.url, and opened two pull requests from branches in the same repository.With the baseline binary, I ran
gh pr merge 1 --merge --delete-branch. The local branch was deleted, but an API lookup showed that the remote branch still existed.With the changed binary, I ran
gh pr merge 2 --merge --delete-branch. The command reported deleting both the local and remote branches, and an API lookup confirmed that the remote branch no longer existed.The focused live acceptance script also passed against
github.comusing thegh-acceptance-testingorganization:Key points
The pull request model already exposes
IsCrossRepository, which is the domain abstraction the merge command needs. Using it avoids adding a repository-owner type or duplicating GitHub login equivalence rules. The merge query no longer requestsheadRepositoryOwner, which was only used for the removed comparison.Notes for reviewers
Start with the requested fields and
crossRepoPRinitialization inNewMergeContext, then readTestDeleteRemoteBranchTreatsOwnerCaseAsSameRepository.The reproduction and investigation are documented in #14404.
Authorship and follow-up
Who wrote this:
Who answers review comments: