GitHubでの”Merge pull request”の弊害

私はGitHubが大好きです。GitHubはオープンソースへのコントリビューション(寄与貢献)を何十倍も容易に、そして楽しいものにしたと思います。ですが、GitHubがPull RequestというwebのUI形式で前面に押し出しているオープンソースのメンテナーのワークフローが、プロジェクト品質とコントリビューションを受けつけるスピードの弊害になるということに気がつきました。そこで、GitHubのPull Requestにある「Merge pull request」ボタンをクリックする前に、少しお話をさせてください。

メンテナーの紹介

ジェーンはそこそこの成功を収めているオープンソースプロジェクトのメンテナーです。彼女は毎週プロジェクトのGitHubリポジトリに上がる新しいIssueを確認し、リクエストに対し速やかにフィードバックを返します。リクエストをすべて実行する時間がない場合は、のマークを送り、開発者たちがコードを完成させPull Requestできるようにします。
いいメンテナーなら誰でもするように、彼女はコントリビューティングドキュメントを書き上げました。当然彼女のリポジトリにはプロジェクトが進むにつれてREADMECHANGELOGが更新されます。コントリビュータが開発しながら実行でき、機能を追加するごとに拡張できる数段階の自動化テストもあります。コードベースをきれいにしておくため、ジェーンは包括的な公式スタイルガイドをも採用しました。

このように、できる限り入念に気を配っているジェーンですが、問題を抱えています。マージされていないPull Requestが彼女のリポジトリに何十個もあるのです。どれもさほど複雑ではないのですが、彼女はマージ作業が常に遅れているように感じています。ざっと見てみると、フィードバックに基づいて大幅な修正が必要なものもいくつかはありますが、大部分はテストの追加やスタイルガイドに沿った数ヵ所のスペース調整、CHANGELOGエントリーの欠落、大量のコミットをひとまとめにするなど、ちょっとしたことで行き詰まっているのです。

さらにストレスが溜まるのは、ジェーンがすぐにフィードバックをしても、大抵の場合、コントリビュータは興味を失っていたり、当初自分が送ったPull Requestを最後に受けつけるのを忘れているのです。ジェーンが(半ば意地になって)見落としによるスタイル変更など、ささいな依頼を5回もすると、もはやあら探しのように感じられ、コントリビュータの興味を失わせる結果となってしまいます。

その結果どうなるでしょうか? マージされていないあらゆるコード一覧が精神的な負担となっていきます。ストレスを溜めたジェーンはやる気を失い、コントリビュータも次第に姿を現さなくなりそうです。

リーナスの書いたGitをそのままに

どうも、ナサニエルです。以前は私もジェーンと同じ立場でした。最近、ActiveMrchantプロジェクトに参加したのですが、当初このプロジェクトのPull Request一覧には、取るに足らない内容の項目が何ページも続いていました。ほとんどはすぐに解決できるものでしたが、下記の2つの方法を前に行き詰まってしまったのです。

  1. リクエストを出したコントリビュータが戻ってきて対応してくれるのを待ち、そのあとでマージする。
  2. まだ完全でない内容のコードをマージし、自分で修正して再度コミットする。

どちらも、あまり気が進みません。しばらくは前者の方法をとっていました。コントリビュータに詳細をフィードバックし、適用されるまで待つのです。しかしほとんどの項目に動きはなく、長いリストが私を見つめ返してくるばかり。そのうち私は大勢の人にGitの上級レベルの使い方を教えて回る羽目になりまし た。「WIPステータスのコミット5つをsquashしてもらえますか?」と言うと「スカッシュって野菜の名前でしたっけ?」という答えが返ってくるんですからね(訳注:米ではズッキーニや近似種をまとめてsummer squashと呼びます)。

ですが、後者の手順をとると修正履歴のステータスがおかしくなります。まず、ちゃんと動かない中間コードがgit bisectの二分探索で特定されてしまう可能性があります。コードの修正に時間がかかる場合、修正前のコミット(または複数のコミット)に対して新しいPull Requestが出されることもあるでしょう。修正自体を忘れてしまうことだってあるかもしれません。この方法は1、2度試してみましたが、あまりの苦痛に完璧主義の私にはとうてい耐えられませんでした。

では、どうすればいいのか。最終的に分かったのは、Gitはこうした状況を簡単に処理できるよう、きちんと設計されているということです。私が抵抗を感じた真の理由は、GitHubが採用しているワークフローにありました。そこで私は「Merge pull request」ボタンを使うのをやめ、代わりに下記の手順を踏むことにしたのです。

  1. hubをインストールする。
  2. Pull RequestのURLを取得する。実際には” https://github.com/Shopify/active_merchant/pull/1259″のような形式。
  3. リポジトリのmasterブランチで git am -3 <url>を実行する(urlはPull RequestのURL)。コンフリクトが起きたら修正して、git am –continue
  4. ここでローカル環境のmasterブランチを変更する。問題点の修正、不要なスペースの削除、CHANGELOGの更新、テストの追加などを行う。簡単、簡単。

もちろんこれで終わりではありません。このあと変更をプッシュすべく準備する必要がありますが、次のステップへ進む前に、このやり方の重要な点を説明させてください。Gitには、メーラーの受信ボックスに届いたパッチを吸い上げるために設計されたam(”apply mail”の略)というビルトインツールが用意されています。GitHubの便利なhubツールは、このgit amを利用してwebにホストされたパッチ、特にどんなPull Requestであっても取得できる生パッチを処理しています。つまり上記のやり方は、例えばカーネルのメンテナーがパッチのレビューやマージを行う際にとる手順にずっと近いと言えるのです。

歴史は勝者によって記される

さて、ローカルのmasterブランチに完璧なコード(言いすぎ?)があって、git logにはコントリビュートされたコミットがすべて表示され、私の変更はまだコミットされていません。このあと私がするべきことは、状況によって異なります。

  • コントリビューションが1つのコミットなら、git commit –amendを使って、オリジナルのコミットを自分の変更で修正します。
  • コントリビューションが複数の”進行中”(WIP)のコミットの場合は、変更をコミットして、git rebase -i origin/masterで自分のものも含めすべてのコミットを1つの論理コミットにまとめます。
  • 私はほとんど経験がありませんが、コントリビューションが複数の論理コミットの場合は、単独で意味を成すステップを少しずつ増やしていくという観点から、「履歴を残す価値あり」です。ですから私は自分のコミットを最後に追加します。
    さて、コントリビューションの準備ができましたが、あと1つだけ、ちょっとしたトリックを追加して仕上げをしましょう。コミットメッセージの最終行としてCloses #XXXを追加するのです。こうしておくと、git pushをするとPull Requestが自動的にクローズするのです。カッコいいでしょう? ブラウザでPull Requestがオープンになっていると、ブラウザは自動的に更新して、クローズするPull Requestを反映します。GitHub、君はなんて賢いんだ。

ActiveMerchantプロジェクトでは、コミット履歴は次のようなものになります。

Railsのコミット履歴と比べてみてください。

私の好みは決まっています。さらにうれしいことに、私は今ではコントリビュータのことでああでもない、こうでもないと時間を費やすことがずっと少なくなり、コードのマージにずっと多くの時間を使えるようになりました。最後にとっておきの情報をお教えしましょう。ActiveMerchantの履歴を見て気がついたでしょうか? 私がどれだけコミットの編集やリベースを行ったとしても、コントリビュータはコミットに対する功績をすべて認められます。それが私のやり方なのです。

あの厄介なボタン

それでも、「Merge pull request」ボタンを使った方がいいのはどんな時でしょうか?

  • ものすごく小さい変更。例えば1センテンスのドキュメントの単なる微調整とか。そうは言っても、私自身はもうやりません。だって面倒ですし、それを「正しく」やることはそんなに時間がかかりませんから。
  • 内部/プライベートのプロジェクト。どうぞお好きなように。あなたの状況も好みも私には分かりません。個人的には、私はやっぱりクリーンなコミット履歴が好きですけどね。

では「Merge pull request」ボタンを使用しないことのメリットは何でしょうか。

  • コントリビュータがすべての詳細を正確に取得する必要がない。
  • メンテナーはコミットを簡単にクリーンアップできる。
  • 投稿やメールにかける時間や手間が減り、その分コーディングに時間をかけられる。
  • コミット履歴は便利なストーリーになり、読むのが楽しくなる。
    私はメリットが大好きですし、私の経験からすると、メンテナーとしての私のメリットとは、時間の短縮とストレスの軽減を意味します。「ボタンを押すだけでコントリビューションを手に入れられる」なんて理論上は素晴らしいけれど、現実はメンテナーとしての仕事をますます大変なものにするだけです。

これには、ジェーンも100%賛成してくれるはずですよ!