Posts GitHub GitHub native Stacked PR

GitHub native Stacked PR

はじめに

20206年7月30日、これまでprivate preview扱いだったGitHub native Stacked PR(以下StackedPR)という機能がpublic previewとなりました!

まだpublic previewであるため破壊的な変更が入る可能性はあるものの、Repositoryごとの有効化設定など不要で利用できるようになります。

GitHub native Stacked PRとは

StackedPRのくわしい解説は公式記事がありますので、こちらを参照してください。

一言で言うと、巨大な修正を複数のPRとして分割して扱うための機能です。

コーディングエージェントを活用した開発効率化のため、worktreeが注目されました。 worktreeが、トピックごとの変更を隔離するための機能なら、StackedPRは、1つのトピックの変更をレビュー可能な単位に分割して積み上げるための機能です。

ghコマンドの拡張として導入できるgh stackコマンドを活用することで、CLIでStackedPRを扱うことができます (gh stackコマンドについてはこちらを参照)。

従来の方法とその課題

PRの差分が大きくなると、レビューが非常に難しくなり、不具合が混入する原因となります。 この問題に対応するため、大きな機能の開発では複数のブランチに差分を分割するという工夫が行われてきました。 以下のようなイメージです(GitHub公式記事より引用)

    ┌── feat/frontend PR #3 (base: feat/api-endpoints)  ← top
  ┌── feat/api-endpoints PR #2 (base: feat/auth-layer)
┌── feat/auth-layer PR #1 (base: main)               ← bottom
main (default base branch)

この例では、作成したPRをfeat/auth-layerfeat/api-endpointsfeat/frontendの順で実装します。 上層は下層で行った実装に依存できます。

レビュアーも同様の順番でレビューを行うことで、実装者と同じ流れで差分を追うことができますし、差分が小さくなるためレビューしやすくなります。 実際に私も業務ではよくこのような方法を使っていました

しかし、私自身は2つの課題感を持っていました

下層での変更の反映

上記の例で、feat/frontendの実装中にfeat/api-endpointsに修正が必要になった時には、多くのgitの操作をする必要が出てきます

  • feat/api-endpointsで修正を実施してcommit
  • feat/api-endpointをmerge or rebaseしてfeat/frontendに修正内容を取り込み
  • もしコンフリクトすればそれを修正
  • 2つのブランチをpush

コンフリクト修正を伴う可能性もありますし、下層での実装内容を修正したいだけなのにgit操作を複数行う必要があり、ヒューマンエラーも起きやすいです。 また、さらに多くのスタックがあった場合、全ての上層に対して同じ作業を繰り返す必要があります

PR間の関係性の明示方法

ブランチをスタックさせる方法は、あくまでGitやGitHubの仕組みを使った開発フローの工夫です。 GitHubやGit上ではPRがスタックされている状態を表現することはできません。

そのため、例えばレビュアーにスタックされているPRであることを知らせるには、PRのdescriptionにその詳細を記入していました。

例えば、渡しの場合はPRの詳細以外に、以下のような情報を都度記載していました

  • スタック全体で達成したい実装内容
  • 全部でいくつのPRがスタックされているのか
  • このPRが何番目のPRなのか
  • 次のPRがどれか

記入自体は難しい内容ではありませんが、スタックしたPRの分だけ同じ内容を書く必要があり、面倒でした

また、コーディングエージェントにとっても、スタックされているPRとそうでないPRの区別はつかず、ただの並列なリストにしか見えません。 ブランチ名の命名の工夫などにより推測させることはできるものの、正確な特定にはブランチ名を明示的に与える必要があります

StackedPRによる解決

StackedPRはGitHub公式によって、ブランチによる差分の分割をサポートした機能です。 ここまでに上げた2つの課題を大きく改善する事ができるようになりました。

下層での変更の反映

gh stack rebaseというコマンドがこの課題を直接的に解決しています。

このコマンド一つで、最下層から順にrebaseを実施し、スタックの線形な履歴を保つことができます。 コンフリクトが発生した場合は修正が必要なものの、git rerereを利用して何度も同じコンフリクトを修正しなくて良い工夫もされています。

また、gh stack sumbitを使えば、スタックされたブランチを全てpushした上で、PRまで作成できます。

一度gh stack submitしてしまえば、GitHubのUI上から操作できる項目も多いです。 また、CLIでもグラフィカルだったりインタラクティブだったりするコマンドが多く、学習コストを抑える工夫がされていると感じました

PR間の関係性が明示的に示される

GitHubのUI上では、スタックされているPRについての情報が自動で表示されます

  • 全部でいくつのPRがスタックされているのか
  • このPRが何番目のPRなのか
  • 次のPRがどれか
  • スタックされているPRのステータス

など、PRの画面から他のスタックされたPRの情報までしっかり確認できます。 レビュアーも確認しやすく、情報の更新忘れもありません

また、CLIコマンドが提供されているため、コーディングエージェントからもスタックされているPRの情報を自由に取得できます。 gh stackコマンドが整備されているので、もちろん操作も自由に行なえます

また、StackedPRを扱うためのエージェントスキルまで提供されているため、自然言語でも操作が可能です スキルについての公式ドキュメント

まとめ

StackedPRによって、ブランチで巨大な差分を分割する方法をGitHub公式がサポートする事になりました。 これまで手動で管理していたPR間のリベースやスタックの管理を、StackedPRに任せることができるようになります。

また、CLIツールとスキルの提供によってコーディングエージェントからの操作を容易に行えるようになりました。

レビューフェーズはボトルネックになりやすい部分ですが、AIを主とするか人を主とするかは、チームによって判断が分かれている部分だと思います。 この機能によって少しでもレビュアー(人にもAIにも)に優しいPRを作れるようになりたいものです。