Keep Merge Queue Pull Requests Out of Your Way
Why the merge queue opens draft pull requests, when it checks in place instead, and how to filter queue pull requests out of searches, notifications, webhooks and CI.
To test what it is about to merge, the merge queue often opens a temporary draft pull request on a branch of its own: a queue pull request, called a batch PR on other pages. Queue pull requests show up in your pull request list, in webhooks, in deploy previews and in any tool that reacts to new pull requests. This page explains when and why the queue opens them, how to configure a queue that does without, and how to filter them out everywhere else.
Why the Queue Opens Draft Pull Requests
Section titled Why the Queue Opens Draft Pull RequestsThe queue has to run your CI on the exact code it is about to merge. Often that code is not any single pull request’s branch:
-
Batches. With
batch_sizeabove 1, the queue tests several pull requests together, so it builds a branch holding all of them on top of the base branch. -
Parallel checks. With
max_parallel_checksabove 1, the queue tests the next pull requests speculatively, on top of the ones still being checked ahead of them. Each of those predicted states needs its own branch. -
Bisection. When a batch fails, the queue splits it to find the culprit, and each half is tested on a new branch.
-
Retries. With
max_checks_retriesabove 0, every retry recreates the draft to get a fresh CI run. -
Two-step CI. With
merge_conditionsthat differ fromqueue_conditions, the merge conditions are evaluated on the draft, so the heavier suite runs there and not on the original pull request. -
Parallel and isolated modes. In
parallelandisolatedmodes, the queue tests several groups of pull requests side by side, each on its own branch.
The draft pull request is how your CI learns about that branch and how its results come back to the queue. Mergify deletes the branch, and with it the draft, once the batch is done.
When the Queue Checks in Place
Section titled When the Queue Checks in PlaceWhen none of the above applies, the queue skips the draft. It updates the original pull request with the latest base branch, runs CI on the pull request itself, and merges it. A queue rule checks in place only when all of these hold:
-
merge_conditionsis empty, or lists the same conditions asqueue_conditions(no two-step CI). -
batch_sizeis 1. With a{min, max}range,maxis 1. -
max_checks_retriesis 0. -
merge_queue.max_parallel_checksis 1. -
merge_queue.modeisserial.
batch_size, max_checks_retries and mode default to those values, but
max_parallel_checks defaults to 5. A queue that has never touched it opens
draft pull requests, so the smallest configuration that checks in place sets it
explicitly:
merge_queue: max_parallel_checks: 1
queue_rules: - name: default queue_conditions: - check-success = ciThe conditions are the same list as in In-place checks (no batch
PRs), which also covers
update_bot_account and pull requests coming from forks.
Checking in place gives up what the drafts were buying. The queue tests one pull request at a time, so it merges at most one pull request per CI run, and every queued pull request gets an update from the base branch pushed to it. See The Trade-offs to weigh that against your merge rate. If you need batches or parallel checks, keep the drafts and filter them with the recipes below.
Recognize a Queue Pull Request
Section titled Recognize a Queue Pull RequestA queue pull request carries four markers:
-
Head branch: the
queue_branch_prefixfollowed by 10 hexadecimal characters, for examplemergify/merge-queue/a76b1f3d25. -
Author:
mergify[bot], writtenapp/mergifyin GitHub search, unless the queue rule setsdraft_bot_account. -
State: draft.
-
Title: starts with
merge queue:.
Match on the head branch. It holds for every queue pull request, whoever opens
it and whatever state it is in, while the others have exceptions: a queue rule
with draft_bot_account opens drafts under that user’s name, the
merge-batch method marks the
draft ready for review before merging it, and a draft state also matches the
drafts your own developers open.
The prefix defaults to mergify/merge-queue/ and is set per queue rule. If you
change it, change your filters to match. See How Leftover Branches Are
Matched for the exact
shape of the name.
Filter Recipes
Section titled Filter RecipesGitHub Search
Section titled GitHub SearchAdd -author:app/mergify to a pull request search to hide queue pull requests:
is:pr is:open -author:app/mergifyIf a queue rule sets draft_bot_account, filter on the branch instead.
head: matches branch names starting with the value:
is:pr is:open -head:mergify/merge-queue/Save the search as a bookmark, or paste it into the filter box of the pull request list.
Webhooks and Slack
Section titled Webhooks and SlackA service that receives GitHub’s pull_request webhook can drop queue pull
requests by testing the head branch in the payload. In Python:
QUEUE_BRANCH_PREFIX = "mergify/merge-queue/"
def is_queue_pull_request(payload: dict) -> bool: return payload["pull_request"]["head"]["ref"].startswith(QUEUE_BRANCH_PREFIX)The same test fits any automation that posts to Slack or another chat tool,
whether a workflow or a bot of your own. To also drop the
drafts your developers open, test payload["pull_request"]["draft"] as well,
but keep the branch test: a draft check alone misses the queue pull request
once merge-batch marks it ready.
The GitHub app for Slack filters pull requests by label only, so it cannot exclude queue pull requests by branch or author. Route those notifications through a filter of your own if they are too noisy.
GitHub Actions
Section titled GitHub ActionsSkip a job on queue pull requests with an if: condition on the head branch.
This suits jobs that do not gate the merge, such as deploy previews or
labelers:
on: pull_request:
jobs: preview: if: ${{ !startsWith(github.head_ref, 'mergify/merge-queue/') }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: make deploy-previewThe queue also pushes its branch, so a workflow triggered on push runs for it
too. On push, filter with branches-ignore, which matches the pushed branch:
on: push: branches-ignore: - "mergify/merge-queue/**"To do the opposite and run a job only in the queue, see Two-Step CI.
CircleCI
Section titled CircleCICircleCI builds the queue branch when it is pushed. Ignore it in a workflow’s branch filter:
workflows: preview: jobs: - deploy-preview: filters: branches: ignore: /^mergify\/merge-queue\/.*/Buildkite
Section titled BuildkiteSkip a step on queue branches with a conditional:
steps: - label: ":rocket: Deploy preview" command: make deploy-preview if: build.branch !~ /^mergify\/merge-queue\//A step-level branches: "!mergify/merge-queue/*" filter does the same. The
pipeline-wide branch filter in the pipeline settings skips every step,
including the ones your merge_conditions require, so keep it for pipelines
that never gate a merge.
Jenkins
Section titled JenkinsIn a multibranch pipeline, the queue branch can be built both as a branch and
as a pull request, where BRANCH_NAME becomes PR-123. Cover both with a
when block:
stage('Deploy preview') { when { not { anyOf { branch 'mergify/merge-queue/*' changeRequest branch: 'mergify/merge-queue/*', comparator: 'GLOB' } } } steps { sh 'make deploy-preview' }}Vercel
Section titled VercelTurn off Vercel deployments for queue branches in vercel.json with
git.deploymentEnabled:
{ "git": { "deploymentEnabled": { "mergify/merge-queue/*": false } }}Vercel then skips the preview deployment for every queue branch. With a custom
queue_branch_prefix, use that prefix followed by *.
Was this page helpful?
Thanks for your feedback!