Reorient GitHub Pull Requests Around Changesets

Mitchell Hashimoto

GitHubのプルリクエストをチェンジセット中心に再設計する

原文は Mitchell Hashimoto により に公開されました。 このブログを購読する

私は、1000人以上のコントリビューターを抱える大規模なオープンソースプロジェクトのメンテナーとして、また大規模なクローズドソースの企業プロジェクトのエンジニアとして、さらにそれより小さなあらゆる規模のプロジェクトでGitHubを使ってきました。今日に至るまでのそうした経験を通して、GitHub上で私がほぼすべての時間を費やしているのがプルリクエストであり、同時にそれは残念ながらGitHubの中で最もフラストレーションの溜まる部分でもあります。

プルリクエストについて改善してほしい点はたくさんありますが、私が抱えている問題の大部分は、たった一つの大きな機能で解決できます。それがチェンジセットです。この記事では、その提案と私が実現してほしい内容について説明します。

断り書き:ここで述べるアイデアはオリジナルではありません!自分が考え出したものだと主張するつもりもありません。ここでの提案は、十分に探求されてきたGitのワークフローに基づくものであり、GerritやPhabricator、あるいは昔ながらのメールベースのパッチレビューといった他のプロダクトで、部分的または完全に実装されているものでもあります。


現在の問題点

現在のGitHubのプルリクエストのライフサイクルは、実質的に一つの巨大で可変なチェンジセットです。これは混乱の元です!

現在の典型的なPRはこうなります。コントリビューターが一連のコミットをブランチにプッシュしてPRを作成すると、そのPRはそのブランチそのものを表すようになります。人々はコメントを通じてPRについて議論します。コントリビューターが新しい変更をプッシュすると、それは同じPR上に直接表示され、即座に更新されます。レビュアーがコメントを残しているのと同時にコントリビューターが変更をプッシュすることもあり、すべてが同じPRを更新していくのです。

これには多くの問題があります。

  • レビュアーがPRの以前の状態に対してレビューを残しても、レビュー中にコントリビューターが変更をプッシュしたために、即座に古くなってしまうことがあります。

  • さらに悪いことに、レビューが部分的に古くなり、他のフィードバックがコントリビューターがプッシュした変更の文脈では意味をなさなくなることもあります。例えば、行コメントで「前のコメントと同様」と書かれていても、コントリビューターがその行を移動させる変更をプッシュしたことで、前のコメント自体が消えたり隠れたりしてしまうのです。1

  • レビューには、それが紐づいていたコミットに関するメタデータが含まれておらず、投稿されたタイムスタンプしかありません。ユーザーはタイムスタンプからコミットをなんとなく推測できますが、完全に正確ではありません。レビュー中にコミットが追加されると、タイムスタンプ上は最新のコミットの後に投稿されたように見えても、実際には前のコミットに対するレビューだったということが起こり得るからです。😕

  • レビューのフィードバックに対応するための作業途中のコミットが、ブランチにプッシュされた途端に可視化されてしまいます。そのため、コントリビューターはすべてのフィードバックを一つのコミットで対応することを強いられるか、レビュアーが部分的に対応されただけのフィードバックに向き合わなければならなくなります。

  • PRの過去の状態を簡単に見返すことができません。後のコミットを無視して以前の一連のコミットだけをレビューしたい場合、手動で「compare」ビューを作るか、ローカルにチェックアウトするしかありません(私は後者の方法をとっています)。しかし、どちらの方法でも得られるのはコードの変更点だけで、その時点でのレビューのフィードバックまでは得られないのです!

  • 上記と同様に、コントリビューターが複数の新しいコミットをプッシュした場合、新しい一連のコミットと古いものを簡単に比較することができません。実質的に1コミットずつしか見返すことができないのです。この場合も、またローカルのgitに頼って手動で差分を作るしかありません。

  • 他にもあります……。後でいくつか触れますが、言いたいことは伝わったかと思います。

上記のいくつかの点については、私の理解が細部で間違っているかもしれません。「問題5(a)はこれをやれば解決できたはずだ」と指摘されることもあるでしょう。それはそれで助かります!しかし、私が言いたいのは、一歩引いて見たときに、これらの問題を引き起こしている根本的な仕組みこそが本当の問題だということです。すなわち、ブランチをコミット単位で追跡する、単一の可変なチェンジセットという仕組みです。


チェンジセット

解決策はチェンジセットです。プルリクエストを単調増加する番号(v1、v2、……)でバージョン管理可能にするのです。これらのバージョンは一般に「チェンジセット」と呼ばれます。

各チェンジセットは、ある固定された時点におけるブランチの状態を指し示します。これらのバージョンは不変です。新しいコミットがプッシュされれば、それは新しいチェンジセットの一部となります。コントリビューターがブランチをforce pushした場合も、それは新しいチェンジセットの一部になります。以前のチェンジセットは永久に保存されます。

新しいチェンジセットは、(コミットごとに)すぐに公開することも、コントリビューターがレビューのために新しいバージョンを提案することに決めるまで公開を遅らせることもできます。後者の方法なら、コントリビューターは以前のフィードバックに対応するために複数のコミットを積み重ね、準備ができたときにだけそれらの変更を公開できます。

チェンジセットの世界では、フィードバックはチェンジセットに紐づけられます。レビュアーがあるチェンジセットのレビューを始めている間に新しいチェンジセットが公開されても、レビューという一つのまとまりが以前のチェンジセットに紐づけられているため問題ありません。

将来のチェンジセットでは、以前のチェンジセットで未解決のコメントがあるファイルや行を明示しておくと便利なことがよくあります。これにより、以前のチェンジセットに対するフィードバックが見失われることがなく、どのチェンジセットが承認される前にも必ず対応しなければならないことが保証されます。

通常、各チェンジセットは異なるGitのrefで表されます。例えば、現在のGitHubのプルリクエストは通常refs/pr/1234であり、ローカルのgitでこのようにして任意のプルリクエストをチェックアウトできます。チェンジセットであれば(仮に)refs/pr/1234/v2のようになり、個々のチェンジセットもチェックアウトできるようになります。

PR自体を「承認」してマージするのではなく、レビュアーはチェンジセットを承認します。これにより、コントリビューターは一つの問題に対して異なるアプローチの複数のチェンジセットを単一のPR内に投稿することもでき、メンテナーは必ずしも最新ではないチェンジセットを、マージしたい変更として選ぶことも可能になります。


GitHub、お願いします!

チェンジセットは、多くのオープンソースプロジェクトや企業で広く確立されたパターンです。GerritやPhabricatorのような既存のプロダクトでは、ユーザー体験としての課題もすでに十分に探求されています。また、チェンジセットは非破壊的な形で導入できると私は考えています(現在のPRは、単一の可変チェンジセットモードのようなものなのですから)。

チェンジセットがあれば、プルリクエストは大規模なプロジェクトや組織にとって格段にスケーラブルなものになります。スケーラビリティだけでなく、プルリクエストに関わる双方にとって、レビューのプロセスをよりクリーンで安全なものにしてくれます。

もちろん、これはあくまで私自身と私の経験に基づく意見ですが、この一つの大きな機能だけで、GitHubを使う上での私のQOLと能力は劇的に向上するはずです2

脚注

  1. これは些細で不便なだけの問題ですが、この問題は拡大すると深刻な問題になります。

  2. 「GitHubを使わなければいいじゃないか!」というフィードバックは以前にも聞いたことがあります。私が現在GitHubを使っているのには他にも多くの理由があるので、個人的には今のところ現実的な選択肢ではありません。もしGitHubを使わずに済むのであれば、他のプロダクトでチェンジセットのサポートを見つけることは確かに可能です。

この記事は「muse-spark-1.2-contributor」を使用して翻訳されました。

コメント