View as Markdown

GitHub Rulesets Compatibility

How Mergify interacts with GitHub branch protections and rulesets, including known incompatibilities and how to resolve them.


Mergify automatically detects GitHub branch protections and rulesets configured on your repository and injects the supported ones as conditions. This page explains how that injection works, which ruleset rule types are supported, which ones are incompatible with the merge queue, and how to resolve conflicts.

A ruleset shaped like this works with the merge queue without further tuning. The rest of this page explains what each point protects you from.

  1. Add Mergify to the bypass list with the exempt bypass mode, on every ruleset that applies to a branch you queue. See Bypass Actors for what the other two modes leave blocked.

  2. Keep rulesets off the queue branches (mergify/merge-queue/* unless you set queue_branch_prefix), or bypass them as above. See Required Signatures and Branch Deletion, Branch Name Pattern, and the compatibility table for creation and update.

  3. Require at least one approving review, if you require reviews at all. At an approval count of 0, Mergify has to rebuild the review gate itself, with gaps. See Review Requirements.

  4. Leave Require branches to be up to date before merging off, unless you use in-place checks or the fast-forward merge method. See Require Branches to Be Up to Date.

  5. Leave GitHub’s native merge_queue rule off on the branches you queue with Mergify. See GitHub Native Merge Queue Rule.

  6. Keep review requirements off the head branches you queue if you use in-place checks. See In-Place Checks and Review Requirements.

  7. Re-express anything Mergify does not handle as a rule it does handle, usually a CI check behind a required_status_checks rule. See Ignored Rule Types.

When Mergify processes a pull request, it reads the branch protection and ruleset rules that apply to the target branch and converts them into merge conditions. For example, if you require at least one approved review, Mergify injects the condition #approved-reviews-by >= 1.

This injection happens automatically for both the merge action and the merge queue. For the merge queue, you can control how injection behaves using the setting below.

You can control merge queue injection with the branch_protection_injection_mode option on your queue rules:

  • queue (default) -- rules are injected as required conditions for both queuing and merging pull requests.

  • merge -- rules are injected as merge conditions, checked after the queue has tested the pull request.

  • none -- rules are not injected at all. This mode requires a merge_bot_account on the queue rule, since Mergify must merge with an account able to satisfy the protections itself.

If you are using GitHub rulesets (not classic branch protections), add Mergify as a bypass actor on the ruleset. The bypass mode you give it decides what the merge queue may do:

  • exempt covers everything, and is the only mode that works for GitHub-native stacked pull requests.

  • always covers everything except GitHub-native stacked pull requests.

  • pull_requests_only covers only what Mergify does through a pull request. The merge queue also creates, pushes to, and deletes its own queue branches, and those are raw ref operations, so this mode still blocks the queue.

Choose exempt unless you have a reason not to. See Configuring Mergify as a Bypass Actor for the steps.

A bypass actor entry lets the merge queue work on your branches. Beyond merging pull requests, the queue runs raw ref operations on its own queue branches, which are prefixed with mergify/merge-queue/ by default, so a ruleset that covers those branches blocks the queue until you either narrow the ruleset so it no longer matches them, or add Mergify as a bypass actor with the exempt or always bypass mode. See Bypass Actors for what each mode covers, and Configuring Mergify as a Bypass Actor for the steps.

With the fast-forward merge method, Mergify advances the target branch itself by a direct ref update, so a ruleset on that branch also needs the exempt or always bypass mode. Narrowing the ruleset is not an option there, since the branch the rules protect is the one being updated.

Bypassing and injection are different controls. A bypass mode decides what GitHub lets Mergify do; injection decides what Mergify requires before it merges, and the two are set in different places:

  • Injection reads every branch protection and ruleset that applies to the target branch, including the ones Mergify can bypass, and turns the supported rules into merge conditions.

  • branch_protection_injection_mode controls it, and Controlling Injection covers its modes. It is set per queue rule and covers everything Mergify detects on the branch, so it turns injection off for every rule there or for none of them.

If a single rule should not gate your queue, take it out of the ruleset or scope the ruleset so it no longer targets the branch. That is the one control that acts on exactly one rule.

GitHub has no API that answers “is this ruleset satisfied”. The closest thing is the GraphQL pullRequest.reviewDecision field, and GitHub only publishes a decision there when a rule on the base branch requires at least one approving review. When the required approval count is 0, the field stays null even while GitHub keeps blocking the merge. So Mergify reads GitHub’s verdict where GitHub publishes one, and rebuilds the gate from the pull request’s own reviews where it does not.

Which of the two applies is decided per base branch, not per ruleset. Mergify takes the highest required_approving_review_count across every active pull_request rule on the branch, from classic branch protection and from every ruleset alike. A single ruleset asking for one approval puts the whole branch on GitHub’s verdict. A ruleset Mergify can bypass counts like any other.

That branch-wide count decides which verdict github-review-approved reads. Whether a given rule also contributes a code owner or last-push condition is decided from that rule’s own count, so a branch on GitHub’s verdict can still carry a local CODEOWNERS condition injected by a second rule that requires no approvals.

Review requirements also collide with two queue features whatever the count. See Review Requirements and Fast-Forward and In-Place Checks and Review Requirements.

At Least One Required Approval

Section titled At Least One Required Approval

github-review-approved mirrors reviewDecision. GitHub has already applied reviewer eligibility, CODEOWNERS ownership, the rule’s own approval-freshness options, and active change requests, so the condition Mergify evaluates and GitHub’s own merge button agree.

require_code_owner_review needs no condition of its own in this case: GitHub folds code owner review into the decision it publishes. require_last_push_approval does get a condition, github-require-last-push-approval, so you can see the requirement in the check summary, but it reads the same decision rather than checking anything separately.

If Mergify cannot read the decision at all, it treats the pull request as not approved rather than merging it.

At required_approving_review_count: 0, a pull_request rule still enforces its other requirements, a blocking review, code owner review, and approval of the most recent push, against anyone who cannot bypass it. GitHub just stops publishing a verdict, so Mergify rebuilds most of them from the pull request’s reviews.

Each requirement Mergify does rebuild is deliberately fail-safe: it can hold a pull request GitHub would have let through, and it never lets one merge that GitHub would have blocked. The symptom is a pull request Mergify is still waiting on while GitHub reports it as mergeable.

RequirementEnforced by Mergify at 0 approvalsWhere Mergify differs from GitHub
A blocking reviewYesStricter: reviewer eligibility is approximated by a write-access check
Code owner reviewYesStricter: an owner that resolves to no GitHub login can never satisfy its file
Approval of the most recent pushNoLooser: a pull request can merge with an unapproved latest push

A blocking review. github-review-approved is false while a reviewer with write access or above has an active Request changes. Only each reviewer’s most recent review counts, and bot reviews are advisory. Mergify approximates GitHub’s reviewer eligibility with that write-access check, so it can hold a pull request on a review GitHub would disregard.

Code owner review. github-code-owner-review-satisfied reads CODEOWNERS itself: for each file the pull request touches it takes the owners of the last matching entry, and is satisfied when one of them approved and none requested changes. Both paths of a rename count. An owner it cannot resolve to a GitHub login, such as a bare email address or a team in another organization, can never satisfy the file, so its approval does not count. When no owner of a file resolves, the check summary names the entry; when another owner does resolve, the pull request waits for that owner’s approval with nothing to say why.

Approval of the most recent push. Mergify does not inject github-require-last-push-approval when the required approval count is 0. GitHub keeps blocking anyone who cannot bypass the rule, but where Mergify bypasses it, as the bypass actor setup this page recommends, nothing checks the latest push. Requiring at least one approving review is what makes Mergify honor the option.

Reviews from named teams or users. These are not in the table because the approval count never decides them. This is the rule’s Required reviewers setting, and GitHub publishes no status for it at any count, so Mergify always evaluates github-require-review-from-specific-teams itself, as covered in Required Reviewers. A team or user ID the ruleset names but Mergify cannot resolve makes the condition false, so the misconfiguration blocks instead of being skipped.

A pull_request ruleset rule can require approvals from specific teams or users (the rule’s Required reviewers setting). Mergify detects this and injects the github-require-review-from-specific-teams boolean condition, so a pull request is merged only once those approvals are in.

The condition is true when every required reviewer is satisfied:

  • each required team has at least the requested number of approvals from its members, and

  • each required user has approved the pull request.

When the requirement is scoped to a subset of files (the ruleset’s file_patterns field), it only applies to pull requests that touch a matching file. Other pull requests are unaffected.

Mergify injects this condition itself, and you cannot write it in your own conditions: a configuration that references it is rejected when it is validated.

Mergify handles each GitHub ruleset rule type as follows.

Ruleset rule typeMergify behaviorNotes
required_status_checksInjected as conditionsSee below
pull_requestInjected as conditionsDepends on the approval count. See above
merge_queue (GitHub native)IncompatibleSee below
creationChecked when creating batch PRsMay block batch PR creation if Mergify is not a bypass actor
updateChecked when updating batch PRsMay block batch PR updates if Mergify is not a bypass actor
branch_name_patternChecked on queue branch creationSee below
required_review_thread_resolutionInjected as conditions--
required_signaturesChecked on queue branch pushSee below
deletionChecked when queue branches are cleaned upSee below
All other rule typesIgnoredSee below

Mergify supports only the ruleset rule types named above. Every other rule type is ignored: Mergify neither injects it as a condition nor checks it for compatibility. Do not assume full ruleset parity. See Ignored Rule Types for how to enforce a rule Mergify does not handle.

GitHub-Native Stacked Pull Requests

Section titled GitHub-Native Stacked Pull Requests

Merging GitHub-native stacked pull requests requires the exempt bypass mode. With any other bypass mode, the merge queue refuses the pull request.

Resolution:

  • Set Mergify’s bypass mode to Exempt on every ruleset that applies to the base branch.

GitHub Native Merge Queue Rule

Section titled GitHub Native Merge Queue Rule

If the merge_queue ruleset rule (GitHub’s built-in merge queue) is enabled on the target branch and Mergify is not a bypass actor, GitHub blocks Mergify from merging pull requests -- all merges must go through GitHub’s own queue.

Resolution:

  • Preferred: disable the merge_queue ruleset rule on branches where you use Mergify’s merge queue.

  • Alternative: add Mergify as a bypass actor on that ruleset with the exempt bypass mode. This lets Mergify merge directly while GitHub’s queue is still active for other actors.

If a branch_name_pattern ruleset rule matches Mergify’s queue branches and Mergify is not a bypass actor with the exempt or always bypass mode, GitHub blocks Mergify from creating queue branches. As a result, Mergify cannot queue or merge pull requests targeting that branch.

Creating a branch is a raw ref operation, which is why pull_requests_only does not unblock it.

Queue branches are prefixed with mergify/merge-queue/, customizable via queue_branch_prefix in queue_rules. The error Mergify reports names the queue branch that was refused, so you know which pattern to narrow.

Resolution:

  • Preferred: add Mergify as a bypass actor on the ruleset with the exempt bypass mode.

  • Alternative: narrow the ruleset pattern so it excludes mergify/merge-queue/*. If you customized queue_branch_prefix, substitute your prefix.

Required Signatures and Branch Deletion

Section titled Required Signatures and Branch Deletion

Mergify builds each queue branch locally and pushes it, then deletes it once the batch is done. Both are raw ref operations, so a ruleset covering the queue branch prefixes can stop the queue:

  • A required_signatures rule rejects the push, because the commits Mergify composes locally are unsigned. Every queue attempt then fails on branch creation.

  • A deletion rule stops Mergify from removing the final queue branch before recreating it, which leaves the queue stuck on that batch.

Resolution:

  • Preferred: add Mergify as a bypass actor on the ruleset with the exempt or always bypass mode. pull_requests_only is not enough for either operation.

  • Alternative: narrow the ruleset so it does not cover the queue branch prefixes.

Require Branches to Be Up to Date

Section titled Require Branches to Be Up to Date

The strict_required_status_checks_policy setting (labeled Require branches to be up to date before merging in the GitHub UI) is incompatible with parallel checks and batches when using batch PR checks.

Mergify creates temporary batch PRs to test combined changes. The original pull requests are merged after those checks pass, but GitHub considers them “not up to date” because they were not the branches that were tested. This setting blocks the merge.

Resolution:

  • Preferred: disable the Require branches to be up to date before merging setting.

  • Alternative: add Mergify as a bypass actor on the ruleset with the exempt bypass mode.

  • Alternative: use the fast-forward merge method, which merges the queue branch directly and is not affected by this setting.

  • Alternative: use in-place checks, which test PRs on their own branch without creating temporary batch PRs.

Review Requirements and Fast-Forward

Section titled Review Requirements and Fast-Forward

The required_approving_review_count, require_code_owner_review, and require_last_push_approval ruleset rules are incompatible with the fast-forward merge method when using batch PR checks (the default for parallel checks and batches).

When Mergify uses batch PR checks, it creates temporary batch PRs to test changes. These batch PRs do not carry the review approvals from the original PRs, so GitHub blocks the fast-forward push if review requirements are enforced.

Resolution:

  • Add Mergify as a bypass actor on the ruleset that enforces review requirements, with the exempt or always bypass mode.

In-Place Checks and Review Requirements

Section titled In-Place Checks and Review Requirements

When using in-place checks (where Mergify tests a PR on its own branch), a pull_request ruleset rule that enforces review requirements on the PR’s head branch stops Mergify from checking the PR, and the queue reports an incompatibility error. Adding Mergify as a bypass actor does not resolve it, since the check looks at whether the rule is set rather than at who may bypass it. PRs opened from a fork are not affected.

Resolution:

  • Drop the review requirement from the ruleset that targets the head branches you queue, or stop using in-place checks on that queue.

The following ruleset rule types are not processed by Mergify. They are neither injected as conditions nor validated for compatibility. If these rules are active on your branches, Mergify will not enforce them:

  • required_deployments

  • required_linear_history

  • non_fast_forward

  • Pattern and file rules (commit_message_pattern, file_path_restriction, max_file_path_length, file_extension_restriction)

  • workflows

  • code_scanning

Mergify as a bypass actor, which the recommended setup calls for, covers the whole ruleset, so GitHub does not apply these rules to a merge the queue performs either. If you rely on one of them, put the requirement behind a CI check and require that check with a required_status_checks rule, which Mergify does inject, so your branch rules stay in the ruleset. Fall back to a merge condition only for a requirement no ruleset rule can express.

Two of them need a different answer. required_linear_history and non_fast_forward constrain the ref update rather than the pull request, so no condition or check reproduces them. GitHub still enforces both against every other actor; to keep the queue’s own merges linear, pick a merge_method that does not create a merge commit.

Configuring Mergify as a Bypass Actor

Section titled Configuring Mergify as a Bypass Actor

To add Mergify as a bypass actor on a GitHub ruleset:

  1. Go to your repository Settings > Rules > Rulesets.
  2. Select the ruleset you want to modify (or create a new one).
  3. Under Bypass list, click Add bypass.
  4. Search for the Mergify app and select it.
  5. Choose Exempt as the bypass mode.
  6. Save the ruleset.

Bypass actors are configured per ruleset, so repeat this on every ruleset that applies to the branches you queue.

Was this page helpful?