If you’re working in a busy GitHub repository with many contributors and frequent pull requests, you’ve likely encountered challenges when it comes time to merge your changes into the main branch. For instance, you may have dealt with outdated pull requests, rebasing or merge conflicts, incompatible changes, or broken builds. These issues can slow down the development process and reduce the quality of your code.
Fortunately, merge queues offer a way to tame the complexity of busy repositories. Essentially, a merge queue enables you to line up pull requests for merging and off-loads the merge work to a bot or app, ensuring each PR is compatible with the target branch and passes all required status checks before it’s automatically merged.
In this article, you’ll learn all about why you need a merge queue and how to manage one.
Why you need a merge queue
A merge queue is a structured way of managing the merging workflow for your GitHub repository. It works by using a label on each pull request to add pull requests to the queue. You can choose any label name that you want, such as “ready-to-merge” or “merge-queue”. The app or bot you use to manage the merge queue keeps track of how many pull requests are in the queue and can trigger builds for every pull request or batch—either when the queue reaches a certain size or a certain time interval has passed since the last build and merge.
Then the bot creates a temporary branch that contains the latest changes from the target branch and merges in the changes from the pull request(s), running and verifying the required status checks along the way.
A merge queue can help you avoid some common problems that arise when working with multiple pull requests in a busy repository, such as the following:
Outdated pull requests
When many pull requests are open at the same time, some may become outdated as new changes are merged into the target branch. This means that they have to be updated with the latest changes before they can be merged, which can be time-consuming and error-prone.
A merge queue bot typically updates the pull requests automatically and runs the required status checks on them before merging them.
Rebasing or merging conflicts
When updating pull requests with the latest changes from the target branch, there may be conflicts that need to be resolved manually. This can be tedious and frustrating, especially if it has to be done repeatedly for multiple pull requests.
With a merge queue, you usually don’t have to deal with conflicts yourself. The app or bot often resolves them for you using its own algorithm and notifies you if there are any issues that need your attention.
Even if a pull request passes all the tests on its own branch, it may still break the build after it’s merged into the target branch. This can happen if there are incompatible changes made by other contributors that affect the code in the pull request.
With a merge queue, you don’t have to worry about incompatible changes breaking your code. The bot tests your code against the latest version of the target branch and any other pull requests in the queue before merging it.
If your repository has continuous integration (CI) workflows that run tests and checks on every push or merge, a broken build can block the entire development process until it’s fixed. With a merge queue, you don’t have to worry about breaking the build with your merge. The bot ensures that each pull request in the queue passes all the required status checks and doesn’t break the build before merging it.
Managing a Merge Queue
A merge queue is a powerful way to automate and streamline your merge workflow. However, to use it effectively, you need to set up the right CI triggers and decide on other factors that affect how the queue operates. This section will explain how to configure these settings and why they are important.
Set up the right CI triggers
One of the requirements for using a merge queue is a CI configuration that can trigger and report on the status of the temporary branch that the queue creates. The temporary branch contains the latest changes from the target branch, the changes from other pull requests already in the queue, and the changes from the current pull request. The CI configuration needs to run and verify the required status checks on this temporary branch before merging it.
Depending on your CI provider, you may need to update your CI configuration to handle this scenario. For example, if you use [GitHub Actions](https://github.com/features/actions), you need to add a webhook event as an additional trigger for your workflows. Otherwise, status checks are not triggered when you add a pull request to the queue, and the merge fails since the required status check is not reported.
Another setting that you need to choose is the merge method. This determines how the queue merges pull requests into the target branch. You can select one of the following options:
Merge is the default method, and it combines the commits from the pull request branch into the target branch. The complete history of changes made to the pull request branch remains intact. This can be valuable if you want to preserve a detailed log of changes for auditing or debugging. However, the downside is that it can make your commit history more complicated and harder to follow.
Rebase adds the commits from the pull request branch to the head of the target branch. While it provides a clean, linear project history, rebasing comes with its own drawbacks. Rebasing rewrites commit history, which can complicate things if those commits have been pushed or shared in other branches. This is usually not a problem for short-lived feature branches but can be disruptive in long-running development efforts where branches are frequently shared.
Squash condenses all the commits from the pull request into a single new commit on the target branch. This creates a clean history and simplifies code reviews by presenting all
changes as a single unit. However, you lose the granular history that could be useful for diagnosing issues later on.
By carefully configuring your CI triggers and selecting the most appropriate merge method, you set the foundation for a successful, efficient merge queue operation.
Deciding on other factors
Once your CI triggers are in place and the merge method is selected, several other options are pivotal in tailoring the behavior of your merge queue to your specific needs. These settings help you fine-tune the merge queue’s operation, striking the right balance between efficiency and reliability.
In a busy repository, you may want to run multiple CI builds in parallel so you can quickly merge pull requests from the merge queue into the release or target branch. [Good merge queue tools](https://www.aviator.co/aviator-github-mergequeue) give you the ability to set up parallel builds that batch additional PRs together as they enter the queue and fall back to sequential merging when a build fails.
If you’re operating a very busy GitHub repository and have high-capacity CI pipelines, a merge queue that allows multiple concurrent builds ensures pull requests are merged quickly.
It’s also important to decide how your merge queue should react to issues like failed status checks or merge conflicts.
The normal way to handle a failure in a merge queue is to remove the failing pull request, leaving the rest of the queue intact. This option makes sense when the changes in your pull requests are largely independent of each other, especially if the failing pull request introduces a low-priority fix or feature.
Sometimes, though, a failing pull request means other related PRs are likely to fail, too. Good merge queue tooling typically offers a way to mark sets of pull requests as related, so a failure of one pull request in the group can remove the entire group from the queue.
Timeout settings dictate how long the merge queue waits for the build and test process to complete before marking a merge as failed. If your CI system typically takes a while to run checks or you have periods where CI runs bog down under heavy load, consider adjusting your
timeout accordingly. It’s important to find the right balance so you aren’t failing merges unnecessarily but also aren’t allowing stuck builds to keep running too long.
Lastly, many merge queue tools let you merge multiple pull requests together in a batch. This limit allows you to balance the number and complexity of changes being merged with your available CI resources.
For instance, if your changes are generally small and easily verifiable, a higher limit would speed up the merging process. Alternatively, if your PRs typically contain larger, more intricate changes, a lower limit helps reduce build times.
Each of these factors lets you optimize the merge queue to better align with your development practices and resource constraints. You’ll likely need to experiment and adjust as you go to optimize your merge queue workflow.
Here’s a look at how a merge queue works at the architectural level:
Merge queues with Aviator MergeQueue
Aviator MergeQueue is a sophisticated merge queue management tool that helps you automate and optimize your GitHub workflow using many of the features you’ve just learned about. In addition to basic merge queue functionality, it lets you run parallel builds, batch changes into ChangeSets, and schedule priority merges.
Use MergeQueue to quickly demonstrate how a merge queue works. If you want to follow along with your own repository, sign up for an account, [follow the instructions to connect Aviator to your GitHub account and authorize it to access at least one of your repositories. For now, stick with the default settings Aviator provides for your repository.
You’ll see the authorized repositories show up in your Aviator dashboard once you authorize access. In one of these repositories, create a branch, push some changes to it, and then create a pull request.
You’ll see Aviator’s bot comment on the pull request:
When your pull request is ready to be merged, apply the mergequeue label. Note that Aviator has added it to your repository, so it appears in the drop-down list:
Once you’ve added the label, head over to the Queue page on your Aviator dashboard. Notice your queued pull request is now in the queue:
Click on the pull request for more details:
If your tests are quick to run, it won’t take long for Aviator to run all required checks. When it does, head back to your pull request on GitHub and notice that Aviator has run its checks and then merged and closed the pull request:
And that’s it! It only took a few minutes to set up and use an Aviator MergeQueue!
While setting up a merge queue involves several considerations—ranging from CI triggers and merge methods to fine-grained controls like concurrency and failure actions—the process isn’t as daunting as it might initially seem. As demonstrated with Aviator MergeQueue, getting your system up and running can be surprisingly straightforward.
The payoff for this initial investment is immediate and tangible: less time spent resolving merge conflicts, fewer broken builds, and more time for your team to focus on core development tasks. You gain not just an automated merging process but a robust framework for integrating changes efficiently, mitigating issues before they escalate, and ultimately accelerating the development cycle. This leaves your team free to focus on what you do best: crafting high-quality, reliable software.
Aviator: Automate your cumbersome processes
Aviator automates tedious developer workflows by managing git Pull Requests (PRs) and continuous integration test (CI) runs to help your team avoid broken builds, streamline cumbersome merge processes, manage cross-PR dependencies, and handle flaky tests while maintaining their security compliance.
There are 4 key components to Aviator:
- MergeQueue – an automated queue that manages the merging workflow for your GitHub repository to help protect important branches from broken builds. The Aviator bot uses GitHub Labels to identify Pull Requests (PRs) that are ready to be merged, validates CI checks, processes semantic conflicts, and merges the PRs automatically.
- ChangeSets – workflows to synchronize validating and merging multiple PRs within the same repository or multiple repositories. Useful when your team often sees groups of related PRs that need to be merged together, or otherwise treated as a single broader unit of change.
- TestDeck – a tool to automatically detect, take action on, and process results from flaky tests in your CI infrastructure.
- Stacked PRs CLI – a command line tool that helps developers manage cross-PR dependencies. This tool also automates syncing and merging of stacked PRs. Useful when your team wants to promote a culture of smaller, incremental PRs instead of large changes, or when your workflows involve keeping multiple, dependent PRs in sync.