|
| 1 | +--- |
| 2 | +title: Merge API |
| 3 | +description: Reference for the asynchronous merge API — the required method for merging stacked pull requests. |
| 4 | +--- |
| 5 | + |
| 6 | +Stacked pull requests are merged through a new **asynchronous merge API**. Because a stack merge can involve several pull requests that may take up to a few minutes to merge, the merge runs in the background: you submit a merge request and then poll for its result. |
| 7 | + |
| 8 | +This is the **required method for merging stacked PRs**. A stack cannot be merged with the legacy synchronous [merge endpoints](https://docs.github.com/rest/pulls/pulls#merge-a-pull-request) or [mutations](https://docs.github.com/en/graphql/reference/pulls#mutation-mergepullrequest). When you merge a stacked pull request, every pull request in the stack up to and including the one you request is merged into the base branch. |
| 9 | + |
| 10 | +:::caution[Private Preview] |
| 11 | +Stacked PRs is currently in private preview. These endpoints are only available for repositories where the feature is enabled. [Sign up for the waitlist →](https://gh.io/stacksbeta) |
| 12 | +::: |
| 13 | + |
| 14 | +## How it works |
| 15 | + |
| 16 | +Merging is a two-step flow: |
| 17 | + |
| 18 | +1. **Submit** a merge request with `PUT .../merge-async`. The response contains a `uuid` identifying the request. |
| 19 | +2. **Poll** for the result with `GET .../merge-async/{uuid}` until the `status` is no longer `pending`. |
| 20 | + |
| 21 | +Only basic pull request state is checked when you submit (the PR must be open and not a draft). Branch protection and repository rules are evaluated later, when the merge actually runs, and a rule failure is reported as a `failed` result while polling. A stack merge request is **atomic**: either the whole group of pull requests lands (or is added to the merge queue), or none of it does. |
| 22 | + |
| 23 | +## Submit a merge request |
| 24 | + |
| 25 | +``` |
| 26 | +PUT /repos/{owner}/{repo}/pulls/{pull_number}/merge-async |
| 27 | +``` |
| 28 | + |
| 29 | +Merges the pull request (and, for a stacked PR, everything below it in the stack) into the base branch in the background. Returns a `uuid` used to fetch the result. |
| 30 | + |
| 31 | +All body fields are optional. |
| 32 | + |
| 33 | +| Body field | Type | Description | |
| 34 | +|------------|------|-------------| |
| 35 | +| `merge_method` | `string` | The merge method: `merge`, `squash`, or `rebase`. Defaults to a merge commit. | |
| 36 | +| `merge_action` | `string` | How to merge: `default` (recommended), `direct_merge`, or `merge_queue`. `default` picks the most appropriate option — it merges directly, or adds the stack to the base branch's merge queue when the branch requires one. `direct_merge` forces a direct merge; `merge_queue` forces the merge queue, if available. | |
| 37 | +| `commit_title` | `string` | Title for the automatic commit message. Not supported on `merge_queue` merge actions. | |
| 38 | +| `commit_message` | `string` | Extra detail to append to the automatic commit message. Not supported on `merge_queue` merge actions. | |
| 39 | +| `sha` | `string` | SHA that the pull request head must match to allow the merge. If the PR head does not match the provided SHA, the merge is cancelled. | |
| 40 | + |
| 41 | +```sh |
| 42 | +echo '{"merge_method": "squash", "merge_action": "default"}' | \ |
| 43 | + gh api --method PUT repos/OWNER/REPO/pulls/102/merge-async --input - |
| 44 | +``` |
| 45 | + |
| 46 | +### Responses |
| 47 | + |
| 48 | +| Status | When | Body `status` | |
| 49 | +|--------|------|---------------| |
| 50 | +| `202 Accepted` | The merge request was accepted and will run in the background. | `pending` | |
| 51 | +| `200 OK` | The pull request was already merged. | `merged` | |
| 52 | +| `409 Conflict` | A merge request already exists for this pull request. The existing request's `uuid` is returned — its options may differ from those you requested. | `pending` | |
| 53 | +| `400 Bad Request` | The pull request is not ready to be merged (for example, it is closed or a draft). | `failed` | |
| 54 | +| `404 Not Found` | Async merge is not available for this repository, or the pull request was not found. | — | |
| 55 | + |
| 56 | +```json |
| 57 | +// 202 Accepted |
| 58 | +{ |
| 59 | + "status": "pending", |
| 60 | + "details": { |
| 61 | + "message": "Merge request enqueued.", |
| 62 | + "uuid": "630b9d5e-3f2a-4f7e-8b0c-2d5f9a8c1e42", |
| 63 | + "merge_method": "squash", |
| 64 | + "merge_action": "default", |
| 65 | + "expected_head_sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e" |
| 66 | + } |
| 67 | +} |
| 68 | +``` |
| 69 | + |
| 70 | +## Get the result of a merge |
| 71 | + |
| 72 | +``` |
| 73 | +GET /repos/{owner}/{repo}/pulls/{pull_number}/merge-async/{uuid} |
| 74 | +``` |
| 75 | + |
| 76 | +Fetches the current result of a merge request, identified by the `uuid` returned when the merge was submitted. A valid lookup always returns `200 OK`. Read the `status` field to see where the merge stands. Poll this endpoint (e.g., once a second) until the `status` is no longer `pending`. |
| 77 | + |
| 78 | +The result is retained for **24 hours** after its most recent update. After that window the request expires and this endpoint returns `404 Not Found` for the UUID. |
| 79 | + |
| 80 | +```sh |
| 81 | +gh api repos/OWNER/REPO/pulls/102/merge-async/630b9d5e-3f2a-4f7e-8b0c-2d5f9a8c1e42 |
| 82 | +``` |
| 83 | + |
| 84 | +```json |
| 85 | +// still running |
| 86 | +{ |
| 87 | + "status": "pending", |
| 88 | + "details": { |
| 89 | + "message": "Merge request is in progress.", |
| 90 | + "uuid": "630b9d5e-3f2a-4f7e-8b0c-2d5f9a8c1e42", |
| 91 | + "merge_method": "squash", |
| 92 | + "merge_action": "default", |
| 93 | + "expected_head_sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e" |
| 94 | + } |
| 95 | +} |
| 96 | + |
| 97 | +// merged directly |
| 98 | +{ |
| 99 | + "status": "merged", |
| 100 | + "details": { |
| 101 | + "message": "Pull request was merged.", |
| 102 | + "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e" |
| 103 | + } |
| 104 | +} |
| 105 | + |
| 106 | +// added to the merge queue |
| 107 | +{ |
| 108 | + "status": "enqueued", |
| 109 | + "details": { |
| 110 | + "message": "Pull request was added to the merge queue." |
| 111 | + } |
| 112 | +} |
| 113 | + |
| 114 | +// could not be merged |
| 115 | +{ |
| 116 | + "status": "failed", |
| 117 | + "details": { |
| 118 | + "message": "Merge conflict: the pull request could not be merged." |
| 119 | + } |
| 120 | +} |
| 121 | +``` |
| 122 | + |
| 123 | +## The result object |
| 124 | + |
| 125 | +Both endpoints return the same object: a `status` enum and a `details` object. |
| 126 | + |
| 127 | +| Field | Type | Description | |
| 128 | +|-------|------|-------------| |
| 129 | +| `status` | `string` | The state of the merge: `pending`, `merged`, `enqueued`, or `failed`. | |
| 130 | +| `details` | `object` | Details for the current state (see below). | |
| 131 | + |
| 132 | +### Status values |
| 133 | + |
| 134 | +| Status | Meaning | |
| 135 | +|--------|---------| |
| 136 | +| `pending` | The merge is running in the background. Keep polling. | |
| 137 | +| `merged` | The stack was merged directly. `details.sha` is the resulting merge commit. | |
| 138 | +| `enqueued` | The stack was added to the base branch's merge queue. It will merge once the queue processes it. This is a terminal state for the merge request — track the merge queue for the final outcome. | |
| 139 | +| `failed` | The merge was attempted but could not complete (for example, a merge conflict or an unmet branch rule). `details.message` explains why. Because the merge is atomic, nothing was merged. | |
| 140 | + |
| 141 | +### Details fields |
| 142 | + |
| 143 | +The fields present in `details` depend on the state: |
| 144 | + |
| 145 | +| Field | Type | Present when | Description | |
| 146 | +|-------|------|--------------|-------------| |
| 147 | +| `message` | `string` | always | A human-readable description of the current state. | |
| 148 | +| `uuid` | `string` | `pending` | The identifier of the merge request, used to poll for the result. | |
| 149 | +| `merge_method` | `string` | `pending` | The merge method being used (`merge`, `squash`, or `rebase`). | |
| 150 | +| `merge_action` | `string` | `pending` | The resolved action (`default`, `direct_merge`, or `merge_queue`). | |
| 151 | +| `expected_head_sha` | `string` | `pending` | The SHA the pull request head must match for the merge to proceed. | |
| 152 | +| `sha` | `string` | `merged` | The resulting merge commit SHA. | |
| 153 | + |
| 154 | +## Limitations |
| 155 | + |
| 156 | +- **Bypassing merge requirements is not supported.** You cannot use admin privileges to bypass a stack's branch protection rules or rulesets; every pull request in the stack must satisfy its requirements before the stack can land. |
| 157 | +- **Auto-merge is not supported.** A stacked pull request cannot be set to merge automatically once its requirements are met. |
0 commit comments