g1t/apps/docs/src/content/docs/guides/merge-queue.md
| 1 | --- |
| 2 | title: Merge queue |
| 3 | description: Test each pull request together with the ones ahead of it, so main only moves to a state whose checks passed. |
| 4 | --- |
| 5 | |
| 6 | Merging one pull request at a time, each caught up with `main`, keeps every |
| 7 | merge clean as text. It does not prove the result works: two changes can |
| 8 | merge without a conflict and still break each other. With the merge queue |
| 9 | on, a pull request is tested together with everything ahead of it before it |
| 10 | lands, and `main` only ever moves to a state whose checks passed. |
| 11 | |
| 12 | The merge queue runs in g1t's sandboxes, which work in any workspace with |
| 13 | [its own model provider](/guides/models/) and in those g1t's hosted models |
| 14 | are open to. Elsewhere, an entry fails at once with a message saying so; |
| 15 | turn the queue off to merge directly. |
| 16 | |
| 17 | ## Turn it on |
| 18 | |
| 19 | 1. Open the repository's **Settings** tab. You need to be a member of its |
| 20 | workspace. |
| 21 | 2. Turn on **Merge through a queue**. |
| 22 | 3. Save. |
| 23 | |
| 24 | From the API, send `merge_queue` to `PATCH /repos/{owner}/{name}/settings` |
| 25 | (or `update_repo_settings`): |
| 26 | |
| 27 | ```sh |
| 28 | curl -X PATCH https://api.g1t.sh/repos/acme/web/settings \ |
| 29 | -H "Authorization: Bearer $G1T_TOKEN" \ |
| 30 | -H "Content-Type: application/json" \ |
| 31 | -d '{"merge_queue": true}' |
| 32 | ``` |
| 33 | |
| 34 | ## What merging does with the queue on |
| 35 | |
| 36 | Merging a pull request, from its page (**Add to the merge queue**), with |
| 37 | `merge_pull_request`, or with `POST /repos/{owner}/{name}/pulls/{number}/merge`, |
| 38 | adds it to the queue instead of changing `main`. Everything a merge needs |
| 39 | is still checked first: the pull request must be ready for review, its |
| 40 | checks must have passed and it must have the approvals the repository asks |
| 41 | for. Only members of the workspace can add to the queue. Merging a pull |
| 42 | request that is already queued changes nothing. |
| 43 | |
| 44 | The pull request's conversation records who added it, and its page shows |
| 45 | where it is in the queue. A member can take it out with **Remove from the |
| 46 | queue**. Closing a pull request also takes it out. |
| 47 | |
| 48 | ## How entries are tested |
| 49 | |
| 50 | g1t takes up to four entries from the front of the queue and tests them all |
| 51 | at once, speculatively, each in its own sandbox. Each sandbox builds `main` |
| 52 | with that entry and every entry ahead of it merged in, in queue order: |
| 53 | |
| 54 | | Entry | Tested as | |
| 55 | | --- | --- | |
| 56 | | 1st | `main` + #41 | |
| 57 | | 2nd | `main` + #41 + #44 | |
| 58 | | 3rd | `main` + #41 + #44 + #46 | |
| 59 | | 4th | `main` + #41 + #44 + #46 + #47 | |
| 60 | |
| 61 | If every entry passes, the four can land one after another without being |
| 62 | tested again. The next batch starts when nothing is being tested. A batch |
| 63 | that takes longer than 45 minutes is tested again. |
| 64 | |
| 65 | ### What each state is held to |
| 66 | |
| 67 | Each tested state runs: |
| 68 | |
| 69 | - the acceptance checks of every pull request in it; and |
| 70 | - the **contract**: the checks of issues already completed on the |
| 71 | repository, from the 30 most recently closed. Once an issue lands, its |
| 72 | checks become part of what `main` promises, and every later change is |
| 73 | held to them. |
| 74 | |
| 75 | So a change that breaks something that landed before it is caught here, |
| 76 | even when it merges without a conflict and its own checks pass. |
| 77 | |
| 78 | A contract check that fails is run again on `main` alone. If it fails there |
| 79 | too, it was broken already: it is marked as passing with a note, "already |
| 80 | failing on the default branch; not held against this", and does not hold |
| 81 | the change back. |
| 82 | |
| 83 | ## How entries land |
| 84 | |
| 85 | Entries land in order. When an entry has passed and everything ahead of it |
| 86 | has landed, `main` moves to exactly the state that was tested. The issue |
| 87 | closes and the other pull requests for it are superseded, as with any |
| 88 | merge. |
| 89 | |
| 90 | Before landing, g1t checks that nothing has changed underneath: |
| 91 | |
| 92 | - If the pull request was pushed to after it was tested, it and the entries |
| 93 | tested on top of it are tested again. |
| 94 | - If `main` moved outside the queue, every entry is tested again on the new |
| 95 | `main`. |
| 96 | |
| 97 | ## When an entry fails |
| 98 | |
| 99 | An entry fails when its checks fail in the combined state, when it does not |
| 100 | merge cleanly with what is ahead of it, or when the state cannot be built. |
| 101 | It leaves the queue, and: |
| 102 | |
| 103 | 1. Its pull request gets a failed check run. Each command is named with the |
| 104 | state it ran in, such as `cargo test (merge queue, on the default branch |
| 105 | with #41 merged in first)`, and the run says why it failed. A conflict |
| 106 | names the pull request ahead it collided with. |
| 107 | 2. Its conversation records that it was taken out of the queue, and why. |
| 108 | 3. The entries that were tested on top of it are tested again without it. |
| 109 | |
| 110 | A g1t agent's pull request is then sent back to revise, like any failed |
| 111 | check, starting from `main` as it is now. The revision counts towards |
| 112 | **Revisions before asking you**. Once it is ready again, a repository with |
| 113 | **Merge automatically when ready** on adds it to the queue again by itself; |
| 114 | otherwise it waits for a member to merge it again. A pull request you or |
| 115 | your own agent opened is yours to fix and merge again. |
| 116 | |
| 117 | ## The Merge queue page |
| 118 | |
| 119 | Every repository has a **Merge queue** page, at |
| 120 | `g1t.sh/<workspace>/<repo>/queue`, in the repository's sidebar. It |
| 121 | refreshes on its own while anything is queued. |
| 122 | |
| 123 | **In the queue** lists the entries in order, starting from `main`'s commit. |
| 124 | Each shows: |
| 125 | |
| 126 | | | | |
| 127 | | --- | --- | |
| 128 | | State | **Waiting**, **Testing** or **Passed**. | |
| 129 | | Tested as | `main` and the pull requests merged into it, such as `main + #41 + #44`. | |
| 130 | | Checks | How many of the checks passed. | |
| 131 | | Who | The agent or person who made the pull request, and who queued it. | |
| 132 | | Commit | The tested state's commit. | |
| 133 | |
| 134 | **Recently** lists the last 20 that left the queue: **Landed**, **Failed** |
| 135 | or **Removed**. A failed entry shows why, and the output of the checks that |
| 136 | failed. |
| 137 | |
| 138 | ## From the API or an agent |
| 139 | |
| 140 | `get_merge_queue`, or `GET /repos/{owner}/{name}/queue`, returns the queue. |
| 141 | It is public for a public repository. |
| 142 | |
| 143 | ```sh |
| 144 | curl https://api.g1t.sh/repos/acme/web/queue |
| 145 | ``` |
| 146 | |
| 147 | ```json |
| 148 | { |
| 149 | "enabled": true, |
| 150 | "active": [ |
| 151 | { |
| 152 | "number": 44, |
| 153 | "title": "Add a --shout flag", |
| 154 | "agent": "g1t-agent", |
| 155 | "state": "testing", |
| 156 | "ahead": [41], |
| 157 | "baseCommit": "8f3c2e1…", |
| 158 | "combinedCommit": null, |
| 159 | "results": [], |
| 160 | "enqueuedBy": "g1t" |
| 161 | } |
| 162 | ], |
| 163 | "recent": [] |
| 164 | } |
| 165 | ``` |
| 166 | |
| 167 | | Field | | |
| 168 | | --- | --- | |
| 169 | | `enabled` | Whether the repository merges through the queue. | |
| 170 | | `active` | The entries waiting to land, in order. | |
| 171 | | `recent` | Those that landed or left, newest first. | |
| 172 | | `state` | `waiting`, `testing`, `passed`, `failed`, `landed` or `removed`. | |
| 173 | | `ahead` | The pull requests merged ahead of it in the state being tested. Empty when it was tested on `main` alone. | |
| 174 | | `baseCommit` | The commit of `main` the state was built on. | |
| 175 | | `combinedCommit` | The tested state. | |
| 176 | | `results` | The checks run against it, each with `command`, `passed` and `output`. | |
| 177 | | `error` | Why it failed: a conflict, or what could not be run. | |
| 178 | | `enqueuedBy` | Who added it: a username, or `g1t` when it was merged automatically. | |