Github Merge Queue

Pierre RAFFA
5 min readJul 17, 2023

A merge queue helps increase velocity by automating pull request merges into a busy branch and ensuring the branch is never broken by incompatible changes.

The merge queue provides the same benefits as the Require branches to be up to date before merging branch protection, but does not require a pull request author to update their pull request branch and wait for status checks to finish before trying to merge.

Using a merge queue is particularly useful on branches that have a relatively high number of pull requests merging each day from many different users.

How does it work ?

Each queue entry creates a temporary branch that contains:

  • the latest changes from the base branch
  • the changes from other pull requests already in the queue
  • the changes from your pull request.

CI then starts, with the expectation that all required status checks must pass before the branch (and the pull requests it represents) are merged.

But there are few scenarios to keep in mind!

➡️ Scenario1:

Multiple entries in the queue but first entry fails.

Result:

This queue entry will be evicted.

As each entry depends on its previous one, all entries after the failed one will need to create another new temporary branch and all jobs will need to be re-run.

This process is automatic, but Github won’t stop in_progressjobs. 😟
To stop in_progressjobs, see the last section of this article.

Step 1:
Dev1 creates a PR branch d1.
pull_request event is triggered and tests pass successfully.

Step:
Dev2 creates a PR branch d2.
pull_request event is triggered and tests pass successfully.

Step 3/4:

Dev1 decides to add their PR branch d1 to the queue.
Dev2 decides to add their PR branch d2 to the queue, few minutes later.
merge_group event is triggered for d1 , then for d2 few minutes later.
Both jobs for d1 and d2 run concurrently.
At some point, jobs for d1 fails. As d2 contains d1 code changes, jobs for d2 will fail later.

Step5:
Github evicts d1 from the queue.
At this moment, d2 is still running and will fail few minutes later.
The jobs for d2 get relaunched but without d1 code changes this time.
The jobs for d2 passes.

Step6:
d2 is merged into main
push event is triggered and succeeds.

➡️ Scenario2

Pushing changes on a PR already queued

Dev adds their PR to the queue.
A new entry appears in the queue.
The merge_group webhook triggers all required jobs.
In the meantime, the dev decides to push some changes in their PR

Result:

The feature branch is temporarily protected until the merge queue accepts or rejects it.

Error message on git push :

remote: error: GH006: Protected branch update failed for refs/heads/merge-group-sha-2.
remote: error: A pull request for this branch has been added to a merge queue. Branches that
remote: are queued for merging cannot be updated. To modify this branch, dequeue the
remote: associated pull request.
remote:
To github.com:valstro/omskit.git
! [remote rejected] HEAD -> merge-group-sha-2 (protected branch hook declined)
error: failed to push some refs to 'git@github.com:valstro/omskit.git'

➡️ Scenario3

PR with merge conflicts

Dev adds their PR to the queue.
A new entry appears in the queue

Github creates the temporary branch but this ends up with conflicts.
The status of this queue entry will be UNMERGEABLE

Result:

Github will send an email to the author of the PR.
The entry for p2 will still be present in the list until p1 jobs are completed, successfully or not.

Scenario 3a:
The previous queue entries succeed, and the PR is merged to
main .

p1 has been merged to main
Then the PR for p2 has now conflicts with main .
The PR will go back to open state and should show some conflicts.

⇒ Scenario 3b:
The previous queue entries fails.

Then the PR for p2 might not show any conflicts with main Github relaunches the jobs for p2 (without p1 code changes).

➡️ Scenario4

Merge Queue entry fails before reaching all Required Checks.

Result:

The entry stays in the queue until it reaches timeout (Default: 60mins)
This requires a manual action to remove the entry from the queue to unblock other entries.

⇒ Manual action:

Stop in_progress merge_group jobs

This GitHub action is responsible to cancel any previous runs for the current workflow.
It supports 3 events ( pull_request , push , merge_group )

The cancellation of the previous runs could be required when:

  • the merge queue has entries running concurrently
  • the first entry fails and all the next ones will have to run again
    This result in multiple runs executed for the same queued PR.

For merge_group event, the cancellation is performed for previous runs:

  • which are in_progress or queued state
  • from the same workflow
  • related the same PR
  • older than the current run

Usage

Place this job at the beginning of your workflow.
Once the job running, it will check for all runs related to the same context than the current run and cancel all of them.

jobs:
cancel-previous-runs:
runs-on: ubuntu-latest
steps:
- name: Cancel Previous Runs
uses: pierreraffa/cancel-previous-runs-action@1.4

--

--