Skip to content
215 linesCodeBlameRaw
1---
2title: Merge queue
3description: Test each pull request together with the ones ahead of it, so main only moves to a state whose required checks passed.
4---
5
6Merging one pull request at a time, each caught up with `main`, keeps every
7merge clean as text. It does not prove the result works: two changes can
8merge without a conflict and still break each other. With the merge queue
9on, a pull request is tested together with everything ahead of it before it
10lands, and `main` only ever moves to a state whose required checks passed.
11
12The merge queue runs in g1t's sandboxes, which need
13[the g1t plan](/guides/usage-and-billing/#the-g1t-plan) or the trial after
14a card check; a public repository can use the open-source pool instead.
15Without one, an entry fails at once with a message saying so; turn the
16queue off to merge directly.
17
18## Turn it on
19
201. Open the project's **Settings → Rules**. You need the Admin
21 [role](/guides/access-and-roles/) on its repository.
222. Open the ruleset that covers the default branch, or create one.
233. Choose **Add a rule**, then **Require the merge queue**. Set how many
24 pull requests it tests at once, the smallest batch it starts with and
25 how long it waits for one, and how long a batch's checks may take.
264. Save.
275. Add `merge_group` to the `on:` of every workflow behind a
28 [required status check](/guides/pull-requests/#required-status-checks),
29 so that it runs on the queue's states too
30 ([below](#what-each-state-is-held-to)).
31
32From the API, send `merge_queue` to `PATCH /repos/{owner}/{name}/settings`
33(or `update_repo_settings`), which adds the rule to the "Default branch
34protection" [ruleset](/guides/rules/):
35
36```sh
37curl -X PATCH https://api.g1t.sh/repos/acme/web/settings \
38 -H "Authorization: Bearer $G1T_TOKEN" \
39 -H "Content-Type: application/json" \
40 -d '{"merge_queue": true}'
41```
42
43## What merging does with the queue on
44
45Merging a pull request, from its page (**Add to the merge queue**), with
46the `pull_request` tool's `merge` action, or with
47`POST /repos/{owner}/{name}/pulls/{number}/merge`,
48adds it to the queue instead of changing `main`. Everything a merge needs
49is still checked first: the pull request must be ready for review, every
50[required check](/guides/pull-requests/#required-status-checks) must have
51passed on its head, and it must have the approvals the repository asks
52for, including its [code owners'](/guides/codeowners/#require-review-from-code-owners)
53when it requires them. Only people with the Write [role](/guides/access-and-roles/) or higher can add to the
54queue. Merging a pull
55request that is already queued changes nothing.
56
57The pull request's conversation records who added it, and its page shows
58where it is in the queue. Anyone who can merge can take it out with **Remove from the
59queue**. Closing a pull request also takes it out.
60
61## How entries are tested
62
63g1t takes up to four entries (the rule's `max_entries_to_build`) from the front of the queue and tests them all
64at once, speculatively, each in its own sandbox. Each sandbox builds `main`
65with that entry and every entry ahead of it merged in, in queue order:
66
67| Entry | Tested as |
68| --- | --- |
69| 1st | `main` + #41 |
70| 2nd | `main` + #41 + #44 |
71| 3rd | `main` + #41 + #44 + #46 |
72| 4th | `main` + #41 + #44 + #46 + #47 |
73
74If every entry passes, the four can land one after another without being
75tested again. The next batch starts when nothing is being tested. A batch
76that takes longer than 45 minutes (`check_response_timeout_minutes`) is tested again.
77
78### What each state is held to
79
80g1t pushes each state it built to a branch of its own, `g1t-queue/<entry>`,
81and runs the repository's [workflows](/guides/actions/) that run on
82`merge_group` on it. The entry waits for them, and passes only if:
83
84- every `merge_group` workflow it started passed; and
85- every [required status check](/guides/pull-requests/#required-status-checks)
86 of the default branch passed on that commit.
87
88So a change that breaks something another change ahead of it relies on is
89caught here, even when it merges without a conflict and its own checks
90passed. The branch is deleted once the entry lands or leaves the queue.
91
92A workflow opts in like this:
93
94```yaml
95on:
96 pull_request:
97 merge_group:
98```
99
100Required checks only report on a queued state if their workflows run on
101`merge_group`. When the branch requires checks and no workflow runs on
102`merge_group`, the entry fails with a message saying so: "the required
103check CI cannot report on it: no workflow runs on merge_group events. Add
104merge_group to the on: of the workflows the branch requires". A required
105check that a workflow did not report on the state fails it the same way.
106A repository that requires no checks and has no `merge_group` workflows
107only has each state built: an entry passes once it merges cleanly with
108what is ahead of it.
109
110## How entries land
111
112Entries land in order. When an entry has passed and everything ahead of it
113has landed, `main` moves to exactly the state that was tested. The issue
114closes and the other pull requests for it are superseded, as with any
115merge.
116
117Before landing, g1t checks that nothing has changed underneath:
118
119- If the pull request was pushed to after it was tested, it and the entries
120 tested on top of it are tested again.
121- If `main` moved outside the queue, every entry is tested again on the new
122 `main`.
123
124A pull request that is already known to conflict with `main` is not added
125to the queue: [its merge box](/guides/pull-requests/#conflicts) says which
126files conflict and how to resolve them first. One that is only behind `main`
127does not need to catch up to join the queue, since the queue tests it on
128top of `main`. Where the repository requires pull requests to be up to
129date, [catch it up](/guides/pull-requests/#catching-up) first: when it and
130`main` changed different files that takes a few seconds and no agent.
131
132## When an entry fails
133
134An entry fails when its `merge_group` workflows or required checks fail
135on the combined state, when it does not merge cleanly with what is ahead of it, or
136when the state cannot be built.
137It leaves the queue, and:
138
1391. Its pull request records the failure, saying why: which workflow failed
140 on the state, which required check did not report, or, for a conflict,
141 the pull request ahead it collided with and the files.
1422. Its conversation records that it was taken out of the queue, and why: a
143 conflict links the pull request it collided with and each conflicting
144 file, which opens in the pull request's changes.
1453. The entries that were tested on top of it are tested again without it.
146
147A pull request g1t opened is then sent back to revise, as for any failed
148check, starting from `main` as it is now. The revision counts towards
149**Revisions before asking you**. Once it is ready again, a repository with
150**Merge automatically when ready** on adds it to the queue again by itself;
151otherwise it waits for someone to merge it again. A pull request you or
152your own agent opened is yours to fix and merge again.
153
154## The Merge queue page
155
156Every repository has a **Merge queue** page, at
157`g1t.sh/<workspace>/<repo>/queue`, in the repository's sidebar. It
158refreshes on its own while anything is queued.
159
160**In the queue** lists the entries in order, starting from `main`'s commit.
161Each shows:
162
163| | |
164| --- | --- |
165| State | **Waiting**, **Testing** or **Passed**. |
166| Tested as | `main` and the pull requests merged into it, such as `main + #41 + #44`. |
167| Checks | How its state's checks stand. |
168| Who | The agent or person who made the pull request, and who queued it. |
169| Commit | The tested state's commit. |
170
171**Recently** lists the last 20 that left the queue: **Landed**, **Failed**
172or **Removed**. A failed entry shows why.
173
174## From the API or an agent
175
176The `pull_request` tool's `merge_queue` action, or
177`GET /repos/{owner}/{name}/queue`, returns the queue.
178It is public for a public repository.
179
180```sh
181curl https://api.g1t.sh/repos/acme/web/queue
182```
183
184```json
185{
186 "enabled": true,
187 "active": [
188 {
189 "number": 44,
190 "title": "Add a --shout flag",
191 "agent": "g1t",
192 "state": "testing",
193 "ahead": [41],
194 "base_commit": "8f3c2e1…",
195 "combined_commit": null,
196 "results": [],
197 "enqueued_by": "g1t"
198 }
199 ],
200 "recent": []
201}
202```
203
204| Field | |
205| --- | --- |
206| `enabled` | Whether the repository merges through the queue. |
207| `active` | The entries waiting to land, in order. |
208| `recent` | Those that landed or left, newest first. |
209| `state` | `waiting`, `testing`, `passed`, `failed`, `landed` or `removed`. |
210| `ahead` | The pull requests merged ahead of it in the state being tested. Empty when it was tested on `main` alone. |
211| `base_commit` | The commit of `main` the state was built on. |
212| `combined_commit` | The tested state. |
213| `results` | What building the state recorded, each with `command`, `passed` and `output`. The workflow runs on it are on its commit, `combined_commit`. |
214| `error` | Why it failed: a conflict, a workflow that failed on it, a required check that did not report, or what could not be built. |
215| `enqueued_by` | Who added it: a username, or `g1t` when it was merged automatically. |