Github Merge Queue
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_progress
jobs. 😟
To stop in_progress
jobs, 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
orqueued
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