You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Allow authorized users to bypass branch protection when merging - #9144
Read GitHub’s viewerCanMergeAsAdmin capability rather than infer bypass permission from repository write access.
For blocked or out-of-date PRs without the classic capability, query applicable active branch rulesets and their current_user_can_bypass permission. Paginate branch rules, deduplicate ruleset IDs, and support parent rulesets.
Offer an unchecked "Bypass branch rules and merge" checkbox in the editor and sidebar, including initially blocked sidebar PRs. Use shared checkbox styling, spacing, and an accessible explanatory note.
Require explicit opt-in before exposing the existing merge flow. GitHub remains responsible for authorization through the normal merge mutation.
Refresh capability changes and discard stale model/polling results and consent when eligibility changes. Ruleset permissions are not cached; unsupported or failed REST requests fail closed.
Preserve conflict, draft, stack, and merge-queue guards. Stack and merge-queue bypass are outside this change.
Ruleset permission limitation
The REST fallback detects permission to bypass any active ruleset applying to the branch, not whether every specific unmet requirement can be bypassed. The UI explains that GitHub will verify all unmet requirements. A merge can still be rejected by GitHub when another required rule is not bypassable.
Validation
npm run compile
npm run compile:test
npx tsc --noEmit -p tsconfig.webviews.json
npm run test:webviews — 142 passing
npm run test:scripts — 75 passing
Focused tests in a real VS Code extension host — 385 passing, 0 failed/pending: GitHubRepository (64), PullRequestModel (135), PullRequestOverview (165), activity bar/sidebar (21)
npm run test:preprocess
npm run lint
npm run hygiene
git diff --check
Regression coverage includes REST pagination and permissions, missing/denied/unsupported APIs, permission revocation without caching, stale responses, ruleset fallback gating, initial sidebar propagation, deferred overview races, explicit consent, eligibility guards, and shared checkbox styling.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
The bypass option can appear before merge-queue requirements finish loading, allowing users to opt into direct merging on branches that require a queue.
Adds an explicit branch-protection bypass option to the editor and sidebar for authorized users, addressing #3996 while reusing the existing merge flow.
Changes:
Fetches GitHub’s viewerCanMergeAsAdmin capability and propagates it through models and webviews.
Adds an unchecked bypass checkbox with eligibility guards and stale polling-response handling.
Adds regression coverage for capability refresh, consent, and eligibility.
File
Description
webviews/editorWebview/test/merge.test.tsx
Tests bypass consent, guards, and polling behavior.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
A pending mergeability callback can overwrite a newer capability revocation with stale data, incorrectly re-enabling the bypass UI.
No open issues 🎉
1 previously missed issue
In code that hasn't changed since last review
Read current capability before publishing mergeability
src/github/pullRequestOverview.ts:774
If getMergeability() returns true while another deferred request is pending, a later capability revocation can publish false before this callback overwrites it with the stale true, re-enabling the bypass UI. During initial loading, a capability-only refresh does not advance _branchStatusSequence, so isBranchStatusCurrent does not prevent this. Read the capability from the current model when publishing instead of from the completed query result.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
A deferred update can overwrite a newer conflict status with stale mergeability data and incorrectly expose the bypass checkbox.
No open issues 🎉
1 previously missed issue
In code that hasn't changed since last review
Prevent stale mergeability updates from restoring bypass eligibility
src/github/pullRequestOverview.ts:775
If mergeability changes from NotMergeable to Conflict while other initial deferred data is still loading, this update can overwrite the newer conflict status and expose the bypass checkbox. A mergeability-only refresh does not invalidate _branchStatusSequence before the initial comparison is published. Read mergeability from the current model alongside the capability so the deferred update cannot restore stale bypass eligibility.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Human assessment is needed because refresh failures can leave stale capability and mergeability state in the overview and sidebar, keeping a previously selected bypass action available after permission is revoked.
Thanks for this, it is exactly what we were missing. I built it locally on top of 0.174.0 and hit one gap worth sharing.
viewerCanMergeAsAdmin does not reflect repository ruleset bypass. Our protected branches use rulesets only (no classic branch protection). I am a bypass actor (bypass_mode: pull_request) on the ruleset that requires an approving review. On a PR where all required checks pass and only the review is missing:
github.com shows "Merge without waiting for requirements to be met (bypass rules)" and the merge works.
GraphQL returns mergeStateStatus: BLOCKED and viewerCanMergeAsAdmin: false for the same user and PR.
REST GET /repos/{owner}/{repo}/rulesets/{id} returns current_user_can_bypass: "pull_requests_only" for that ruleset (and "never" for the one holding the required status checks).
So with this PR as is, the checkbox never appears for ruleset-based repos. Locally I added a fallback that only runs when the PR is BLOCKED/BEHIND and viewerCanMergeAsAdmin is not true:
// GitHubRepositoryasynccanViewerBypassBranchRules(branch: string): Promise<boolean>{// cached per branch for 5 minutesconst{ octokit, remote }=awaitthis.ensure();constowner=remote.owner;constrepo=remote.repositoryName;construles=awaitoctokit.call(octokit.api.repos.getBranchRules,{ owner, repo, branch,per_page: 100});constids=[...newSet(rules.data.map(rule=>rule.ruleset_id).filter((id): id is number=>typeofid==='number'))];for(construleset_idofids){construleset=awaitoctokit.call(octokit.api.repos.getRepoRuleset,{ owner, repo, ruleset_id });constcan=ruleset.data.current_user_can_bypass;if(can==='always'||can==='pull_requests_only'||can==='exempt'){returntrue;}}returnfalse;}// PullRequestModel.getMergeability, after the PullRequestMergeability queryconstfetched=data.repository?.pullRequest;if(fetched&&fetched.viewerCanMergeAsAdmin!==true&&(fetched.mergeStateStatus==='BLOCKED'||fetched.mergeStateStatus==='BEHIND')&&this.base?.ref){fetched.viewerCanMergeAsAdmin=awaitthis.githubRepository.canViewerBypassBranchRules(this.base.ref);}
With that the checkbox shows up on our blocked PR, matching the web UI. Two caveats:
It is coarser than the web UI: it answers "can the viewer bypass any ruleset on this branch", not "can the viewer bypass the specific unmet rules". If the unmet rule is one the viewer cannot bypass (e.g. required checks with never), the checkbox still appears and GitHub rejects the merge. Authorization stays server-side either way.
It costs 1 + N REST calls (N = rulesets on the branch), only for blocked/behind PRs. A short cache keeps the 3s mergeability polling from repeating them.
Happy to open this as a follow-up PR if that is easier to review separately.
Also a small visual note: the checkbox has no styling, so in the overview it sits flush against the bottom border of the status box with the label touching the input.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Human assessment is needed because branch changes and failed or delayed refreshes can leave stale mergeability and bypass permissions reflected in the overview controls.
Thanks for picking this up so quickly. I rebuilt e7ad01a on top of 0.174.0 and tried it on the same ruleset-only repo: on a PR blocked only by the required review, the "Bypass branch rules and merge" checkbox now appears in the overview, and the new layout reads well.
The enforcement === 'active' check and the includes_parents lookup also cover two cases my local version missed.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Retargeting a PR to a branch requiring a merge queue leaves stale eligibility state, allowing bypass and direct merge actions until the new queue lookup completes.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Clearing stack during reload bypasses the stack guard and temporarily exposes single-PR merge controls for a stacked PR, risking an accidental merge through the wrong flow.
One or more custom setup steps configured for this repository failed during this Copilot code review run:
Install dependencies
Setup steps run before each review. If the review above is missing context, or no review was posted at all, the failing step above may be the cause. See the workflow run for failure details, fix your setup steps configuration, and re-request a review.
Note
You can configure setup steps for Copilot code review separately from Copilot cloud agent with a copilot-code-review.yml file. Read the docs for details.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Human assessment is needed because retargeting can retain stale branch state and bypass consent, while eligibility refreshes can expose direct merging before queue eligibility is known.
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
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.
Fixes #3996
Summary
viewerCanMergeAsAdmincapability rather than infer bypass permission from repository write access.current_user_can_bypasspermission. Paginate branch rules, deduplicate ruleset IDs, and support parent rulesets.Ruleset permission limitation
The REST fallback detects permission to bypass any active ruleset applying to the branch, not whether every specific unmet requirement can be bypassed. The UI explains that GitHub will verify all unmet requirements. A merge can still be rejected by GitHub when another required rule is not bypassable.
Validation
npm run compilenpm run compile:testnpx tsc --noEmit -p tsconfig.webviews.jsonnpm run test:webviews— 142 passingnpm run test:scripts— 75 passingnpm run test:preprocessnpm run lintnpm run hygienegit diff --checkRegression coverage includes REST pagination and permissions, missing/denied/unsupported APIs, permission revocation without caching, stale responses, ruleset fallback gating, initial sidebar propagation, deferred overview races, explicit consent, eligibility guards, and shared checkbox styling.
No live protected-branch merge was performed.