> For the complete documentation index, see [llms.txt](https://docs.aviator.co/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.aviator.co/mergequeue/how-to-guides/merging-stacked-prs.md).

# How to Merge Stacked PRs

Aviator offers an [<mark style="color:blue;">open-source CLI</mark>](https://github.com/aviator-co/av) for managing stacked PRs within GitHub. When used alongside Aviator MergeQueue, it simplifies the process of validating and merging those stacked PRs (or subset of those stacked PRs) together.

When requested to merge stacked PRs, Aviator will validate all the stacked PRs as if they were a single PR, and then merge them together after validation. The merge behavior may be slightly different depending on the [<mark style="color:blue;">queue mode</mark>](/mergequeue/concepts/queue-modes.md).

## Queueing action

To merge a subset of the stack, request a merge for the topmost PR in that subset. So for instance, if your stack is:

```
master <- PR#1 <- PR#2 <- PR#3 <- PR#4
```

Requesting a stack-merge action for `PR#3` will validate `PR#1`, `PR#2` and `PR#3` leaving `PR#4` untouched. After merging, the stack would look like:

```
master <- PR#4
```

There are a few ways to request queueing a stack of PRs.

* **Chrome Extension (recommended)**: This is the simplest way to request way since the user experience for this is very similar to merging a regular PR. Go to `PR#3` from above example in GitHub and click "Queue pull request" button:

<figure><img src="/files/3T4Ew712ci3T5sfAUiCk" alt=""><figcaption><p>Queue pull request action for stacked PRs</p></figcaption></figure>

* **GitHub Label:** You can also queue the stack the same way using GitHub label like you would a single PR. To do so, just apply the label at the top PR that would want to merge with the stack. In the example above, apply the label on `PR#3` .
* **CLI**: Another way to queue the PR is via the `av` command line. Simply checkout the branch associated with `PR#3` and run the command:

```
av pr queue
```

Note that, using this command requires [<mark style="color:blue;">authenticating the CLI with your Aviator account</mark>](https://docs.aviator.co/aviator-cli#setting-up-aviator-mergequeue).

* **Slash command**: A stack or sub-stack can also be merged by commenting a [slash command](/flexreview/reference/flexreview-slash-commands.md) from the GitHub interface:

```
/aviator merge
```

## Merge behavior

Depending on the queue mode being used, the merge behavior for stacked PRs can vary.

### Sequential mode (default mode)

Taking the example above, in sequential mode:

* Aviator will consider `PR#1`, `PR#2` and `PR#3` as a single PR in the queue.
* When this PR reaches the top of the queue, Aviator only updates `PR#3` with the target branch (mainline), and run the CI.
* Once the CI passes, Aviator will change the base branch of `PR#3` and merge it
* Since `PR#3` is stacked on top of `PR#1` and `PR#2`, merging PR#3 also merges all the changes associated with `PR#1` and `PR#2`.
* Since GitHub does not recognize that `PR#1` and `PR#2` are already merged, Aviator automatically closes them and applies the GitHub label `merged-by-mq` to represent that the PRs are indeed merged.

See also [<mark style="color:blue;">Separating the merge commits</mark>](#separating-the-merge-commits).

### Parallel mode

Similar to Sequential mode, parallel mode also considers the stack as a single PR in the queue. In parallel mode:

* Aviator creates a batch using the head branch of `PR#3` including all the changes of `PR#1` and `PR#2` as well.
* After the batch passes CI, Aviator changes the base branch of `PR#3` and merges it.
* Since `PR#3` is stacked on top of `PR#1` and `PR#2`, merging PR#3 also merges all the changes associated with `PR#1` and `PR#2`.
* As in Sequential mode, Aviator closes `PR#1` and `PR#2`, while applying the GitHub label `merged-by-mq` to represent that the PRs are indeed merged.

See also [<mark style="color:blue;">Separating the merge commits</mark>](#separating-the-merge-commits)<mark style="color:blue;">.</mark>

### Fast forwarding

[<mark style="color:blue;">Fast forwarding mode</mark>](/mergequeue/concepts/fast-forwarding.md) provides a simpler experience for merging stacked PRs. Since in case of fast-forwarding, we fast-forward the mainline to commits in draft PRs, the CI is already passing. This allows Aviator to merge the PRs individually without GitHub blocking the commits.

In fast-forwarding mode:

* When the stack with `PR#3` is requested merging, Aviator creates one squash commit for each PR in the stack.
* After CI passes, the mainline is fast-forwarded to the latest commit in the batch associated with `PR#3`, this includes separate commits associated with `PR#2` and `PR#1` in the linear history.

## Separating the merge commits

By default the Sequential and Parallel modes create a single commit in the mainline for all of the stacked PRs that are queued together. This is because GitHub does not let an app account bypass the branch protection rules. Even when allowed to bypass the rules, GitHub will let the Aviator app only bypass the approval requirement and PR creation requirement, not the CI validation requirement.

### Rulesets

This capability of bypassing the CI requirement is now available using GitHub’s [Rulesets](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/managing-rulesets-for-a-repository). Rulesets offer feature parity with the configurations you use in classic branch protection rules and you should be able to migrate all your existing configurations to using Rulesets.

Once migrated to Ruleset, you can add Aviator app in the bypass list.

<figure><img src="/files/2piE6Otb67FWpOG8U1vS" alt=""><figcaption><p>Add Aviator GitHub app to Bypass list</p></figcaption></figure>

### **Configuring separate commits**

Once Rulesets are configured and other GitHub branch protection rules are disabled, you can configure Aviator to start creating separate commits for stacked PRs. To do so, set the following in the `merge_strategy` section of the configuration YAML.

```
merge_strategy:
  use_separate_commits_for_stack: true
```

For details, refer to the [<mark style="color:blue;">configuration reference</mark>](https://docs.aviator.co/mergequeue/reference/complete-reference-guide#merge-strategy).

If you need assistance in setting up the Rulesets, please contact <howto@aviator.co>.

### Setting original PRs as merged \[Beta]

When Aviator merges stack PRs, by default the following occurs:

* PRs are marked as closed with the label `merged-by-mq`
* PRs do **not** appear as "merged" in the GitHub UI
* The commits are squashed into the main branch using a separate strategy to prevent merge conflicts
* GitHub cannot detect that these PRs were actually merged due to the squashing strategy

To workaround this behavior, Aviator has introduced a new mode. In this mode Aviator will force update each PR with the corresponding squash commit while merging the PR. That way, when mainline is forwarded to this commit SHA, GitHub recognizes the PR as merged.

To enable this config, set `update_pr_commits_before_stack_merge` to true in `merge_strategy`. Note that this config also requires `use_separate_commits_for_stack` to be enabled.

```
merge_strategy:
  use_separate_commits_for_stack: true
  update_pr_commits_before_stack_merge: true
```

### Detecting stacked PRs without av CLI

If you are creating stacks manually or using a thirdparty tool, you can enable auto-detection of stacked PRs using the config:

```
merge_rules:
  auto_detect_stacks: true
```

Note that, auto-detection can have some side effects where Aviator might detect PRs in a stack even when they were not intentionally created as stacked. For instance, if you have a PR from a `release`branch to `main` to backport some changes, meanwhile have a PR from a `featureA`branch to `release`branch, the auto-detect will detect this as a stack. To avoid this, you can add `release`branch as one of the base branches in the config.

## Using GitHub's Stacked PRs

GitHub has its own [stacked pull requests](https://github.github.com/gh-stack/) feature (currently in preview), managed with the `gh stack` commands in the GitHub CLI — `gh stack submit` to create or update the PRs and link them as a stack, and `gh stack sync` to rebase and re-link the whole stack. See GitHub's [Working with Stacked PRs](https://github.github.com/gh-stack/guides/stacked-prs/) guide for the full workflow.

Aviator supports these stacks natively, with no configuration required. Aviator reads the stack information GitHub tracks on each PR, so the stack and its ordering are recognized automatically — the `auto_detect_stacks` and `use_separate_commits_for_stack` settings described above are not needed for GitHub-managed stacks.

Queue the stack the same way as any other — request the merge on the topmost PR of the subset you want to land, using any of the methods in [Queueing action](#queueing-action).

<figure><img src="/files/UPRNLZSm9pGSwbCObA2K" alt=""><figcaption><p>Queueing a GitHub-managed stack from the pull request page</p></figcaption></figure>

When the stack reaches the front of the queue, Aviator merges it through GitHub itself. Every PR in the stack shows as **Merged** in GitHub (not closed), and GitHub's stack state stays consistent with what landed. The [Setting original PRs as merged](#setting-original-prs-as-merged-beta) workaround exists for exactly this purpose on Aviator-managed stacks; GitHub-managed stacks do not need it, so `update_pr_commits_before_stack_merge` has no effect on them and can be left disabled.

### Remove the "Restrict updates" rule

A branch protected by the **Restrict updates** ruleset rule cannot accept a stacked-PR merge — not from Aviator, and not from a person clicking Merge in GitHub's own web interface. This includes users and apps listed as bypass actors on the ruleset.

The rule means that only actors with bypass permission may update the branch. GitHub honors that bypass for an ordinary pull request merge, which is why regular merges keep working. It does not honor it when merging a stack, so the merge is refused with:

```
Cannot update this protected ref.
```

The message names no rule, so this is easy to mistake for a missing status check or approval. The symptoms are every pull request in the stack reporting as **not ready** in GitHub with that message on the bottom PR, or Aviator blocking the stack with `GitHub could not merge this stack: Cannot update this protected ref.`

To use GitHub's stacked pull requests, remove **Restrict updates** from the ruleset that targets your mainline.

#### Preventing merges that skip the queue

**Restrict updates** is often enabled to stop developers merging directly and bypassing MergeQueue. A required status check does that job without breaking stacks.

Add `aviator/checks` to the **Require status checks to pass** rule in the same ruleset.

<figure><img src="/files/oFPUEqtfFYmFRqHHGWKQ" alt="Branch rules with Restrict updates unchecked and aviator/checks listed as a required status check"><figcaption><p>Restrict updates turned off, with <code>aviator/checks</code> required instead</p></figcaption></figure>

Aviator publishes this check on every pull request and only reports it as successful at the point the PR is merged through the queue. Until then it stays pending, so GitHub blocks a manual merge while Aviator itself can still merge. Leave `publish_status_check` at its default of `ready` — setting it to `never` disables the check this depends on.

Direct pushes to the branch are still blocked by **Require a pull request before merging**, which is unaffected by removing **Restrict updates**.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.aviator.co/mergequeue/how-to-guides/merging-stacked-prs.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
