GitHub の merge queue を試してみて分かったこと

Seiya Kokushi
9 min readFeb 9, 2023

--

Photo by Ryutaro Uozumi on Unsplash

モチベーション

2023/02/09 に GitHub の Pull request merge queue が public beta ステータスでリリースされた。
うまく動かないところや、ドキュメントからは読み取れない動きがあったのでまとめる。

public beta リリース時点での検証のため、今後の修正や仕様変更で動作が変わる可能性が大いにある

概要

  • branch protetion のルールを守りながら、更新頻度の高いブランチ (main ブランチなど) へのマージを、安全に行えるもの。
  • branch protection が設定された branch に対して merge queue を有効化できる。
  • branch protection のルールを全て満たした PR のみ merge queue に追加することができる。
  • merge queue 上でのステータスチェックやコンフリクトによってマージできない場合は、merge queue から取り除かれる。merge queue から取り除かれた理由は、各 PR に表示される。

何が嬉しいか

先述の特徴により、PR マージ時にベースブランチのアップデートを気に掛けることなく、merge queue に詰めることができる。
ステータスチェックを通れば、ベースブランチにマージされ、コンフリクトやステータスチェックでコケた場合は、マージされない。これにより、ベースブランチが壊れる可能性を大きく抑えることができる

日々の開発で 1 つのブランチに多くのマージが同時多発的に発生するようなリポジトリに向いている。

有効にするには

merge queue を有効にしたいブランチに branch protection を設定し、branch protection rule 内の Require merge queue にチェックを入れる。

設定時に気にしたいポイント

merge queue の振る舞いは、merge queue 自体の設定と、branch protection rule が関わってくる。

merge queue 追加時のイベント

公式ドキュメントだと merge_group: を workflow のトリガーに設定すると、merge queue 追加時に job を実行できそうだが、これがうまく動かなかった。merge_group: イベントのドキュメントを確認し、 types: の有無や他のトリガーとの組み合わせも試したが、動かなかった。

公式の Discussions にも報告されているが、特定ブランチに対する push で動いた。merge queue は、一時的なブランチ (gh-readonly-queue/{base_branch}/pr-{pr_num}-{base_branch_hash}) を queue ごとに作成するため、 下記のように設定する。

on:
push:
branches:
- gh-readonly-queue/main/*

なお、このトリガーによる workflow の変更は、merge queue 上のその変更を含むコミット以降の workflow で反映される。

追記

merge_group: が正しく動かない問題は解消された模様。

merge queue 追加時に GitHub Action を動かす

merge queue 内で GitHub Action を動かすには Require status checks to pass before merging で動かしたい job を指定しておく必要がある。

これを指定しておかないと、merge queue に PR の変更が詰められても、ステータスチェックを待たずにベースブランチにマージされてしまう。

さらに、ここに指定する job の workflow は、先述の merge queue 追加時のイベントがないと、merge queue 追加時に job がトリガーされない。そのため、ステータスチェックが終わらずベースブランチにマージできないという現象が発生する。

name: PR では動くが merge queue 追加時に動かないやつ

on:
pull_request:

# merge_group:
# が必要

job:
build:
steps:

Merge queue に詰められた後のコミット順

merge queue は、queue ごとに一時的なブランチを作成すると説明したが、そのブランチが含むコミットの順番は Merge method の設定によって変わる。

次を例に説明する。

main ブランチから A ブランチ、 B ブランチを切る。

時系列順に、

1. A ブランチにコミット (コミット a1)
2. B ブランチにコミット (コミット b1)
3. A ブランチにコミット (コミット a2)
4. B ブランチにコミット (コミット b2)

を作成し、

5. B ブランチの PR をマージ
6. A ブランチの PR をマージ

という順で merge queue に詰める。

Merge commit の場合

この設定では、コミットの時系列に沿って merge queue のコミットが並ぶ。

つまり、a1 → b1 → a2 → b2 → b3 (B ブランチのマージコミット) → a3 (A ブランチのマージコミット) と完全にコミットの時系列順になる。

そして、merge queue 上でのステータスチェックはマージコミットに対して行われる。

Merge commit

Squash and merge の場合

この設定では、merge queue に追加された順で、squash されたコミットが merge queue のコミットが並ぶ。

つまり、b3 (b1 + b2 の squash) → a3 (a1 + a2 の squash) という順になる。

merge queue 上でのステータスチェックは、squash されたコミットに対して行われる。

Squash and merge

Rebase and merge の場合

この設定では、merge queue に追加された順で、前の queue の最後のコミットに rebase される。前の queue がない場合は merge queue のベースブランチの最後のコミットに rebase される。

つまり、b1’ (b1 の main への rebase コミット) → b2’ → a1’ (a1 の b1’ への rebase コミット) → a2’ という順になる。

そして、merge queue 上でのステータスチェックは、rebase された各 PR の最後のコミットに対して行われる。

Rebase and merge の場合

merge queue に詰められた後にコンフリクトが発生した場合

merge queue に詰められた後にコンフリクトが発生するケースが起こり得る。この場合、コンフリクトが発生した queue に対するステータスチェックは走らない。そのため、無駄に workflow が走ることはない。

merge queue に詰められた後にステータスチェックが失敗した場合

コンフリクトだけでなく、merge queue に詰められた後にステータスチェックが失敗するケースも起こり得る。その場合、失敗した queue より後の queue は、成功した queue にリベースされた後、再度ステータスチェックが走る。

おわりに

これまで main ブランチ push 時に動かしていた workflow は、多くが merge queue 追加時に移行できるはず。Merge method の設定はチームの方針によって、最適なものを選ぶのが良さそう。

参考

--

--