Merge Queue & Branch Protection
Strict status checks
"Require branches to be up to date before merging" is the difference between a green check that means something and one that does not.
Without it, a PR can merge on a green run against a base from three days ago — the checks passed against code that no longer exists. With it, the head must be current, so every merge is gated on a run against the real post-merge state.
The cost is rebase churn on busy repositories. That is what merge queues solve.
What a merge queue actually does
The queue builds a speculative merge of your PR on top of the queue ahead of it, runs checks against that combination, and merges only if it passes. This gives strict-check correctness without every author manually rebasing.
Consequences worth knowing:
- Your PR's own green checks are not the queue's checks. The queue re-runs against the speculative merge.
- A PR can be ejected from the queue when the speculative merge fails — even though nothing about your PR changed. The conflict is with what landed ahead of you.
- Repeated ejection for the same check means a genuine interaction, not bad luck. After two ejections on the same check, pull the PR out and triage rather than re-queueing a third time.
Prefer enable_pr_auto_merge over merging directly in queue repositories, so
the queue owns ordering.
mergeable_state values
| Value | Meaning |
|---|---|
clean |
Ready |
blocked |
A required check is failing or a required review is missing |
behind |
Head is behind the base and strict checks are required |
dirty |
Merge conflict |
unstable |
A non-required check is failing — mergeable, but look at why |
draft |
Draft PR |
has_hooks |
Clean, with a pre-receive hook that may still reject |
unstable is the one that gets misread. It means mergeable; it does not mean
fine.
Protection settings that matter
| Setting | Why |
|---|---|
| Required status checks, strict | Green means green against the current base |
| Dismiss stale approvals on push | Otherwise an approval survives a rewrite of the diff it approved |
| Require conversation resolution | Unresolved blocking feedback cannot merge |
| Restrict force push and deletion | |
enforce_admins |
A bypass available to admins is a bypass |
| Required linear history | Forces squash or rebase; simplifies revert |
enforce_admins: false is the most common silent gap — the protection looks
complete on the settings page and is bypassable by anyone with admin.
Diagnosing a blocked merge
Check in this order and stop at the first hit:
- Is a required check failing at the current head sha?
- Is a required check missing — never reported, not failed? A renamed job is a required check that will never arrive.
- Is the review quorum unmet, or is a
CHANGES_REQUESTEDoutstanding? - Are there unresolved conversations?
- Is the head behind with strict checks required?
- Is there a conflict?
- Is the PR a draft?
Point 2 is the one that wastes the most time — a required check that was renamed in the workflow leaves the PR waiting forever for a job that no longer exists.
Never
Never merge with a red required check. Never use admin bypass. Never re-run a check to turn it green without classifying why it was red.
See also
stacked-prs— landing dependent PRs through a queuegithub-orchestration— the gate list enforced before merging