tea-e7n.dev

GitHub Stacked PRを使って依存PRを管理する

最近、GitHubからStacked PRという機能がパブリックプレビュー版としてリリースされました。

これが結構便利そうなので、サクッと使ってみてどのような使用感かを探っていこうと思います。

GitHub Stacked PRとは

名前の通りではありますが、GitHub上におけるPull Request (PR)をStackして管理できるという機能となっています。

1つの機能を実現するために、複数のブランチを切ることでスコープを切って段階的に実装を進める方式をとることがありますよね。

main
 └ PR1
    └ PR2
       └ PR3

そのような場合には、このように複数ブランチを連結させる形でPRを作成することで擬似的にPRをStack形式で管理することができます。

しかし、この方法には少々厄介なポイントがあり、例えば上記の例だとPR3まで連結させた後にPR1を変更するとなると、その変更をPR3にまで波及させるために手動での作業が必要となります。 また、PRの依存を管理する機能自体もGitHubの公式機能としては提供されておらず、あくまでもベースブランチ名を判断して、どのブランチからどのブランチにマージするPRであるということを自力で追うのが基本でした。

これが例のように3つ程度のPRであればなんてことないのですが、さらに多くの数のPRが連結するとなると管理が大変になることは容易に想像できます。

といったように、基本的な仕組みだけでもPRの連結管理はできるものの、簡単に管理できるかというとそれはまた別の話となっていました。

そこで登場したのがGitHub Stacked PRとなっています。

Stacked PRは、連結したPRをStack形式のグループとして扱うことで、連結PRを取り巻く作業や管理を簡単にしてくれる機能です。

今回は、そんなGitHub Stacked PRの使用感についてみていこうと思います。

セットアップ

ghコマンドがインストールされていることを前提として、以下のコマンドを実行するだけで簡単にStacked PRを扱うためのコマンドをインストールできます。

> gh extension install github/gh-stack

次のコマンドにてインストールが正しく行われたかどうかを確認できます。

> gh stack -v
gh stack version 0.1.0

試してみる

まず初めにmainブランチから以下のコマンドを実行して、新規stackと、ベースとなるブランチを作成します。

> gh stack init user-model

 Created stack: main user-model
  You're on user-model (top of stack).

説明されている通りですが、このコマンドによって、main <- user-model というブランチの依存関係を表現したstackが作成されました。 そして、同時にuser-modelブランチにswitchしたので、このブランチにて適当に実装を追加してみましょう。

今回は以下のような適当なGoの実装を追加しました。 (go.modは適当に準備します)

package main

type User struct {
	Name string
}

func (u User) DisplayName() string {
	return u.Name
}

それでは、一旦これをコミットします。 コミット自体は普段通りのやり方で行えば問題ありません。

それでは次に、ここに連結するブランチを2つ追加してみます。 ここでは省略しますが、各々のブランチで小さな変更を加えています。

> gh stack add greeting
 Created and checked out branch "greeting"

> gh stack add greeting-test
 Created and checked out branch "greeting-test"

現時点で、stack上では main <- user-model <- greeting <- greeting-test というブランチの依存関係が表現されていることになります。

それでは、現時点でのstackの内容をGitHubのremoteに反映させようと思います。 それには以下のコマンドを実行します。

> gh stack submit

すると、次のようなTUIが表示されます。

gh stack submitでStacked PRを作成するためのTUI

このTUI自体は、マウスやトラックパッド操作で直感的に操作可能となっています。 今回は全てのブランチに対してPRを作成したいのでCREATE PRを有効に、またDraftである必要性はないので、CREATE ASREADYに設定しました。

設定が終わったら、最下部のSUBMIT 3 PRsをクリックします。 すると、しばらく時間を置いた後に3つのブランチに対応するPRが自動的に作成されます。

GitHub上では、次のようにPRがStack管理されていることを確認することができます。

GitHub上に表示されたStacked PRのスタックマップ

GitHubのマージボックスに表示されたStacked PRのスタック情報

もうすでにめっちゃいいですね。こうやって管理されるだけで随分と負荷が減ります。

では次に、最もベースのuser-modelブランチに簡単な変更を入れてみます。 対象ブランチにswitchして、実装を追加します。

> gh stack checkout user-model
package main

type User struct {
	Name string
}

func (u User) DisplayName() string {
	// 以下を追加
	if u.Name == "" {
		return "guest"
	}
	return u.Name
}

更新が終わったらcommitして、この更新をstackの上位のブランチに一斉に波及させます。

> gh stack rebase
 Fetched latest main from origin
 Trunk main is already up to date
Stack detected: (main) <- user-model <- greeting <- greeting-test
Rebasing branches in order, starting from user-model to greeting-test
 Rebased user-model onto main
 Rebased greeting onto user-model
 Rebased greeting-test onto greeting
All branches in stack rebased locally with main (458dc2c)
To push up your changes, run `gh stack push`

> gh stack push
Pushing 3 branches to origin...
 Pushed 3 branches
Run `gh stack view` to see your stack of PRs

これだけで、依存ブランチ全てに対してrebase処理が走ります。便利だ・・・

それでは最後に、ブランチのマージ作業を行います。 本来マージ順はタスク内容によって色々事情が絡んでくると思いますが、今回は特に何もないので一旦最下位のブランチをマージします。えい。

Stacked PRの最下位PRをマージした後のGitHub画面

マージ後も、上位PRがまだopenであることがUI上で一目でわかりますね。これは嬉しいです。

では次に、あえて2つ目を飛ばして、最も上位のPRをマージしましょう。

Stacked PRの最上位PRをマージしてスタック全体がマージされたGitHub画面

すると、2つ目のPRも同時にマージされました。

ということで、大体使用感はわかったので今回はこの辺りで作業は終わります。

まとめ

GitHub Stacked PRの機能を待ち望んでいた人は多いのではないでしょうか。

私自身、連結したPRを作って作業をすることが結構あるので、今回の機能追加はかなり嬉しい気持ちです。

まだまだ今回触れられなかったコマンドやオプションがたくさんあるので、もうちょっと勉強して上手く使えるようになろうと思います。