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.
Recommended ruleset setup
Section titled Recommended ruleset setupA ruleset shaped like this works with the merge queue without further tuning. The rest of this page explains what each point protects you from.
-
Add Mergify to the bypass list with the
exemptbypass mode, on every ruleset that applies to a branch you queue or to the queue branches (mergify/merge-queue/*unless you setqueue_branch_prefix). See Bypass Actors. -
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. -
Leave Require branches to be up to date before merging off, unless you use in-place checks or the
fast-forwardmerge method. See Require Branches to Be Up to Date. -
Leave GitHub’s native
merge_queuerule off on the branches you queue with Mergify. See GitHub Native Merge Queue Rule. -
Keep review requirements off the head branches you queue if you use in-place checks. See In-Place Checks and Review Requirements.
-
Re-express anything Mergify does not handle as a rule it does handle, usually a CI check behind a
required_status_checksrule. See Ignored Rule Types.
How condition injection works
Section titled How condition injection worksWhen 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.
Bypass actors
Section titled Bypass actorsIf you are using GitHub rulesets (not classic branch protections), add Mergify
as a bypass actor with the exempt bypass mode on every ruleset that
applies to a branch you queue or to the queue branches. Beyond merging pull
requests, the merge queue works on its own queue branches, and the
fast-forward and
merge-batch merge methods update
the base branch directly (see Bypass actors and
injection). exempt covers all of it, and it is
the only mode GitHub accepts for merging
GitHub-native stacked pull requests.
With always or pull_requests_only, part of the queue stays blocked.
See Configuring Mergify as a Bypass Actor for the steps.
Bypass actors and injection
Section titled Bypass actors and injectionThe bypass mode decides what GitHub lets Mergify do. A pull_requests_only
bypass covers only what Mergify does through a pull request: merging it. The
merge queue also updates branches directly, with no pull request involved:
-
The
fast-forwardmerge method, when it tests in batch pull requests, moves the base branch onto the batch pull request’s head instead of merging it. -
The
merge-batchmerge method moves the base branch onto merge commits Mergify writes itself when consecutive batches are ready to land together, instead of merging each batch pull request. -
The merge queue creates, pushes to and deletes its own queue branches.
GitHub treats each of these updates as a push to the branch, which a
pull_requests_only bypass does not cover: the ruleset applies to it in full,
and a rule that requires a pull request refuses it whatever approval count the
rule asks for. Only the exempt and always modes let it through.
A merge-batch queue asks for the bypass even if its batches have never landed
together: Mergify knows which batches can land together only once they are
ready, so the queue checks the base branch’s rules for a direct update when a
pull request is queued. Give Mergify the exempt mode from Bypass
actors, or always on GitHub Enterprise Server before 3.20,
which has no exempt mode.
What Mergify requires before it merges is set by the conditions it injects from the target branch’s rules. To keep a single rule from gating your queue, take it out of the ruleset or scope the ruleset so it no longer targets the branch. Narrowing Mergify’s bypass mode blocks the queue and leaves the requirement in place.
Review requirements
Section titled Review requirementsGitHub 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 Required pull requests with fast-forward and merge-batch and In-Place Checks and Review Requirements.
At least one required approval
Section titled At least one required approvalgithub-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.
Nothing the rule asks for beyond approvals needs a condition of its own here.
GitHub folds code owner review, approval of the most recent push, review thread
resolution, and the rule’s Required reviewers into the decision it
publishes, so github-review-approved answers for all four and Mergify injects
nothing alongside it. The one exception is a rule with a Required reviewers
entry scoped to a subset of files, which keeps that condition. See Required
Reviewers.
If Mergify cannot read the decision at all, it treats the pull request as not approved rather than merging it.
No required approval
Section titled No required approvalAt 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 |
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. This is the rule’s Required
reviewers setting. At 0 approvals GitHub publishes no verdict for it either,
so Mergify 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.
Required reviewers
Section titled Required reviewersA 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. A rule that also requires at least one approving review gets the condition
only when one of its entries is scoped to a subset of files: GitHub folds the
entries that cover every file into the verdict
github-review-approved reads.
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.
Ruleset rule compatibility
Section titled Ruleset rule compatibilityMergify handles each GitHub ruleset rule type as follows.
| Ruleset rule type | Mergify behavior | Notes |
|---|---|---|
required_status_checks | Injected as conditions | See below |
pull_request | Injected as conditions | Depends on the approval count. See above |
merge_queue (GitHub native) | Incompatible | See below |
creation | Checked on queue branch creation | See below |
update | Checked on queue branch creation | See below |
branch_name_pattern | Checked on queue branch creation | See below |
required_signatures | Compatible on the hosted service | See below |
deletion | Checked on queue branch creation | See below |
| All other rule types | Ignored | See below |
Mergify supports only the ruleset rule types named above. Every other rule type is ignored: Mergify does not inject it as a condition. Do not assume full ruleset parity. See Ignored Rule Types for how to enforce a rule Mergify does not handle, and for the one of them it does check.
required_review_thread_resolution is an option on the pull_request rule, not
a rule type of its own, so it has no row above. Mergify injects
#review-threads-unresolved = 0 for it alongside the review
conditions that rule contributes, as long as the rule
requires no approving review; at one approval or more GitHub folds thread
resolution into the verdict
github-review-approved reads. Classic
branch protection’s required_conversation_resolution gets the condition at any
approval count.
Known incompatibilities
Section titled Known incompatibilitiesGitHub-native stacked pull requests
Section titled GitHub-native stacked pull requestsMerging 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 ruleIf 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_queueruleset rule on branches where you use Mergify’s merge queue. -
Alternative: add Mergify as a bypass actor on that ruleset with the
exemptbypass mode. This lets Mergify merge directly while GitHub’s queue is still active for other actors.
Branch name pattern
Section titled Branch name patternIf a branch_name_pattern ruleset rule matches Mergify’s queue branches, GitHub
blocks Mergify from creating them unless Mergify bypasses the ruleset. As a
result, Mergify cannot queue or merge pull requests targeting that branch.
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
exemptbypass mode. -
Alternative: narrow the ruleset pattern so it excludes
mergify/merge-queue/*. If you customizedqueue_branch_prefix, substitute your prefix.
Required signatures and branch deletion
Section titled Required signatures and branch deletionMergify builds each queue branch itself and pushes it, then deletes it once the batch is done. Neither goes through a pull request, so a ruleset covering the queue branch prefixes applies to both.
On the hosted service, a required_signatures rule on the queue branches works
without a bypass. Mergify signs the merge commits it adds to a queue branch and
commits them as Mergify <commit-signer@mergify.com>, so GitHub shows them as
Verified. With the merge-batch merge
method, or fast-forward in
batch-PR mode, these commits
land on your base branch, and a signature rule there accepts them too.
The rule can still refuse a push in these cases:
-
A queued pull request carries unsigned commits. When Mergify creates a queue branch, GitHub checks the commits that are on no other branch of the repository yet, typically those of a pull request opened from a fork. If one of them is unsigned, GitHub rejects the push and the queue reports the refusal. Have the author sign those commits and push again before requeuing, rather than adding a bypass that would let them past your signature policy.
-
Another Mergify action writes the commits. The
squash,backportandcopyactions write unsigned commits.backportandcopypush them to new branches undermergify/, so a signature rule covering that namespace rejects those pushes.squashforce-pushes the pull request’s own branch as a GitHub user, so a signature rule covering that branch rejects it unless that user can bypass the rule. -
Mergify runs on-premise. An on-premise installation does not sign its queue commits, so a
required_signaturesrule on the queue branches would reject every push. The queue reports the conflict before it tries to push.
If one of your workflows verifies signatures against its own keyring, for
example with git verify-commit or git log --format='%G?', add Mergify’s
public key from
github.com/mergify-commit-signer.gpg
to it. Keep GitHub’s key from
github.com/web-flow.gpg there too: it signs
the commits GitHub creates for Mergify, such as the merge into your base branch.
Fetch Mergify’s key from that address rather than pinning a copy, since the
signing key is rotated.
The other rules a ruleset can apply to the queue branches stop the queue wherever Mergify runs:
-
A
deletionrule stops Mergify from removing a queue branch, so it cannot manage its queue branches at all. The queue reports this as it prepares the batch, not later when a branch would have been deleted. -
A
creation,update, orpull_requestrule refuses the queue branch outright, whatever the rule itself asks for. Apull_requestrule counts here because the queue branch is pushed, never opened as a pull request of its own, so the rule can never be satisfied on it.
The resolution below covers these rules, and the unsigned pushes of an
on-premise installation or of the backport and copy actions.
Resolution:
-
Preferred: add Mergify as a bypass actor on the ruleset with the
exemptbypass mode. -
Alternative: narrow the ruleset so it does not cover the queue branch prefixes, or the
mergify/branches thebackportandcopyactions create.
Require branches to be up to date
Section titled Require branches to be up to dateThe 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
exemptbypass mode. -
Alternative: use the
fast-forwardmerge 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. Note that in-place checks require
batch_size: 1,max_parallel_checks: 1and serial mode, so this resolution means giving up the batches or parallel checks that caused the conflict rather than keeping them.
Required pull requests with fast-forward and merge-batch
Section titled Required pull requests with fast-forward and merge-batchA pull_request ruleset rule on the base branch is incompatible with the
merge-batch merge method, and
with fast-forward when it tests
in batch pull requests, unless
Mergify bypasses the rule with the exempt or always mode. Both move the base
branch directly, onto commits no pull request merged, and GitHub refuses that
update under any rule that requires a pull request, even one that requires no
approving review. A pull_requests_only bypass does not cover it: see Bypass
actors and injection for why. A classic branch
protection with Require a pull request before merging refuses the update
the same way.
Both queues refuse a pull request when it is queued, before its CI runs, and name the ruleset or branch protection to change.
Resolution:
-
Add Mergify as a bypass actor with the
exemptbypass mode on every ruleset that has apull_requestrule on the base branch. -
With a classic branch protection, allow Mergify to bypass the required pull requests setting.
In-place checks and review requirements
Section titled In-place checks and review requirementsWhen 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.
Ignored rule types
Section titled Ignored rule typesThe following ruleset rule types are not injected as conditions. If these rules are active on your branches, Mergify does not enforce them itself:
-
required_deployments -
required_linear_history -
non_fast_forward -
Pattern and file rules (
commit_message_pattern,commit_author_email_pattern,committer_email_pattern,file_path_restriction,max_file_path_length,file_extension_restriction,max_file_size) -
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 reproduces them. GitHub still enforces both against every other
actor; to keep the queue’s own merges linear, pick
merge_method: squash or rebase.
required_linear_history is also the one rule in this list Mergify checks for
compatibility, on a fast-forward
queue that tests in batch pull requests. That queue moves the base branch onto
the batch pull request’s head, which carries one merge commit per pull request,
so a linear-history rule on the base branch can never accept it: the queue
reports the incompatibility instead of pushing. The same check covers a
required_signatures rule on the base branch while the commits are unsigned,
as they are on an on-premise installation.
Configuring Mergify as a bypass actor
Section titled Configuring Mergify as a bypass actorTo add Mergify as a bypass actor on a GitHub ruleset:
- Go to your repository Settings > Rules > Rulesets.
- Select the ruleset you want to modify (or create a new one).
- Under Bypass list, click Add bypass.
- Search for the Mergify app and select it.
- Choose Exempt as the bypass mode.
- Save the ruleset.
Bypass actors are configured per ruleset, so repeat this on every ruleset that applies to the branches you queue or to the queue branches.
GitHub Enterprise Server offers the Exempt mode from version 3.20. On an older version, choose Always allow: it covers everything the merge queue does except GitHub-native stacked pull requests.
Was this page helpful?
Thanks for your feedback!