Mergify Merge Queue

作者 mergifyioe7c1ebbc2813无许可证收录于 2026年10月8日更新于 2026年10月8日

Use Mergify merge queue to queue/dequeue PRs, to monitor and inspect the queue, and to diagnose a dequeued PR — whether it is queued, why it was dequeued, where its CI failure is, and what to do next. ALWAYS use this skill when queuing or dequeuing a PR, checking queue status, investigating PR merge state, finding out why a PR left the queue, pausing/unpausing the queue, or debugging merge failures. Triggers on queue a PR, requeue, dequeue, dequeued, why was my PR dequeued, dequeue reason, merge queue, queue status, queue pause, queue show, pause, unpause, frozen, bisecting, batch, CI checks, CHECKS_FAILED, PULL_REQUEST_UPDATED.

仅含说明DevOps & Cloud
AI 生成的概览

操作并诊断 Mergify 合并队列:将 PR 入队或出队、查看队列状态并解释出队原因。

功能
本技能说明如何使用 Mergify 合并队列:通过 PR 评论将拉取请求入队、出队和重新入队,并使用 Mergify CLI 监控队列状态。它说明如何判断 PR 是已入队、已出队、由队列合并还是从未入队,并介绍如何通过 queue show 命令、Mergify 活动日志 API、Mergify Merge Queue 检查运行和 Merge Queue Status 评论来诊断出队。内容还涵盖队列状态查看、队列状态含义、暂停与恢复,以及出队代码与后续操作对照表。
适用场景
适用于将拉取请求入队、出队或重新入队,查看合并队列状态,或排查 PR 为何离开队列或未能合并的情况。也用于暂停或恢复队列,以及调试合并队列的 CI 失败。
运行要求
需要 Mergify CLI 和 Mergify 令牌(直接调用 API 时使用 MERGIFY_TOKEN);GitHub 侧界面需要 GitHub CLI 访问权限。需要访问 Mergify API 和 GitHub 的网络连接。该技能不附带脚本,仅为说明文档。

Mergify Merge Queue

Overview

The merge queue serializes PR merges, running CI on temporary merge commits to catch integration failures before they reach the target branch. Use comments on the PR to queue/dequeue it, and the CLI to monitor queue state, inspect individual PRs, and manage the queue.

mergify queue show <PR> reports on a PR that is no longer in the queue too — whether it was dequeued, merged by the queue, or never queued, and why — so start there for a PR that vanished from the queue. See Diagnosing a dequeued PR; the GitHub-side surfaces documented there are the fallback for when the CLI cannot read the activity log.

Queuing and Dequeuing a PR

Queue, dequeue, and requeue actions are driven by comments on the pull request, not the CLI:

CommentEffect
@mergifyio queueAdd the PR to the merge queue (also use to requeue a PR that was dequeued)
@mergifyio dequeueRemove (dequeue) the PR from the merge queue

@mergifyio requeue is accepted, but it is a deprecated alias that runs the same command as @mergifyio queue — there is no separate requeue behavior. Post @mergifyio queue.

When Mergify processes the comment, it adds a 👍 (thumbs up) reaction to the comment to acknowledge receipt. After queuing, use mergify queue show <PR_NUMBER> to watch the PR's status as it progresses through the queue.

Commands

bash
mergify queue status                 # Show queue status (batches, waiting PRs)mergify queue status --branch main   # Filter by branchmergify queue status --json          # Machine-readable JSON outputmergify queue show <PR_NUMBER>       # Detailed state of a PR in the queuemergify queue show <PR_NUMBER> -v    # Full checks table and conditions treemergify queue show <PR_NUMBER> --json # Machine-readable JSON outputmergify queue pause --reason "..."   # Pause the queue (requires reason)mergify queue unpause                # Resume the queue

That is the whole queue group: status, show, pause, unpause. There is no subcommand for dequeuing a PR — the dequeue reason comes from queue show on a PR that has left the queue, not from a flag. For the full queue trail (every enter / checks / leave event, not just the last exit), use mergify events --pr <PR> --since 90d — see the mergify-events skill.

Is the PR queued, dequeued, or never queued?

Start here — the rest of the workflow branches on this answer.

mergify queue show <PR> answers all four cases. A PR with no queue entry is a normal answer, not an error: the command prints a notice and exits 0 in every case below.

queue show resultMeaning
PR #N block with position / CI stateThe PR is in the queue
PR #N was dequeued <when> + a Dequeue codeIt left the queue without merging — see the reason table
PR #N was merged by the merge queue <when>It left the queue by merging. Not a dequeue
PR #N is not in the merge queueNo queue activity in the retained window — never queued, aged out, or not a real PR number

Under --json, a PR that is not currently queued carries queued: false plus a dequeued discriminator:

bash
mergify queue show 1234 --json | jq -e '.queued == false' >/dev/null && echo "not in queue"
  • dequeued: true — left without merging; the raw leave event is under queue_leave
  • dequeued: false — merged by the queue, or never queued (queue_leave tells the two apart: null means never queued)
  • dequeued: null — the lookup itself failed, and queue_leave_error says why. Not the same as "never queued" — fall back to the GitHub-side surfaces rather than concluding anything

queue_leave_head_sha carries the head the diagnosis describes. Compare it against the PR's current head before reporting anything from it — a dequeue is often caused by a push, so its failing checks routinely belong to a commit the PR no longer has:

bash
mergify queue show 1234 --json | jq -r '.queue_leave_head_sha'   # 31b4a485b8ce…gh pr view 1234 --json headRefOid -q .headRefOid                 # 340361aae580…  → superseded, stay quiet

Two limits to keep in mind. History goes back 90 days (the activity log's retention), so an older dequeue reads as "no activity". And the command does not check that the PR exists, so a typo'd number prints the same "not in the merge queue" notice — confirm the PR is real (gh pr view <PR>) before concluding it was never queued.

Do not use the presence of a Mergify Merge Queue check run as the test — a PR that merely matches the queue conditions gets one titled Waiting for queue conditions without ever being queued. A # Merge Queue Status comment is a reliable positive signal (the pre-queue "Queue this pull request" offer comment deliberately carries no such heading), but its absence proves nothing, since the comment can be disabled per repository.

Diagnosing a dequeued PR

Four surfaces carry the reason. Prefer them in this order.

1. mergify queue show <PR> (start here)

On a PR that is no longer queued, queue show reads the PR's last merge-queue exit and renders it:

PR #37823 was dequeued 24m ago
  Dequeue code:  CHECKS_FAILED  Queue:         default  Queued at:     46m ago  Trigger:       merge queue internal  Head SHA:      31b4a48
  The merge conditions cannot be satisfied due to failing checks
  - `@github-actions/all-greens`
  Failing checks:    ✗ all-greens  failure      https://github.com/Mergifyio/monorepo/actions/runs/…/job/…
  Fix the cause above, then comment `@mergifyio queue` on the pull request.

That is the engine's own explanation plus the failing checks with their job-log URLs — go straight to the failing job rather than hunting for it. The explanation is capped at 12 lines in compact mode; add -v for the whole thing (a PR_DEQUEUED reason embeds the full unmet-condition tree, which runs to dozens of lines).

Head SHA is the commit all of that describes. If it is not the PR's current head, the report is about a commit that has since been replaced — the checks above may already be green again. Check it before acting on the failure.

--json gives the same thing machine-readably: dequeued, plus queue_leave carrying the raw event, so read dequeue_code, reason, and unsuccessful_checks[].details_url from .queue_leave.metadata. Use the promoted queue_leave_head_sha for the staleness check above.

Reading the activity log is best-effort: a token scoped to the merge queue can be refused the repository's event log (403). The command then degrades to the plain "not in the merge queue" notice plus a warning on stderr, and reports dequeued: null — that is the signal to fall through to the surfaces below, not a statement that the PR was never queued.

2. The Mergify activity log (what surface 1 reads underneath)

GET /v1/repos/{owner}/{repo}/logs returns the queue lifecycle events, newest first. queue show calls this for you, and mergify events --pr <PR> --since 90d (the mergify-events skill) browses the whole lifecycle from the CLI — including every event type, not just queue ones. Go direct with curl only when it degrades (403 above, with a token that can read the log) or from somewhere the CLI is not installed:

MERGIFY_TOKEN must hold a Mergify token: a GitHub token still works against the Mergify API but is deprecated, and the credential mergify auth login stores lives in the OS keychain rather than the environment, so a raw curl cannot reach it.

bash
REPO=owner/repoPR=1234FROM=$(date -u -d '90 days ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-90d +%Y-%m-%dT%H:%M:%SZ)
curl -sS -H "Authorization: Bearer ${MERGIFY_TOKEN:?set a Mergify token}" \  "https://api.mergify.com/v1/repos/$REPO/logs?pull_request=$PR&event_type=action.queue.leave&received_from=$FROM" \| jq 'if .events == [] then "no leave event in window — never queued (or aged out)"      else .events[0].metadata           | {merged, dequeue_code, reason,              failing: [.unsuccessful_checks[]? | {name, state, details_url}]}      end'
json
{  "merged": false,  "dequeue_code": "CHECKS_FAILED",  "reason": "The merge conditions cannot be satisfied due to failing checks\n\n- `ci-gate`",  "failing": [    {      "name": "ci-gate",      "state": "failure",      "details_url": "https://github.com/owner/repo/actions/runs/28589756829/job/84771312071"    }  ]}

Read it as:

  • "events": [] / size: 0 → no leave event in the window → the PR was never queued (or the event aged out; see the window rule below).
  • merged: true → it left the queue by merging. Not a dequeue.
  • merged: false → it was dequeued; dequeue_code says why (see the reason table).
  • unsuccessful_checks[].details_url → direct link to the failing CI job log. This is how you reach the CI failure after the PR has left the queue.

Two traps that make this silently return nothing:

  • received_from is required in practice. The window defaults to the last 24 hours. A dequeue from last week returns size: 0 with no error, which reads exactly like "never queued". Always pass received_from.
  • The window may not exceed 93 days (retention is 90 days) or the call fails with 422 'received_from' and 'received_to' cannot span more than 93 days.

Same endpoint, other useful filters: &outcome=failure restricts leave events to dequeues (a merge is success); drop event_type to see the whole lifecycle (action.queue.enter, checks_start, checks_end, leave).

3. The Mergify Merge Queue check run

The check-run title names the reason directly, and its summary is the full queue report:

bash
SHA=$(gh pr view $PR --repo $REPO --json headRefOid -q .headRefOid)gh api "repos/$REPO/commits/$SHA/check-runs" \  -q '.check_runs[] | select(.name=="Mergify Merge Queue") | {conclusion, title: .output.title, summary: .output.summary}'

Titles map to state without any parsing:

output.titleState
Dequeued — <reason> (conclusion neutral)Dequeued, reason in the title
Dequeued from merge queue (conclusion neutral)Dequeued, but the reason did not resolve to a named code — use surface 1
Merged via merge queue (conclusion success)Merged by the queue
Waiting for queue conditions, Checks …, In merge queueStill in the lifecycle

Caveat: the check run lives on the head SHA it was written against. If the dequeue was caused by a push (PULL_REQUEST_UPDATED, DRAFT_PULL_REQUEST_CHANGED), the current head has a fresh check run and the dequeue report sits on the previous SHA. Use surface 1 or 4 in that case — surface 1 names the SHA (Head SHA / queue_leave_head_sha), so it is the one that lets you detect the mismatch rather than fall into it. Note also that gh pr view --json statusCheckRollup returns a null title — go through gh api .../check-runs as above.

4. The # Merge Queue Status comment

mergify[bot] posts one comment per queue session, so read the last one. It survives pushes, which makes it the most robust GitHub-side surface.

bash
gh api --paginate --slurp "repos/$REPO/issues/$PR/comments" \| jq -r '[.[][] | select(.user.login=="mergify[bot]")              | select(.body|contains("# Merge Queue Status"))] | last | .body'

--paginate matters: the endpoint returns 30 comments per page and the newest are on the last page, so without it last silently hands you a stale status comment (or none) on any PR with real discussion. --slurp collects the pages into an array of arrays — hence .[][] to flatten — and is incompatible with -q, so the filter goes through jq instead.

Its structure, in order: a hidden JSON payload, a timeline, the merge conditions, then ## Reason, Failing checks: (each with a [job log] link), and ## Hint. The hidden payload gives the state without parsing prose:

<!--- ... {"version": 1, "state": "dequeued", "queue_rule_name": "default", ...} ... -->

state is one of waiting, checking, frozen, bisecting, merged, dequeued. It does not carry the dequeue code — that is in the ## Reason prose below it.

Caveat: this comment can be turned off per repository (merge_queue.status_comments: none, or outcomes for terminal events only). Absence of a comment does not prove the PR was never queued.

Dequeue reasons and what to do next

dequeue_code values and the action they call for. The engine ships a per-reason ## Hint in the report — for a code not listed here, read that Hint rather than guessing.

dequeue_codeWhat happenedWhat to do next
PR_MERGEDMerged by the queueNothing — this is success
PR_MANUALLY_MERGEDMerged outside the queueNothing
CHECKS_FAILEDRequired checks failed on the merge commitRead unsuccessful_checks[].details_url, fix the CI. Pushing a fix requeues it automatically once conditions match again; if it was flaky, requeue as-is with @mergifyio queue
CHECKS_TIMEOUTchecks_timeout elapsed before conditions were satisfiedCheck the reason's details: checks that never reported mean the check names in your conditions don't match what CI publishes (fix the config, not the PR). Checks still running mean CI is too slow or stuck
PULL_REQUEST_UPDATEDSomeone pushed to the PR while it was queuedStop pushing to a queued PR. Requeue when the branch is final
DRAFT_PULL_REQUEST_CHANGEDThe queue's draft/batch PR got commits Mergify did not createNever push to the merge-queue draft branch. Requeue the original PR
CONFLICT_WITH_BASE_BRANCHThe PR conflicts with its base branchRebase or merge the base branch, resolve conflicts, then requeue
CONFLICT_WITH_PULL_AHEADThe PR conflicts with a PR ahead of it in the queueWait for the PR ahead to merge, then rebase and requeue
BRANCH_UPDATE_FAILEDMergify could not update the PR's head branchRead the reason details, update the branch yourself, requeue
BASE_BRANCH_MISSING / BASE_BRANCH_CHANGEDThe base branch is gone or changedRetarget the PR to a live base branch, then requeue
PR_MANUALLY_DEQUEUEDA human removed it (command, dashboard, or API)The reason names who and how. Requeue only once you know why they pulled it
PR_DEQUEUEDQueue conditions stopped matchingLook at the conditions in the report; fix the PR or requeue
DROPPED_BY_BISECTION_ELIMINATIONBisection blamed other PRs and dropped this one untestedIt is unproven, not known-broken. Requeue to test it on its own
STACK_PREDECESSOR_DEQUEUEDA predecessor in the same stack was dequeuedFix the predecessor, requeue the stack
QUEUE_RULE_MISSING / CONFIGURATION_CHANGEDThe config changed under the queued PRFix .mergify.yml (see the mergify-config skill), then requeue
INCOMPATIBILITY_WITH_BRANCH_PROTECTIONSQueue settings clash with branch protectionsReconcile the repository's branch protections with the queue config — requeuing alone will not help
UNPROCESSABLE_PULL_REQUESTToo many check runs, comments, or files for Mergify to processShrink the PR

Not every code means the PR left the queue. These reasons interrupt the checks and the PR stays queued — do not treat them as a dequeue and do not requeue:

PR_AHEAD_DEQUEUED, BATCH_AHEAD_FAILED, PR_WITH_HIGHER_PRIORITY_QUEUED, MERGE_QUEUE_RESET, SCHEDULED_FREEZE_STATUS_CHANGED, SPECULATIVE_CHECK_NUMBER_REDUCED, INTERMEDIATE_RESULTS_SKIPPED, CHECKS_RETRIED, BATCH_SCOPES_CHANGED, SCHEDULE_BLOCKED_AHEAD_YIELDED, PR_CHECKS_STOPPED_BECAUSE_MERGE_QUEUE_PAUSE

They arrive as abort_code on an action.queue.checks_end event (with aborted: true) rather than as dequeue_code on a leave event, and the check-run title reads Checks restarted — … or Checks aborted — … rather than Dequeued — …. The authoritative test for "did it actually leave the queue" is an action.queue.leave event with merged: false — not the presence of a code from this list.

Checking Queue Status

Use mergify queue status to see the current state of the merge queue:

  • Batches: groups of PRs being tested together, shown with their CI status and ETA
  • Waiting PRs: PRs queued but not yet in a batch, shown with priority and queue time
  • Pause state: whether the queue is paused and why

Use --json when you need to parse the output programmatically.

Inspecting a PR in the queue

Use mergify queue show <PR_NUMBER> to check why a PR is stuck or how it's progressing:

  • Position: where the PR sits in the queue
  • Priority: which priority rule matched
  • CI timeout: when the queue will give up on the PR's checks (- when no timeout is configured) — watch this to catch a CHECKS_TIMEOUT before it fires
  • CI state: whether checks are passing, pending, or failing
  • Conditions: which conditions are met and which are blocking
  • Use -v (verbose) for the full checks table and conditions tree

-v lists check names and states only — no links to the CI jobs. For job-log URLs on a PR that is still queued, use the GitHub-side surfaces above (the check-run summary and the status comment); once the PR has left the queue, queue show itself prints them. --json is a raw passthrough of the API payload, so it carries one more field the human render drops: queue_rule (the resolved queue rule config, not just its name).

Queue States

StateMeaning
runningBatch is actively running CI
preparingBatch is being set up
bisectingBatch failed, bisecting to find the culprit
failedCI failed for this batch
mergedPRs in this batch have been merged
waiting_for_mergeCI passed, waiting for GitHub to merge
waiting_for_previous_batchesBlocked on earlier batches completing
waiting_for_batchWaiting to be picked up into a batch
waiting_for_requeueA batch ahead failed; this batch will be re-embarked
waiting_scheduleOutside the configured merge schedule
frozenQueue is paused

Pausing and Unpausing

Pause the queue to temporarily halt all merges (e.g., during incidents or deployments):

bash
mergify queue pause --reason "production incident — halting merges"mergify queue unpause
  • Pausing does not cancel running CI — it prevents new merges from starting
  • The reason is visible to all team members in the queue status
  • Use --yes-i-am-sure to skip the confirmation prompt in scripts

Troubleshooting

PR not entering the queue:

  • Make sure the PR was queued: post @mergifyio queue and confirm Mergify reacted with 👍 on the comment
  • Check that the PR's merge conditions are met: mergify queue show <PR_NUMBER> -v
  • Look at the conditions section for unmet requirements
  • Do not assume the queue command never landed: queue show tells a PR that was queued and dequeued apart from one that never entered

PR stuck in queue:

  • Check CI state: mergify queue show <PR_NUMBER>
  • If checks are failing, -v names them; for the job logs, read the Failing checks: links in the # Merge Queue Status comment or the Mergify Merge Queue check-run summary
  • If the queue is paused, check who paused it: mergify queue status

PR disappeared from the queue:

  • mergify queue show <PR_NUMBER> — it says whether the PR was dequeued, merged by the queue, or never queued, and prints the dequeue code, the reason, and the failing checks' URLs
  • Never assume "not in the merge queue" means "never queued": read the headline (or dequeued under --json) before telling anyone to requeue
  • Then act per the reason table

Queue moving slowly:

  • Check for failing batches that trigger bisection: mergify queue status
  • Bisecting batches test PRs individually, which is slower than batch merging

来源与署名

来源:mergifyio/mergify-cli位于skills/mergify-merge-queue提交e7c1ebb

许可证: 无许可证

内容归原作者所有。SourceWeft 从公开仓库中收录这些内容。

举报或申请下架