マージ

ブランチを開発の独立したラインでメンテナンスするのに使用する所では、何らかの段階で、ブランチに対して行われた変更をトランクにマージしたくなるでしょう。逆もまた然りです。

Subversion 内でどのように分岐やマージを行っているかを、はじめる前に理解しておくのは (かなり複雑になってしまうため) 重要です。Subversion book の Branching and Merging の章を読むのを強くお勧めします。ここには詳しい説明とたくさんの使用例があります。

注意する次のポイントは、マージは 常に 作業コピーで行われるということです。ブランチの中に、変更をマージしたい場合、ブランチの作業コピーをチェックアウトして、その作業コピーで TortoiseSVNマージ... を行い、マージウィザードを起動しなければなりません。

一般に変更を加えていない作業コピーへマージするのはいい方法です。作業コピーに何か他に変更が加えられているなら、まずコミットしてください。思うようにマージできないのなら、変更を取り消す必要があるかもしれません。取り消す コマンドはマージする前に行った変更をすべて取り消してしまいます。

マージを行うには、以下で説明するような、少し異なる方法で行う、3 つのよくあるケースがあります。マージウィザードの最初のページで、どちらで行うか確認しますので、選択してください。

リビジョンの範囲をマージ

ひとつ以上のリビジョンをブランチに (またはトランクに) 適用したり、その変更点を他のブランチに適用したい時、この方法で行えます。

Subversion に以下のことを行うように指示を出しています。ブランチ A のリビジョン 1 [FROM] から、ブランチ A のリビジョン 7 [TO] までの必要な変更点を計算し、(トランクやブランチ B の) 作業コピーに変更点を適用する

ブランチの再統合

この方法は、Subversion book で述べられている機能ブランチを作成する際についてを扱います。トランクのすべての変更を、週ごとに機能ブランチに移し、機能が完全になったらトランクにマージします。機能ブランチをトランクと同期させておくため、ブランチの最新バージョンとトランクは、ブランチへの変更を除いて、完全に一致するはずです。

これは以下に述べるツリーのマージの特殊なケースで、(通常) 開発ブランチからマージする URL のみが必要です。これは使用する正しいリビジョン範囲を計算するのに、Subversion のマージ追跡機能を使用し、トランクの変更でブランチを完全に更新できたかを確認するのに、追加チェックを利用します。これはあなたが最後に同期を取ってから、他の誰かがトランクにコミットした作業を、偶然元に戻していないことを確認します。

マージ後には、全ブランチでの成果物が、メインの開発ラインに完全にマージされます。このブランチは冗長になり、削除できます。

一度再統合によるマージを行うと、このブランチを開発に使用し続けるべきではありません。というのは、既存ブランチをトランクに再同期しようとする場合に、マージ追跡機能は再統合を、まだブランチへマージされていないトランクの変更として認識し、ブランチからトランクへのマージをブランチに対して行います! これを解決するには、単純にトランクから新しいブランチを作成し、次のフェーズの開発を続けることです。

異なる 2 つのツリーをマージ

これは、再統合する方法のもっと一般的なケースで、Subversion に次のことを行うように指示を出しています。トランクの最新リビジョン [FROM] から ブランチの最新リビジョン [TO] までの必要な変更点を計算し、(トランクの) 作業コピーに変更点を適用します 最終結果はトランクがまさしくブランチと同様になっているということになります。

サーバ・リポジトリが、マージ追跡をサポートしていない場合、これはブランチをトランクにマージする唯一の方法になります。別のケースとしては、ベンダブランチを使用していて、新しくベンダから落としたコードの変更を、トランクにマージする必要がある場合もありえます。詳細は、Subversion Book の vendor branches をご覧ください。

リビジョン範囲のマージ

図4.36 マージウィザード - リビジョン範囲の選択

マージウィザード - リビジョン範囲の選択


元: フィールドには、作業コピーに取り込みたい変更があるブランチやタグのフォルダの URL をすべて入力してください。... をクリックしてリポジトリを閲覧し望みのブランチを探すこともできます。このブランチから以前マージしていれば、以前に使用した URL の履歴のドロップダウンリストを使うだけです。

マージするリビジョンの範囲 フィールドに、マージするリビジョンのリストを入力してください。ここではリビジョン一つ、カンマで区切ったリビジョンの指定、ダッシュでつないだリビジョンの範囲と以上の組み合わせが利用できます。

重要

TortoiseSVN とコマンドラインクライアントを比較すると、リビジョン範囲を指定する方法に重要な違いがあります。これを思い浮かべる簡単な方法は、フェンスの柱と、フェンスの板について考えることです。

コマンドラインクライアントでは、 で指定した 2 本の フェンスの柱 のリビジョンを使用して、マージする変更を指定します。

TortoiseSVN では、フェンスの板 を使用して、マージする変更セットを指定します。マージするためのリビジョンを指定するログダイアログを使用する場合、チェンジセットとしてどこに各リビジョンが現れるか、明白になるためです。

かたまりとしてリビジョンをマージしていると、subversion book にある方法では、今回 100-200 をマージし、次回に 200-300 をマージすることになります。TortoiseSVN では、今回 100-200 をマージし、次回に 201-300 をマージします。

この違いは、メーリングリストでたくさんの論争を巻き起こしてきました。私たちは、コマンドラインクライアントと違うことを認めます。しかし、大多数の GUI ユーザにとって、私たちが実装した方法の方が理解しやすいと信じています。

必要なリビジョンの範囲を選択する最も簡単な方法は、ログを表示 をクリックして、最近の変更点一覧をログメッセージと共に表示することです。単一リビジョンからマージしたい場合は、そのリビジョンを選択するだけです。複数のリビジョンからマージしたい場合は、その範囲を (通常 Shift キーを押しながら) 選択してください。OK をクリックすると、マージするリビジョン番号のリストに入力されます。

すでにコミットしてしまった変更を取り消して、作業コピーを元に戻すようにマージする場合、元に戻すリビジョンを選択して、逆マージ チェックボックスを必ずチェックしてください。

このブランチからの変更をすでにマージしてある場合、うまくいけば変更をコミットしたときのメッセージに、マージした最後のリビジョンを記録してあるかもしれません。この場合、作業コピーのログメッセージを追跡するのに ログを表示 を使用できます。リビジョンをチェンジセットとして考えているのを思い出し、前回のマージの終点を今回のマージの始点をしてください。たとえば、前回リビジョン 37 から 39 までマージした場合、今回のマージの始点をリビジョン 40 にしてください。

Subversion のマージ追跡機能を使用する場合、どのリビジョンをすでにマージしたかを覚えておく必要はありません。Subversion が記録しています。リビジョン範囲をからのままにしておくと、まだマージしていないリビジョンすべてが対象になります。詳細は 「マージ追跡」 をご覧ください。

他の人が変更をコミットしている場合、HEAD リビジョンを使用するのは注意してください。最後の更新のあとに誰かがコミットを行っており、想定しているリビジョンを指していないかもしれません。

をクリックして 「マージオプション」 に進んでください。

ブランチの再統合

図4.37 マージウイザード - マージの再統合

マージウイザード - マージの再統合


機能ブランチをトランクにマージするには、トランクの作業コピーから、マージウィザードを起動しなければなりません。

元 URL: フィールドには、マージして戻したいブランチの、完全なフォルダ URL を入力してください。... をクリックして、リポジトリの参照もできます。

再統合マージを適用するには、いくつか条件があります。まず、サーバがマージ追跡をサポートしていなければなりません。作業コピーの深さに制限があってはなりません (まばらなチェックアウトではならない)。ローカルの変更や切り替わった項目、最新以外のリビジョンに更新された項目があってはなりません。ブランチでの開発中にトランクに行われた変更は、ブランチをまたがってマージされなければなりません (もしくはマージするとマークされなければなりません)。マージするリビジョンの範囲は自動的に計算されます。

2 つの異なるツリーをマージする

図4.38 マージウィザード - ツリーのマージ

マージウィザード - ツリーのマージ


機能ブランチをトランクにマージするために、この方法を使用する場合、トランクの作業コピーでマージウィザードを起動する必要があります。

元: フィールドには、トランク の完全なフォルダ URL を入力してください。間違っているように思われるかもしれませんが、トランクが、ブランチの変更を追加する起点となることを思い出してください。... をクリックして、リポジトリの参照もできます。

先: フィールドに、機能ブランチの URL を入力してください。

元 リビジョン フィールドと 先 リビジョン フィールドの両方に、同期をとりたい 2 つのツリーの最新リビジョン番号を入力してください。他の誰もコミットしていないという確認が取れているのなら、どちらも HEAD リビジョンを使用できます。同期をとってから、誰かがコミットする機会があったのなら、最近のコミットの内容を失わないように、リビジョン番号を指定してください。

リビジョンを選択するには ログを表示 も使用できます。

マージオプション

ウィザードのこのページでは、マージの実行前に、さらにオプションの指定ができます。ほとんどの場合デフォルトの設定使用するだけでしょう。

また、マージする深度も指定できます。これは、作業コピー内をどの程度深くたどってマージを行うべきかということです。深度については 「チェックアウトの深度」 で説明しています。デフォルトの深度は 作業コピー で、既存の深度をそのまま使用します。

大抵、共通の系統が関係する変更点をマージするように、ファイルの履歴を考慮に入れたいと思うでしょう。時には、おそらく関係する (しかしリポジトリにない) ファイルのマージが必要となります。例えば、サードパーティ製ライブラリのバージョン 1 とバージョン 2 を、それぞれ別のディレクトリにインポートしたとします。これは論理的には関係ありますが、Subversion は tarball をインポートしたとしか見えないので、関係があるとはわかりません。この 2 つのツリーの違いをマージしようとするなら、完全に追加してから完全に除去することになります。Subversion が履歴ベース差分ではなくパスベース差分を使用するようにするには、系統情報の無視 チェックボックスをチェックしてください。このトピックの詳細は、Subversion book の Noticing or Ignoring Ancestry をご覧ください。

改行コードや空白の変更に対する扱い方を指定できます。このオプションについては 「改行コードと空白のオプション」 で説明しています。デフォルトの振る舞いは、空白や改行コードの差異も実際の変更としてマージします。

マージ追跡を使用していて、実際にはマージせずにマージしたという印をつけたい場合、マージのみを記録する チェックボックスをチェックしてください。そうする理由として二つ考えられます。一つは、マージそのものがマージアルゴリズムに対して複雑すぎ、手でコードを修正した後、マージアルゴリズムが行うようにマージにより変更したという印をつけたい場合です。もう一つは、特定のリビジョンがマージされるのを防ぐ場合です。すでにマージしたという印がついていれば、マージ追跡を関知するクライアントでは、マージを防げます。

すべての設定が終わって、マージ ボタンを押すだけになったとします。結果のプレビューを行う場合、マージのテスト はマージ操作を行いますが、作業コピーにいずれの変更も加えません。これで実際のマージで変更が入るファイルの一覧を表示し、また競合が発生する範囲も表示します。

マージ進行ダイアログには、マージの各状態を、リビジョン範囲とともに表示します。ここには想定していたよりも一つ多くリビジョンを表示するかもしれません。例えば、リビジョン 123 をマージするように指示した場合、進行ダイアログには、リビジョン 122 〜 123 をマージ と表示されます。これを理解するには、マージは差分と密接に関連していることを思い出す必要があります。マージ処理は、リポジトリの 2 点間における差異の一覧を生成し、その差異を作業コピーに適用するというように動作します。進行ダイアログは、単純に差分の始点と終点を表示しているに過ぎません。

マージ結果のレビュー

さて、マージが完了しました。マージ結果を見て期待通りになっているかを確認するのがいいでしょう。通常マージはかなり複雑です。ブランチがトランクからかなりずれてしまえば、しばしば競合を引き起こします。

バージョン 1.5 未満の Subversion のクライアント・サーバは、マージ情報を格納しておらず、マージしたリビジョンは、手動で追跡しなければなりません。ある変更点のテストを行い、そのリビジョンをコミットする際に、マージで取り込んだリビジョン番号を 常に コミットログに記録するべきです。あとで別のマージを適用しようとしたときに、再度変更を取り込むことがないよう、すでにマージした内容を知る必要があります。これについては Subversion Book の Best Practices for Merging をご覧ください。

サーバとすべてのクライアントが Subversion 1.5 以上の場合、マージ追跡機構がマージしたリビジョンを記録し、再度マージすることがないようにします。これにより、単純にリビジョン範囲全体を指定し、実際に新しいリビジョンのみがマージされるといったことができるようになります。

ブランチ管理は重要です。このブランチをトランクに合わせて最新の状態を維持したければ、たびたびマージを確実に行わないと、ブランチとトランクがだんだん離れていってしまうのです。もちろん上で説明したように、変更点のマージを繰り返してしまうのは避けなければなりません。

ヒント

昨日ブランチからトランクへマージが完了すると、トランクには新機能のすべてのコードが含まれ、ブランチは必要なくなります。必要ならリポジトリから削除してかまいません。

重要

Subversion ファイルをフォルダとマージはできませんし、逆もまた同様です。ただフォルダとフォルダ、ファイルとファイルで行えます。ファイルをクリックしてマージダイアログを開いたら、ファイルのパスを与えなければなりません。フォルダを選択してダイアログを開いたらマージするフォルダの URL を指定しなければなりません。

マージ追跡

Subversion 1.5 は、マージ追跡機構を導入しました。あるツリーから別のツリーへ変更をマージすると、マージしたリビジョン番号を格納し、この情報を様々な異なる用途に利用します。

  • 再度同じリビジョンをマージする危険 (再マージ問題) を避けられます。一度マージ済みとマークされたリビジョンは、将来のマージで、マージ範囲にそのリビジョンが含まれていてもスキップします。

  • トランクにブランチをマージする際、トランクのログの一部として、ブランチのコミットもログダイアログに表示し、変更のトレーサビリティが向上します。

  • マージダイアログの中からログダイアログを表示すると、すでにマージしたリビジョンを灰色で表示します。

  • ファイルに対して注釈履歴を表示する際、マージした人ではなく、マージされたリビジョンのオリジナル作者を表示するよう選択できます。

  • マージされたリビジョンのリストにある、実際にはマージされていないリビジョンに対して、未マージ としてマークできます。

マージを行う際、クライアントはマージ追跡情報を、svn:mergeinfo に格納します。マージをコミットする際には、サーバはその情報をデータベースに格納し、マージやログ、注釈情報などのリクエストに適切に応答します。システムが適切に動作するには、サーバとリポジトリ、全クライアントを確実にアップグレードしなければなりません。以前のクライアントでは、svn:mergeinfo 属性を格納しませんし、以前のサーバでは、新しいクライアントが要求した情報を提供できません。

マージ追跡についての詳細は、Subversion の Merge tracking documentation にあります。

マージ中に発生した競合の扱い

マージが常にすんなり完了するとは限りません。時には競合を起こしますし、複数の範囲をマージしている場合は、一般的に、次の範囲のマージを行う前に、競合を解消しておきたいでしょう。TortoiseSVN は、マージ競合コールバック ダイアログを表示して、この処理の手助けをしてくれます。

図4.39 マージ競合コールバックダイアログ

マージ競合コールバックダイアログ


マージ中に競合が発生した場合、3 種類の選択肢があります。

  1. あなたの行った変更のほうが重要で、リポジトリのものを破棄して手元のバージョンを保持するという判断もあり得ます。もしくはリポジトリのものを支持し、自分のした変更を破棄することもできます。どちらにしろ、マージは一切行いません。どちらかを選ぶことになります。

  2. 通常は、競合の内容を調べ、解消することになります。この場合、競合の編集 を選択してマージツールを起動することになります。結果に満足したら、問題の解消 をクリックしてください。

  3. 最後の選択は、解消を先延ばしにして、マージを継続することです。現在競合したファイルと、残りのマージするファイル双方に対して、その選択ができます。しかし、そのファイルにまだ変更があると、マージは完了しないでしょう。

この対話的コールバックが必要なければ、マージ進行ダイアログに 非対話的なマージ チェックボックスがあります。マージ時にこれをセットし、マージ結果に競合がある場合、ファイルに競合マークが付き、マージを継続します。マージがすべて完了したあとで、競合を解決しなければなりません。セットしない場合は、競合マークがファイルに付く前に、マージ中に競合解決を行う機会があります。ファイルが複数のマージ (複数のリビジョンがそのファイルに変更を与える) を受ける場合、影響を受ける行によって、続くマージが成功するという利点があります。しかしもちろん、マージ実行中には、コーヒーを入れにそこを離れるわけにはいきません。

完全なブランチをマージ

機能ブランチの変更を、トランクにすべてマージして戻す場合、拡張コンテキストメニュー (Shift キーを押したままファイルを右クリック) にある TortoiseSVNマージの再統合... を利用できます。

図4.40 マージ再統合ダイアログ

マージ再統合ダイアログ


このダイアログは非常に簡単です。しなければならないことは、「マージオプション」 にあるように、マージオプションを設定することだけです。TortoiseSVN は、マージ追跡を利用して残りを自動的に処理します。

機能ブランチの保守

別々のブランチで新しい機能を開発する際、機能が完成した時の再統合指針を立てると良いと思います。別の作業がトランク (trunk) で同時に進んでいる場合、時間がたつにつれて差異は深刻になり、マージを行うのは悪夢のようになります。

機能が比較的単純で、開発に時間がかからなそうであれば、機能が完成するまで全体を分けたブランチを維持し、ブランチの変更をトランクにマージするという、単純なアプローチを採用できます。マージウィザードでは、これを単純に リビジョンの範囲をマージ で、リビジョン範囲にブランチのリビジョン期間を指定して行います。

その機能に時間がかかり、トランクに対して変更を説明する必要がある場合、ブランチの同期を維持する必要があります。これは、トランクの変更点 プラス 新機能をブランチが持つように、単純にトランクへの変更を定期的にブランチへマージするということです。同期プロセスでは リビジョンの範囲をマージ を用います。機能が完成したら、ブランチを再統合する異なる2つのツリーをマージ のどちらかを用いて、trunk にマージできます。