Git チームワークフロー: マージ (merge)、それともリベース (rebase) ?

質問は簡単です。git フィーチャーブランチ を利用しているソフトウェアチームにとって、完了済みの作業を開発のメインラインに取り込む最良の方法は何でしょうか?これは、確固たる意見を持つ両陣営によって繰り返し展開されている議論の一つですが、やはり議論には最低限の配慮を持って対応したいものです。 (その他の激しい議論の例としてはこれがあります: The Internet)。

リベースを行って、リポジトリの履歴をフラットかつクリーンに保つべきでしょうか?それとも、可読性と明晰さを犠牲にする事でトレーサビリティを得られる、マージを行うべきでしょうか?( ファストフォワード マージを禁止するなど。)

議論

このトピックは、vim と Emacs や Linux と BSD ほどまでには有名な論争の的とはなっていないものの、双方共に遠慮なく意見を述べ合っています。

all-things-git に対する私の経験的な印象としては、常にマージするアプローチの方が若干マインドシェアが大きい気がします。しかし、常にリベースするアプローチの援護者も、オンライン上でかなり積極的に意見を述べています。以下はその例です。

正直な所、ローカルクリーンアップとしてのリベースチームポリシーとしてのリベースは別物であるため、常にリベース vs. 常にマージという両陣営の対決は話がややこしいのです。

余談: コーディングのライフサイクルにおいては最高な、クリーンアップとしてのリベース

チームポリシーとしてのリベース、クリーンアップとしてのリベースとは異なります。 クリーンアップとしてのリベース は、gitの専門家のコーディングライフサイクルの健全な一部を構成しています。以下に、リベースを行う事が合理的かつ効果的となる場合とならない場合の、いくつかのシナリオを解説します。

  • ローカルで開発中。 あなたは、自分の作業を誰とも共有していません。この時点では、履歴をクリーンに保つためにはマージよりもリベースを優先した方がよいでしょう。仮に、リポジトリの個人的なフォークがあってそれを他の開発者と共有していない場合、自分のフォークにプッシュした後でもリベースできます。
  • コードレビューの準備が整っている。 あなたはプルリクエストを作成しており、他の開発者はあなたの作業をレビューして、恐らくそれをローカルでレビューするために自分たちのフォークに取ってきている場合です。この時点では、あなたは自身の作業をリベースするべきではありません。「リワーク」のコミットを作成して、フィーチャーブランチを更新する必要があります。これはプルリクエストのトレーサビリティに貢献して、不慮の履歴破損を防止します。
  • レビューは終了して、ターゲットブランチへの統合の準備が整っている。 おめでとうございます!これで、フィーチャーブランチを削除できます。これ以降、他の開発者はこれらの変更点をフェッチしてマージする事が無いため、これを機に履歴を削除しましょう。ここでは履歴をリライトして、元のコミットとやっかいな「pr リワーク」と「マージ」コミットを、重点のおかれた少数のコミットに収める事ができます。これらコミットへの明示的なマージの作成は任意ですが、フィーチャーからマスターへの進行を記録してくれるため、やる価値はあります。

この件についてハッキリさせた所で、ポリシーについて話しましょう。私はこの議論に対してバランスの取れた見解を保つよう心がけると共に、Atlassian 内におけるこの問題への対処法を紹介します。

リベースチームポリシー: 定義、良い点と悪い点

それぞれのチームは皆異なるため一般論を述べるのは難しいですが、とりあえず取り組んでみない事には始まりません。まず、次のポリシーを一例として検討してみて下さい。フィーチャーブランチの開発が完了したら、全ての作業内容を可能な限り最小限のコミット数にまでリベース/融合 して、マージコミットの作成を避けます。この際、変更点が確実にファストフォワードされるか、あるいはそれらのコミットをターゲットブランチに向けて cherry-pickします。)

作業がまだ進行中で、上流のターゲットブランチに合わせてフィーチャーブランチを最新の状態にする必要がある場合、履歴を上辺だけのマージで汚さない為にもpullまたは mergeの代わりにrebaseを使用します。

良い点

  • コード履歴は、フラットかつ可読のままになります。クリーンかつ明確なコミットメッセージは、コードコメント、または課題トラッカーへのコメント等と同様にコードベースの文書の一部を構成しています。このため、単一のフィーチャーまたはバグ修正のために相殺してしまう、31の単一行のコミットで履歴を汚してしまわない事が重要です。このような状況においては、バグまたはフィーチャーが導入された時期とその理由を求めて履歴を遡って探し出す行為は、非常に難しくなります。
  • 単一のコミット操作が簡単 (例、元に戻す)。

悪い点

  • 全ての開発履歴を備えた履歴ブランチを保持しておかない限り、フィーチャーを一握りのコミットにまで融合してしまうとコンテキストが隠れてしまいます。
  • 誰かがリベースしてしまうと、その人が行った細かい変更点を確認できないため、リベースはプルリクエスト と上手く合いません(ついでに述べると、Stash 開発チーム内では、プルリクエスト中には絶対にリベースしない事がコンセンサスとなっています。)
  • リベースには、危険な場合があります!共有ブランチの履歴をリライトすると、チームワークが崩れる要因を作ってしまうのです。フィーチャーブランチのコピー上でリベース/融合を行う事でこの問題は軽減できるものの、これはリベースの扱いには能力と注意力が要求される事を暗示しているのです。
  • 作業が増えます。お使いのフィーチャーブランチを最新の状態に保つためにリベースを利用する場合、類似したコンフリクトを何度も繰り返し解決しなくてはなりません。 確かに、rerere (reuse recorded resolutions)が可能な場合もありますが、ここではmergesに軍配が上がります。マージならば、一度コンフリクトを解決すれば全て完了です。
  • リモートブランチを使用してリベースする事のもう一つの副作用は、ある時点で強制的にプッシュしなくてはならない点にあります。今まで Atlassian で起きた最大の問題は、皆が強制的にプッシュしたまではよかったけれども、git push.defaultを設定していないという点でした。こうなると、ローカルとリモート双方における同一名のブランチが全て更新されるため、非常に煩わしい 状況がもたらされます。

注: 複数の開発者が携わっている共有ブランチにおいて履歴がリライトされると、破損が生じます

マージチームポリシー: 定義、良い点と悪い点

逆に、常にマージする事をベースにしたポリシーは次のような流れになります。フィーチャーブランチが完了したら、ターゲットブランチにマージします(masterまたはdevelopまたは next)。

必ず、マージは–no-ffで明示的にします。これによって、変更点を自動的にターゲットブランチの上にリプレイできる場合でも、gitは全てのケースにおいてマージコミットを記録するよう強制されます。

良い点

  • トレーサビリティ: フィーチャーブランチの歴史に関する情報を保持して、フィーチャーの全てのコミット部分を集合化します。

悪い点

  • 大量のマージコミットによって履歴が極度に汚され、またリポジトリの視覚的チャートにはさほど情報を追加しない虹色の線が表示され、何が起きているのかが分かりづらくなります。(公平に言えば、履歴内をどのようにナビゲートするかを把握しておくだけで、混乱は簡単に解決できます。ここでのポイントは、例えばgit log –first-parentを利用して、何が起きたかを理解する事です。)

merge-1

  • git bisectを利用したデバッグは、マージコミットのせいで非常に難しくなります。

決断しなければなりません。あなたにとって最も重要なものは何でしょうか?

どれが一番良いのでしょうか?専門家のお勧めはどれでしょうか?

あなた自身、あるいはあなたのチームがrebaseの複雑さに慣れていないか、これを理解できないのであれば、利用しない方が懸命でしょう。このようなコンテキストでは、常にマージするが最も安全な選択肢となります。

あなた自身、およびあなたのチームが両方の選択肢に慣れ親しんでいるのであれば、以下が決断のポイントとなります。重視するのは、クリーンでリニアな履歴ですか?それとも、ブランチのトレーサビリティでしょうか?前者であれば リベースポリシー、後者であればマージポリシーを選びましょう。

また、リベースポリシーには少数の禁忌が存在するため、より多くの手間が要求されますのでご注意下さい。

アトラシアンのケース

アトラシアンの Stash チーム内においては、フィーチャーブランチを常にマージする事、そして品質とコードレビューのためにブランチはプルリクエスト を通じてマージされる事がポリシーとなっています。ただし、チームはファストフォワードに関してはさほど厳密ではりません。

結論と謝辞

本記事は、Stash チームとの洞察力溢れるやりとりの confluence (合流) によって可能となりました。

この記事によってこれまでの疑念が解消され、あなたのチームに適したアプローチを採用するきっかけとなれば幸いです。git の素晴らしさをもっと知りたい場合は、私 @durdn と、素晴らしき @AtlDevtools チームをフォローして下さい。


*本ブログは Atlassian Blogs の翻訳です。本文中の日時などは投稿当時のものですのでご了承ください。
*原文 : 2013 年 10 月 28 日 "Git team workflows: merge or rebase?"