How the merge queue works
How it works
What a queued PR needs
A labeled PR keeps its place in line, on hold, until GitHub would merge it and the queue can take it:
- It is not a draft.
- It has the reviews its branch protection or rulesets require, and no requested changes from a required reviewer. Mergepatrol reads GitHub’s review decision, so a repo that requires no review needs none.
- It targets the default branch, or a branch in
branchesof the config file. - With Automatic review on, the agent’s review cleared it, or a human answered what it asked.
- On the hosted Mergepatrol, a private repo has a subscription, and a public repo’s workspace is under the fair-use limit.
Then it runs, rebased alone or tested in a batch, and merges once its checks pass. Only the base branch’s required checks count. A branch with no required checks counts every check on the commit, so turn on required checks for CI that must pass. The merge uses the repo’s default merge method on GitHub. The PR’s comment and the dashboard say what holds a PR.
When a rebase has conflicts, an agent resolves them. Claude is the default. A plain git rebase runs first, so the agent runs only on real conflicts.
When a PR’s required checks fail, an agent fixes them too. It reads what GitHub shows of the failure, commits a fix on top of the PR, and the checks run again. After two fixes that do not make the checks pass, or when the agent gives up because the failure is not the PR’s to fix (a flaky test, an outage), the PR is ejected.
Automatic review
With Automatic review on in Settings, an agent reviews every queued PR before it takes its place in line. It reads the change and the code around it, and reports each thing it looked at with how sure it is. What it accepts and is sure enough of, it clears alone. Anything else, it asks a human about: it requests a review from the code owners of the lines in question, comments on the PR, and lists only those findings on the dashboard, each with its code, why the agent was unsure, and what it already checked. The human answers each one, fine as is or needs a change, or asks the agent to dig further. All fine approves the PR, as the human’s review on GitHub. A change asked for is a review with the change on its line: the PR keeps its place, and the agent reviews it again on the next push, knowing what was answered. The agent never approves on GitHub itself, and never sends a PR back alone.
How sure is sure enough depends on the kind of change. The defaults: 90% for security, 85% for dependencies and for public APIs, 80% for everything else, and migrations always go to a human. Settings shows how the agent’s confidence has held up against the humans’ answers, to set each bar by. Automatic review is off until a workspace turns it on, since it runs the agent on the workspace’s key. It does not review PRs from forks: a human reviews those. A rebase or a fix by the queue keeps the approval.
Required checks that have not finished 3 hours after a rebase or a fix also eject the PR, with no fix: a required check that no workflow runs would hold the queue forever. In a batch, it counts as a failure.
An error outside the PR’s job does not eject it. That covers GitHub being down, or slow to answer, as well as a rate limit. The PR keeps its place at the head of the queue, and its job runs again. Only after 30 minutes of such errors is it ejected.
Branches
The default branch has a queue, and so does each branch that branches in .mergepatrol/mergepatrol.yml lists, release say. A labeled PR joins the queue of the branch it targets, and is rebased onto that branch. A PR into any other branch is held, as is a PR stacked by hand on another PR’s branch: a stack made with gh stack joins the queue of the branch the whole stack merges into. The queues of a repo take turns, one job each, so a busy branch does not hold another up; they share the repo’s git cache, so two never run a job at once. A batch holds PRs into one branch only.
A PR retargeted to another branch leaves its queue, and joins the other branch’s at the back. Deleting a branch closes the PRs into it, which leave its queue. A branch taken off the list holds its PRs. The settings in the file, on the default branch, apply to every branch’s queue.
Batches
A queue that runs CI once per PR merges a few PRs an hour. So when several queued PRs have passed their own checks, Mergepatrol tests them together:
- It merges them, in queue order, onto a branch
mergepatrol/batch-<PRs>at the base branch, and opens a draft PR from it, so the repo’spull_requestCI runs on the combination. The draft’s title is the first PR’s title, plus a note, so a PR title check passes. - When the required checks pass, it merges the PRs one by one, as they are: no rebase, and no second CI run. Each merge must leave the base branch at the tree that the batch tested, else it stops.
- It deletes the branch, which closes the draft PR.
When a batch fails, Mergepatrol tests half of it next, and so on down to one PR, which runs alone: rebased, with its own checks. That PR is ejected; the rest merge.
A PR runs alone when its own checks have not passed, when it conflicts with the PRs ahead of it, and on a base branch that requires PRs to be up to date before they merge. Mergepatrol reads that setting from the rulesets, and from classic branch protection with the App’s administration: read permission. Without the permission, every base branch counts as requiring it. An App created before this permission existed must be given it on GitHub.
Stacks
A stack of PRs, made with gh stack, is one item of the queue: the label on any of its PRs queues the whole stack, and Mergepatrol merges it all at once, with GitHub’s asynchronous merge. Either every PR of the stack merges, or none does. GitHub merges a stack only when the required checks of each of its PRs passed.
- The stack waits while any of its PRs is a draft or waits for a review.
- In a batch, the stack is one step: its top PR holds every layer below it.
- Alone, Mergepatrol rebases each layer onto the one below it, the bottom onto the base branch, and waits for each layer’s checks. A failed check is fixed on the layer it failed on, and the layers above it are rebased onto the fix.
- A failed stack is ejected whole.
Every merge goes through the asynchronous merge API, stacked or not. A merge still pending when Mergepatrol restarts is waited for, not requested again.
PRs from forks
A maintainer queues a PR from a fork with the label, as any other. Its author is not trusted as the repo’s own are, so the queue treats it differently:
- It merges the PR only as it was when the label went on. Mergepatrol reads that head from the
labeledwebhook, or, without webhooks, from when it first sees the PR. If the author pushes after that, the PR leaves the queue, and a maintainer adds the label again to queue the new commits. - It never tests the PR in a batch, whose draft PR runs CI with the repo’s secrets. The PR runs alone, with the fork’s own CI.
- It runs no agent on the PR: a plain
git rebaseonly. A conflict, or a failed required check, ejects the PR, for its author to fix. - It pushes the rebase to the fork, which GitHub allows while the author allows edits by maintainers. Without that, a PR that needs a rebase waits on hold until they allow it. GitHub does not let the App push to a fork that an organization owns, nor push changes to workflow files to a fork: such a PR that needs a rebase is ejected, for its author to rebase.
- While GitHub waits for a maintainer to approve the fork’s workflow runs, the PR waits on hold.
A stack with a PR from a fork is not supported yet.
The You tab reads Mergepatrol’s own database, never GitHub, when you open it. Webhooks tell Mergepatrol which PRs changed, and it reads those again from GitHub as the App. It also reads every open PR of each repo once a day, in case a webhook got lost. Without webhooks, as on a self-hosted http URL, it reads them every 10 minutes. Team review requests need the App’s members: read permission, which each organization’s owner accepts. An App created before this permission existed must be given it on GitHub, along with the pull_request_review_comment, issue_comment, membership and team events. Until then, only review requests made to you directly show.
Docs
- Get started with Mergepatrol
Sign in with GitHub, install the App, merge the setup PR, add a model key, and label a pull request to queue it.
- Configuration: .mergepatrol/mergepatrol.yml
Every setting in a repo's .mergepatrol/mergepatrol.yml: the label, batch size, the agent's model, verify commands, CI fixes, extra branches and review bars.
- Pricing
Mergepatrol is free on public repos. Private repos cost $15 a seat a month after a 14-day trial with no card. Self-hosting is free.
- MCP server and API
Let Claude Code, Codex or Cursor queue and review PRs through Mergepatrol's MCP server, or read a PR's queue state from the JSON API.
- Self-host Mergepatrol
Run the open-source Mergepatrol yourself with Docker Compose, configure it, bring your own GitHub App, and isolate its jobs in gVisor sandboxes.