---
title: GitHub Rulesets Compatibility
description: How Mergify interacts with GitHub branch protections and rulesets, including known incompatibilities and how to resolve them.
---

Mergify automatically detects [GitHub branch
protections](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches)
and
[rulesets](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/about-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.

## Recommended Ruleset Setup

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](#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`](/configuration/file-format/#queue-rules)), or
   bypass them as above. See [Required Signatures and Branch
   Deletion](#required-signatures-and-branch-deletion), [Branch Name
   Pattern](#branch-name-pattern), and the [compatibility
   table](#ruleset-rule-compatibility) 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](#review-requirements).

4. **Leave *Require branches to be up to date before merging* off**, unless you
   use [in-place checks](/merge-queue/batches#in-place-checks-no-batch-prs) or
   the [`fast-forward` merge
   method](/merge-queue/merge-strategies#fast-forward). See [Require Branches to
   Be Up to Date](#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](#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](#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](#ignored-rule-types).

## How Condition Injection Works

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](/configuration/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`](/workflow/actions/merge) action and the merge queue. For the
merge queue, you can control how injection behaves using the setting below.

### Controlling Injection

You can control merge queue injection with the
[`branch_protection_injection_mode`](/configuration/file-format/#queue-rules)
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.

### Bypass Actors

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](#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](#configuring-mergify-as-a-bypass-actor) for the steps.

### Bypass Actors and Injection

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](#bypass-actors) for what each mode
covers, and [Configuring Mergify as a Bypass
Actor](#configuring-mergify-as-a-bypass-actor) for the steps.

With the [`fast-forward` merge
method](/merge-queue/merge-strategies#fast-forward), 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`](/configuration/file-format/#queue-rules)
  controls it, and [Controlling Injection](#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.

:::caution
  A bypass mode does not shape your merge conditions. Mergify injects the rules
  of a ruleset it can bypass like any other, whichever mode you picked, so
  narrowing the mode keeps nothing in your conditions and only blocks the queue.
:::

## Review Requirements

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](#review-requirements-and-fast-forward) and
[In-Place Checks and Review Requirements](#in-place-checks-and-review-requirements).

### At Least One Required Approval

[`github-review-approved`](/configuration/conditions#github-rulesets-and-branch-protection-attributes)
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`](/configuration/conditions#github-rulesets-and-branch-protection-attributes),
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.

### No Required Approval

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.

| Requirement | Enforced by Mergify at `0` approvals | Where Mergify differs from GitHub |
|---|---|---|
| A blocking review | Yes | Stricter: reviewer eligibility is approximated by a write-access check |
| Code owner review | Yes | Stricter: an owner that resolves to no GitHub login can never satisfy its file |
| Approval of the most recent push | **No** | **Looser: a pull request can merge with an unapproved latest push** |

:::caution
  Approval of the most recent push is the only review requirement on this page
  that a pull request can merge without. Require at least one approving review
  on the rule if the latest push must be approved before a merge.
:::

**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`](/configuration/conditions#github-rulesets-and-branch-protection-attributes)
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](#bypass-actors) 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](#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.

### Required Reviewers

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`](/configuration/conditions#github-rulesets-and-branch-protection-attributes)
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.

:::note
  The condition evaluates to `false` if a ruleset entry points to a team or
  user that no longer exists, so the misconfiguration surfaces instead of
  being silently skipped. Fix the ruleset to remove the stale reviewer.
:::

## Ruleset Rule Compatibility

Mergify handles each GitHub ruleset rule type as follows.

| Ruleset rule type | Mergify behavior | Notes |
|---|---|---|
| `required_status_checks` | Injected as conditions | See [below](#require-branches-to-be-up-to-date) |
| `pull_request` | Injected as conditions | Depends on the approval count. See [above](#review-requirements) |
| `merge_queue` (GitHub native) | **Incompatible** | See [below](#github-native-merge-queue-rule) |
| `creation` | Checked when creating batch PRs | May block batch PR creation if Mergify is not a bypass actor |
| `update` | Checked when updating batch PRs | May block batch PR updates if Mergify is not a bypass actor |
| `branch_name_pattern` | Checked on queue branch creation | See [below](#branch-name-pattern) |
| `required_review_thread_resolution` | Injected as conditions | -- |
| `required_signatures` | Checked on queue branch push | See [below](#required-signatures-and-branch-deletion) |
| `deletion` | Checked when queue branches are cleaned up | See [below](#required-signatures-and-branch-deletion) |
| All other rule types | Ignored | See [below](#ignored-rule-types) |

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](#ignored-rule-types) for how to enforce a rule Mergify does not handle.

:::caution
  Branch protection support has some limitations. For example, GitHub does
  not provide an API to support [code
  owners](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners),
  which makes it unreliable in certain circumstances.
:::

## Known Incompatibilities

### GitHub-Native Stacked Pull Requests

Merging [GitHub-native stacked pull
requests](/merge-queue/stacks#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

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.

### Branch Name Pattern

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`](/configuration/file-format/#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`](/configuration/file-format/#queue-rules), substitute
  your prefix.

:::note
  Mergify also periodically deletes leftover queue branches that match these
  prefixes. See [Queue Branch
  Cleanup](/merge-queue/lifecycle#queue-branch-cleanup) for how branches are
  matched and an important caveat about naming your own branches.
:::

### 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

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](/merge-queue/performance#parallel-checks) and
[batches](/merge-queue/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.

:::note
  Disabling this setting does **not** mean Mergify tests outdated code.
  Mergify always updates pull requests against the latest base branch before
  testing.
:::

**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](/merge-queue/merge-strategies#fast-forward), which merges the queue
  branch directly and is not affected by this setting.

- **Alternative:** use [in-place checks](/merge-queue/batches#in-place-checks-no-batch-prs),
  which test PRs on their own branch without creating temporary batch PRs.

### 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](/merge-queue/merge-strategies#fast-forward)
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

When using [in-place checks](/merge-queue/batches#in-place-checks-no-batch-prs)
(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.

## Ignored Rule Types

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](#bypass-actors), which the [recommended
setup](#recommended-ruleset-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](/configuration/conditions) 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`](/merge-queue/merge-strategies) that does not create a merge
commit.

## 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.
