競合の解決

リポジトリから自分のファイルを更新・マージしたり、別の URL に作業コピーを切り替えたりすると、時には 競合 することもあります。競合には以下の二種類があります。

ファイルの競合

複数の開発者が、同じファイルの同じ行を変更すると、ファイルの競合が発生します。

ツリーの競合

開発者がファイルやフォルダーの移動・名前変更・削除を行い、別の開発者も移動・名前変更・削除を行ったり、単に変更すると、ツリーの競合が発生します。

ファイルの競合

ファイルの競合は、複数の開発者があるファイルの同じ行を変更した際に発生します。 Subversion はプロジェクトの事情を何も知らないため、開発者間の競合については解決できません。テキストファイルの場合、競合している箇所は以下のようなマークがつけられます。

<<<<<<< filename
    your changes
=======
    code merged from repository
>>>>>>> revision

また、競合しているファイルについては、3つのファイルが Subversion によって追加されます。

filename.ext.mine

作業コピーを更新する前に、作業コピーに存在していた(競合の印がついていない)ファイルです。このファイルには最新の変更のみが含まれています。

filename.ext.rOLDREV(OLDREV=旧リビジョン番号)

作業コピーを更新する前(BASE リビジョン)のファイルです。最新の編集を行う前にチェックアウトした時のファイルです。

filename.ext.rNEWREV(NEWREV=新リビジョン番号)

作業コピーを更新したときに Subversion クライアントが受信したファイルです。リポジトリの最新(HEAD リビジョン)に相当します。

TortoiseSVN競合の編集 で外部マージツールや競合エディターを起動するか、テキストエディターを使用して手作業で競合を解決するかしてください。コードがどうあるべきか決定し、必要な変更をしてから保存してください。TortoiseMergeやその他の有名なマージツールを使用した方が、3画面表示でこれらのファイルを確認でき、競合マーカーを気にする必要がないため、より簡単でしょう。テキストエディターを使用する場合は、 <<<<<<< という文字列で始まる行を検索してください。

その後、TortoiseSVN競合の解決コマンドを実行し、リポジトリに変更をコミットしてください。なお、競合の解決コマンドは、実際に競合を解決する訳ではありません。変更をコミットできるようにfilename.ext.minefilename.ext.r*ファイルを削除するだけです。

バイナリファイルが競合した場合、 Subversion はそのファイルをマージしようとしません。ローカルファイルには変更が加えられず(自分が変更したままで)、filename.ext.r*ファイルを作られます。自分の変更を取り消し、リポジトリのバージョンにしたい場合は、「変更の取り消し」コマンドを使ってください。リポジトリのバージョンを自分の変更したファイルで置き換えるのなら、「競合の解決」コマンドを実行してからコミットしてください。

親フォルダーを右クリックし、

プロパティの競合

複数の開発者が同じプロパティを変更した場合、プロパティの競合が発生します。ファイルの内容と同様に、競合は開発者のみが解決できます。

一方の変更を取り消してもう一方で上書きする場合は、ローカルのプロパティを用いて解決するリモートのプロパティを用いて解決するを選択してください。変更をマージする必要がある場合は、手作業でプロパティを編集を選択し、どのプロパティをどうするか決めてから、解決してください。

ツリーの競合

開発者がファイルやフォルダーを移動・名前変更・削除したときに、別の開発者が移動・名前変更・削除を行っていたり、変更をしていたりすると、ツリーの競合が発生します。ツリーの競合はさまざまな状況で起こり得ますし、その競合を解決するにはそれぞれ異なった手順が必要です。

また、ファイルが Subversion によりローカルで削除されると、ローカルファイルシステムからも削除されるため、ツリーの競合があったとしても競合のオーバーレイを表示できず、競合を解決しようにも右クリックもできません。 競合の編集 オプションを用いる代わりに、 変更をチェック ダイアログを使用してください。

TortoiseSVN は変更をマージするための正しい場所を見つけるのを支援することができますが、競合を整理するにはさらに作業が必要かもしれません。作業ベースを更新すると、常に更新時のリポジトリにある各項目のリビジョンが含まれます。更新後に変更を取り消した場合、そのリポジトリの状態へと戻り、変更開始時の状態には戻りません。

ローカルで削除、更新により編集を受信

  1. 開発者Aが Foo.c を変更し、リポジトリにコミットする。

  2. 開発者Bは同時に、作業コピーで Foo.cBar.c に移動したか、単に Foo.c またはその親フォルダ―を削除した。

開発者Bの作業コピーを更新した結果、次のようにツリーの競合が発生します。

  • Foo.c が作業コピーからすでに削除されていますが、ツリーの競合としてマークされます。

  • 削除ではなく名前変更の結果が競合した場合、Bar.cは追加としてマークされますが、開発者Aの変更点は含まれていません。

開発者Bは、現在、開発者Aの変更を保持するかどうかを選ばなければなりません。ファイルの名前変更の場合では、名前変更されたファイル Bar.c に対して、 Foo.c に行った変更をマージできます。単なるファイルやディレクトリの削除の場合には、開発者Aの変更を保持し、削除を取り消すという選択もできます。もしくは、何もせずに競合を解決したとみなし、事実上開発者Aの変更を破棄することもできます。

Bar.c に名前を変更する前のファイルが見つかれば、競合の編集ダイアログで変更をマージするかどうか尋ねてきます。複数のファイルが移動元である可能性がある場合は、それぞれのファイルのボタンが表示され、正しいファイルを選べます。

ローカルで編集、更新により削除を受信

  1. 開発者Aが Foo.cBar.c に移動し、リポジトリにコミットします。

  2. 開発者Bは Foo.c を、自分の作業コピーで変更します。

またはフォルダーの移動の場合...

  1. 開発者Aは、親フォルダー FooFolderBarFolder に移動し、リポジトリへコミットします。

  2. 開発者Bは Foo.c を、自分の作業コピーで変更します。

開発者Bの作業コピーを更新した結果、ツリーが競合しました。単純なファイルの競合では次のようになります。

  • Bar.c は作業コピーに通常ファイルとして追加されます。

  • Foo.c は(履歴と共に)追加されたとマークされ、ツリーが競合状態になります。

フォルダーの競合では次のようになります。

  • BarFolder は作業コピーに、通常のフォルダーとして追加されます。

  • FooFolder は(履歴と共に)追加されたとマークされ、ツリーが競合状態になります。

    Foo.c は変更されたとマークされます。

開発者Bは、開発者Aの再配置を受け入れて変更を移動先のファイルにマージするか、開発者Aの変更を取り消してローカルのファイルを保持するかを決めなければならなくなります。

彼女の移動を伴うローカルの変更をマージするには、開発者Bは競合ファイルFoo.cがリポジトリで何というファイル名に名前変更または移動されたかを調べなければなりません。これはマージ元のダイアログで行うことができます。それから、正しいソースを表すボタンを使用して、競合を解決します。

開発者Aの変更は誤りだと開発者Bが判断した場合は、競合の編集ダイアログの解決済みとするボタンを選択します。これは競合したファイルやフォルダーを、解決済みとしてマークしますが、開発者Aの変更は手作業で削除する必要があります。何が移動されたかを見つけ出すには、またログダイアログが役に立ちます。

ローカルで削除、更新により削除を受信

  1. 開発者Aが Foo.cBar.c に移動し、リポジトリにコミットします。

  2. 開発者Bが Foo.cBix.c に移動します。

開発者Bの作業コピーを更新した結果、次のようにツリーの競合が発生します。

  • Bix.c は、履歴と共に、追加されたとマークされます。

  • Bar.c は、作業コピーへ「通常」の状態で追加されます。

  • Foo.c は、削除されたとマークされ、ツリーが競合状態になります。

競合を解決するには、 Foo.c がリポジトリで名前変更・移動されたことに対する競合したファイルの名前を、開発者Bが調べなければなりません。これはログダイアログで行えます。

開発者Bが、 Foo.c の新しい名前の方を残すと決めた場合、開発者Aにしてもらうか、自分で名前変更できます。

開発者Bが、手作業で競合を解決した後で、競合の編集ダイアログのボタンで、ツリーの競合を解決済みにする必要があります。

ローカルで紛失、マージにより編集を受信

  1. トランクで作業している開発者Aが、 Foo.c を変更しリポジトリにコミットします。

  2. ブランチで作業している開発者Bが、 Foo.cBar.c に移動し、リポジトリにコミットします。

開発者Aのトランクへの変更を、開発者Bのブランチにマージすると、作業コピーは以下のようにツリーの競合状態になります。

  • Bar.c は、状態が「通常」で、すでに作業コピーにあります。

  • Foo.c は、紛失マークがつき、ツリーの競合状態になります。

この競合を解決するには、開発者Bが競合の解決ダイアログでファイルに解決済みにし、競合リストから取り除く必要があります。さらに、紛失ファイル Foo.c を、リポジトリから作業コピーへコピーするか、 Foo.c へ行った開発者Aの変更を、名前変更した Bar.c にマージするか、競合に解決マークを付ける他は何もしないかを決めなければなりません。

紛失したファイルをリポジトリから取得し、それから解決済みとすると、自分のコピーが再度削除されます。まず競合を解決しなければなりません。

ローカルで編集、マージにより削除を受信

  1. トランクで作業している開発者Aが、Foo.cBar.c に移動し、リポジトリにコミットします。

  2. ブランチで作業している開発者Bが、 Foo.c を変更しリポジトリにコミットします。

  1. トランクで作業している開発者Aが、親フォルダー FooFolderBarFolder に移動し、リポジトリにコミットします。

  2. ブランチで作業している開発者Bが、自分の作業コピーにある Foo.c を変更します。

開発者Aのトランクへの変更を、開発者Bのブランチにマージすると、作業コピーは以下のようにツリーの競合状態になります。

  • Bar.c には追加マークが付きます。

  • Foo.c はツリーの競合として、変更マークが付きます。

開発者Bは、開発者Aの再配置を受け入れて変更を移動先のファイルにマージするか、開発者Aの変更を取り消してローカルのファイルを保持するかを決めなければならなくなります。

再配置された結果にローカルの変更をマージするためには、開発者Bはまず、競合したファイル Foo.c がリポジトリ中でどのようなファイル名に名前変更・移動されたのかを探さなければなりません。マージ元のログダイアログを使用すると調べることができます。競合エディターは、どのパスがマージで使用されたのかを知らず、作業コピーのログを表示するだけですので、自分で探さなければなりません。現在のところ、この手順を自動化したり、単純化したりする方法がないからです。いったん変更を移植してしまえば、競合したパスは余分なものとなり、削除できます。

開発者Bが、開発者Aの変更は誤りだと判断した場合、競合の編集ダイアログの 解決済みとする ボタンを選択しなければなりません。これは 競合したファイルやフォルダーを、解決済みとしてマークしますが、開発者Aの変更は手で削除する必要があります。何が移動されたかを見つけ出すには、またマージソースのログダイアログが役に立ちます。

ローカルで削除、マージにより削除を受信

  1. トランクで作業している開発者Aが、Foo.cBar.c に移動し、リポジトリにコミットします。

  2. ブランチで作業している開発者Bが、Foo.cBix.c に移動し、リポジトリにコミットします。

開発者Aのトランクへの変更を、開発者Bのブランチにマージすると、作業コピーは以下のようにツリーの競合状態になります。

  • Bix.c は通常(未変更)状態としてマークされます。

  • Bar.c は履歴と共に追加マークが付きます。

  • Foo.c には紛失マークが付き、ツリーの競合状態となります。

競合を解決するには、 Foo.c がリポジトリで名前変更・移動されたことに対する競合したファイルの名前を、開発者Bが調べなければなりません。これはマージ元のログダイアログで行えます。

開発者Bが、 Foo.c の新しい名前の方を残すと決めた場合、開発者Aにしてもらうか、自分で名前変更できます。

開発者Bが、手作業で競合を解決した後で、競合の編集ダイアログのボタンで、ツリーの競合を解決済みにする必要があります。

他ツリーの競合

他にも、競合にファイルではなくフォルダーが含まれるために、単純にツリーの競合としてマークされている場合があります。例えば、トランクとブランチの両方に同じ名前のフォルダーを追加してからマージしようとした場合には、ツリーの競合と表示されます。マージ対象のフォルダーを維持したい場合は、単に解決済みとしてください。マージ元のどちらか一方を使用する場合は、最初のターゲットの1つに対してSVN 削除を実行し、再びマージを実行する必要があります。もっと複雑な場合は、手作業で解決する必要があります。