Skip to content

Test each pull request together with the ones ahead of it, so main only moves to a state whose checks passed.

Merging one pull request at a time, each caught up with main, keeps every merge clean as text. It does not prove the result works: two changes can merge without a conflict and still break each other. With the merge queue on, a pull request is tested together with everything ahead of it before it lands, and main only ever moves to a state whose checks passed.

The merge queue runs in g1t’s sandboxes, which work in any workspace with its own model provider and in those g1t’s hosted models are open to. Elsewhere, an entry fails at once with a message saying so; turn the queue off to merge directly.

  1. Open the project’s Settings → Repository. You need the Maintain role or higher on its repository.
  2. Turn on Merge through a queue.
  3. Save.

From the API, send merge_queue to PATCH /repos/{owner}/{name}/settings (or update_repo_settings):

curl -X PATCH https://api.g1t.sh/repos/acme/web/settings \
-H "Authorization: Bearer $G1T_TOKEN" \
-H "Content-Type: application/json" \
-d '{"merge_queue": true}'

Merging a pull request, from its page (Add to the merge queue), with merge_pull_request, or with POST /repos/{owner}/{name}/pulls/{number}/merge, adds it to the queue instead of changing main. Everything a merge needs is still checked first: the pull request must be ready for review, its checks must have passed and it must have the approvals the repository asks for. Only people with the Write role or higher can add to the queue. Merging a pull request that is already queued changes nothing.

The pull request’s conversation records who added it, and its page shows where it is in the queue. Anyone who can merge can take it out with Remove from the queue. Closing a pull request also takes it out.

g1t takes up to four entries from the front of the queue and tests them all at once, speculatively, each in its own sandbox. Each sandbox builds main with that entry and every entry ahead of it merged in, in queue order:

Entry Tested as
1st main + #41
2nd main + #41 + #44
3rd main + #41 + #44 + #46
4th main + #41 + #44 + #46 + #47

If every entry passes, the four can land one after another without being tested again. The next batch starts when nothing is being tested. A batch that takes longer than 45 minutes is tested again.

Each tested state runs:

  • the acceptance checks of every pull request in it; and
  • the contract: the checks of issues already completed on the repository, from the 30 most recently closed. Once an issue lands, its checks become part of what main promises, and every later change is held to them.

So a change that breaks something that landed before it is caught here, even when it merges without a conflict and its own checks pass.

Once those pass, the repository’s GitHub Actions workflows that run on: merge_group run on the state too, with the same merge_group event GitHub sends, on the branch g1t-queue/<entry>. The entry waits for them, and lands only if they pass. The branch is deleted once the entry lands or leaves the queue. A workflow opts in like this:

on:
pull_request:
merge_group:

A contract check that fails is run again on main alone. If it fails there too, it was broken already: it is marked as passing with a note, “already failing on the default branch; not held against this”, and does not hold the change back.

Entries land in order. When an entry has passed and everything ahead of it has landed, main moves to exactly the state that was tested. The issue closes and the other pull requests for it are superseded, as with any merge.

Before landing, g1t checks that nothing has changed underneath:

  • If the pull request was pushed to after it was tested, it and the entries tested on top of it are tested again.
  • If main moved outside the queue, every entry is tested again on the new main.

A pull request that is already known to conflict with main is not added to the queue: its merge box says which files conflict and how to resolve them first. One that is only behind main does not need to catch up to join the queue, since the queue tests it on top of main. Where the repository requires pull requests to be up to date, catch it up first: when it and main changed different files that takes a few seconds and no agent.

An entry fails when its checks or its merge_group workflows fail in the combined state, when it does not merge cleanly with what is ahead of it, or when the state cannot be built. It leaves the queue, and:

  1. Its pull request gets a failed check run. Each command is named with the state it ran in, such as cargo test (merge queue, on the default branch with #41 merged in first), and the run says why it failed. A conflict names the pull request ahead it collided with, and the files.
  2. Its conversation records that it was taken out of the queue, and why: a conflict links the pull request it collided with and each conflicting file, which opens in the pull request’s changes.
  3. The entries that were tested on top of it are tested again without it.

A g1t agent’s pull request is then sent back to revise, like any failed check, starting from main as it is now. The revision counts towards Revisions before asking you. Once it is ready again, a repository with Merge automatically when ready on adds it to the queue again by itself; otherwise it waits for someone to merge it again. A pull request you or your own agent opened is yours to fix and merge again.

Every repository has a Merge queue page, at g1t.sh/<workspace>/<repo>/queue, in the repository’s sidebar. It refreshes on its own while anything is queued.

In the queue lists the entries in order, starting from main’s commit. Each shows:

State Waiting, Testing or Passed.
Tested as main and the pull requests merged into it, such as main + #41 + #44.
Checks How many of the checks passed.
Who The agent or person who made the pull request, and who queued it.
Commit The tested state’s commit.

Recently lists the last 20 that left the queue: Landed, Failed or Removed. A failed entry shows why, and the output of the checks that failed.

get_merge_queue, or GET /repos/{owner}/{name}/queue, returns the queue. It is public for a public repository.

curl https://api.g1t.sh/repos/acme/web/queue
{
"enabled": true,
"active": [
{
"number": 44,
"title": "Add a --shout flag",
"agent": "g1t-agent",
"state": "testing",
"ahead": [41],
"base_commit": "8f3c2e1…",
"combined_commit": null,
"results": [],
"enqueued_by": "g1t"
}
],
"recent": []
}
Field
enabled Whether the repository merges through the queue.
active The entries waiting to land, in order.
recent Those that landed or left, newest first.
state waiting, testing, passed, failed, landed or removed.
ahead The pull requests merged ahead of it in the state being tested. Empty when it was tested on main alone.
base_commit The commit of main the state was built on.
combined_commit The tested state.
results The checks run against it, each with command, passed and output.
error Why it failed: a conflict, or what could not be run.
enqueued_by Who added it: a username, or g1t when it was merged automatically.