第2章 Subversionによるバージョン管理入門
Authors:YOSHIHARA Hidehiko
ここからは、作業コピーAと作業コピーBを題材に、Subversionのチーム開発に関連する操作を見ていきます。
まず準備として、通常の並行開発に見立てて、作業コピーBのほうにも変更を加えておきましょう。
作業コピーBのhoge.txt、hoge_conflict.txtを以下のように変更してください。
【hoge.txt】
a
B
C
【hoge_conflict.txt】
a
B
E
先ほど作業コピーAでコミットした内容は、まだ作業コピーBには反映されていません。リポジトリの最新状態と同期を取るために「更新」(アップデート)と呼ばれる作業が必要になります。
更新を行う作業コピーのディレクトリ(C:\work\2\B)に移動し、svn updateコマンドでリポジトリと同期化を行います。
●【更新】
svn update
C:\ work\ 2\ B> svn update
D hoge_delete.txt
D hoge_move.txt
G hoge.txt
C hoge_conflict.txt
A add_dir
A add_dir\ hoge_add.txt
A hoge_move2.txt
A hoge_copy2.txt
リビジョン3に更新しました。
行頭の記号の意味を、表1にまとめました。
表1 ステータス
| 記号 |
意味 |
| A |
作業コピーに新しいファイルが追加された |
| D |
作業コピーからファイルが削除された |
| U |
作業コピーには何も手を加えていない状態でリポジトリから変更が行われた |
| G |
作業コピーで変更を加えているファイルに対して、リポジトリから衝突が発生せずに変更が行われた |
| C |
作業コピーで変更を加えているファイルに対して、リポジトリから変更が行われた際に衝突が発生した |
※「UU」や「U」のようなログ表示されることがあるが、これらはプロパティに対して行われた変更を表している。プロパティについては今回は取り上げない。
作業コピーBの内容を確認してみてください。作業コピーAで行った変更が反映されていますね。
hoge.txtの内容も、次のように、上書きではなく差分が反映されていることを確認してください。(hoge_conflict.txtについては後述します)。
a ,...... 作業コピーBで行った変更
b ,...... 作業コピーAで行った変更
c ,...... 作業コピーAで行った変更
このようにSubversionでは、リポジトリを介して
作業コピー間の同期を行い並行開発作業を進めてい
きます(図4)。
Subversionの更新機能を使用すると、リポジトリ間の同期処理を自動的に行ってくれますが、中にはSubversionでも解決できない同期処理が発生することがあります。
先ほどの更新処理時に、hoge_conflict.txtに対しては何か問題が発生したことを示す「C」というログが出力されました。作業コピーBのhoge_conflict.txtを見てみましょう。
a ,..... 作業コピーBで行った変更
<<\<\<\ .mine
B ,..... 作業コピーBで行った変更
E ,..... 作業コピーBで行った変更
=======
b ,..... 作業コピーAで行った変更
c ,..... 作業コピーAで行った変更
>>>>>>> .r3
どうやら、作業コピーAと作業コピーB で、同じファイルの同じ行に対して変更を行ったことが問題のようです。このような状況をコンフリクト(衝突)が発生したと呼びます。コンフリクトの発生は、並行作業を行う上ではしかたがないことなのです。 Subversionはコンフリクトを検出した際、コンフリクトが発生したファイルに、前記のような衝突場所を示す特殊なマーク付けを行います。
また、同時に、次の3つのファイルを作成します。
- filename.mine
ここではhoge_conflict.txt.mine。更新を行う前に作業コピーにあったファイル。このファイルには作業コピーで行った最後の変更が含まれている
- filename.rOLDREV
ここではhoge_conflict.txt.r1。作業コピーを変更する直前のリビジョン(チェックアウトもしくはコミット時)のファイル
- filename.rNEWREV
ここではhoge_conflict.txt.r3。作業コピーを更新したときにリビジョンから受け取ったファイル。リポジトリのHEADリビジョンに対応している。
コンフリクトが発生したままではSubversionはコミットを行うことを許してくれません。手修正でコンフリクトが発生したファイルを修正します。今回は次のように修正しました。
a
B
E
c
次にコンフリクトが解決したことをSubversionに教えてあげます。この操作を「コンフリクトの解決」と呼びます。コンフリクトが発生した作業コピーのディレクトリ(C:\work\2\B)に移動して、svn resolvedコマンドでコンフリクトの解決を行います。
●コンフリクトの解決
svn resolved PATH
PATH:対象のファイル(ディレクトリ)
C:\work\2\B> svn resolved hoge_conflict.txt
'hoge_conflict.txt' の競合状態を解消しました
コンフリクトの解決を行うと、コンフリクト発生時に作成された3つのファイルは削除されて、コミットができるようになります。
C:\ work\ 2\ B> svn commit -m "作業コピーBで行った変更"
送信していますhoge.txt
送信していますhoge_conflict.txt
ファイルのデータを送信中です..
リビジョン4 をコミットしました。
これまでの説明で、Subversionはリポジトリを介して作業コピー間の整合性を保つことで並行作業を可能にしていることを説明してきました。しかし、作業コピー間で整合性が保たれることを望まない場合もあります。たとえば、ほかのリリース版に必要のないリース先固有の変更や、今必要ではない新しい機能や不確定なアルゴリズムを試したい場合などです。
このようなとき、Subversionでは、「ブランチ」と呼ばれる機能を使用して、枝分かれした作業領域を作成できます。ブランチの作成方法は非常に簡単で、branchesに作業領域(ここではtrunk)のコピーを作成するだけです。コピー先が違うだけで、やり方はタグの作成と同じです。それでは、リリース用のブランチ「RELB-1.0.x」を作成してみましょう。
C:\ work\ 2\ A> svn copy file:///C:/webdb39/2/example1/
trunk file:///C:/webdb39/2/example1/branches/RELB-1
.0.x -m "1.0リリースブランチ" (実際は1行)
リビジョン5 をコミットしました。
試しに作成したブランチRELB-1.0.xをチェックアウトして、変更を加えコミットしてみてください。
trunkに対しては影響がないことが確認できます。これで、ほかの開発者のことを考える煩わし
さから解放されましたね。恐れることなく開発を続けましょう。
■タグ、ブランチ、trunkの使い分け方の整理
Subverionの管理下では、タグとブランチは、共にコピーの作成を行うだけで機能としての区別はありません。タグとブランチの違いは、ポリシーの違いです。
タグは、あるリビジョンの状態を保持するためにコピーを作成するので作成したコピーに対して変更を行いません。それに対してブランチは、隔離した作業領域を望んでいるため、作成したコピーに対して継続して変更が行われます。
Subversionでは、次のようにリポジトリのディレクトリを使い分けることを推奨しています。
- trunk :主な開発ラインを管理するディレクトリ
- tags :変更を加えることが許されないコピー(タグ)を管理するディレクトリ
- branches :変更を加えることができるコピー(ブランチ)を管理するディレクトリ
ブランチでのバグフィックスなどの修正を、trunkや他のブランチに反映させたいことがあります。このような作業を「マージ」と呼びます。
マージと聞くと2つのブランチを統合するような難しいイメージを持ってしまいますが、Subversion のマージは意外とシンプルです。Subversionのマージは、図5のようにマージを行いたいブランチのリビジョン間の差分が作業コピーに適用されると考えてください。
それではマージ作業を行ってみましょう。図5のような状態を作成したリポジトリをfile:///C:/webdb39/2/example2/に用意していますので、これを利用して行います。
今回は、「ブランチからtrunkへマージ」、「trunkからブランチへマージ」の2つのパターンを取り上げます(図6)。
trunkへのマージは、新機能開発用ブランチで作成した新機能をtrunkに盛り込む場合など、ブランチで行った変更をtrunkに反映する必要がある場合に行われます。trunkからブランチへのマージは、trunkで行われた修正をリリース用に作成したブランチにも加える場合などで行われます。両パターンとも、並行開発でよく行われるマージです。
■ブランチからtrunkへマージ
マージを行ううえで必要な情報は、差分を取得する開始リビジョンと終了リビジョン(=差分情報)、その差分を適用する作業コピーの3つです。
まずは差分を適用する作業コピーをチェックアウトしましょう。ブランチからtrunkへのマージでは、マージ先であるtrunkを作業コピーとして使用します。
C:\work\2\BtoTにチェックアウトしてください。
C:\work\2\BtoT> svn checkout file:///C:/webdb39/2/ex
ample2/trunk ./ (実際は1行)
A hoge.txt
リビジョン5 をチェックアウトしました。
次に差分情報ですが、ブランチからtrunkへのマージでは、「ブランチを作成したリビジョン」から「ブランチの最新リビジョン」までの差分情報が必要です。今回のサンプルでは、「RELB-1.0.0のリビジョン2」から「RELB-1.0.0のリビジョン5」までです。
差分情報の参照は、svn diff コマンドで行えます。
●差分情報の参照
svn diff URL1 @rev1 URL2 @rev2
URL1:差分取得先URL
rev1:差分取得開始リビジョン
URL2:差分取得先URL
rev2:差分取得終了リビジョン
C:\ work\ 2\ BtoT> svn diff file:///C:/webdb39/2/examp
le2/branches/RELB-1.0.0@2 file:///C:/webdb39/2/exam
ple2/branches/RELB-1.0.0@5 (実際は1行)
Index: hoge.txt
=================================================
--- hoge.txt (revision 2)
+++ hoge.txt (revision 5)
B 本文: A
-B ,......... 2行目を削除
-C ,......... 3行目を削除
+c ,......... cを追加(1行追加)
+D ,......... Dを追加(1行追加)
この差分情報を先の作業コピーに適用すると、
hoge.txtの中身は次のようになります。
a
c
D
それでは、svn mergeコマンドで実際にマージを行ってみましょう。
●マージ
svn merge URL1 @rev1 URL2 @rev2 WORK_PATH
URL1:マージ用の差分取得先URL
rev1:マージ用の差分取得開始リビジョン
URL2:マージ用の差分取得先URL
rev2:マージ用の差分取得終了リビジョン
WORK_PATH:差分を適用する作業コピーのディレクトリパス(省略時はカレントディレクトリが対象)
C:\ work\ 2\ BtoT> svn merge file:///C:/webdb39/2/example2/branches/RELB-1.0.0@2 file:///C:/webdb39/2/exa
mple2/branches/RELB-1.0.0@5 (実際は1行)
U hoge.txt
■trunkからブランチへマージ
同様に、trunkからブランチのマージも行ってみましょう。
- 差分を適用する作業コピー
RELB-1.0.0(C:\work\2\TtoBにチェックアウト)
- 差分を取得する開始リビジョン
trunkのリビジョン2
- 差分を取得する終了リビジョン
trunkのリビジョン4
C:\ work\ 2\ TtoB> svn checkout file:///C:/webdb39/2/example2/branches/RELB-1.0.0 ./ (実際は1行)
A hoge.txt
リビジョン5 をチェックアウトしました。
C:\ work\ 2\ TtoB> svn merge file:///C:/webdb39/2/examp
le2/trunk@2 file:///C:/webdb39/2/example2/trunk@4(実際は1行)
U hoge.txt
マージ結果(マージ後のhoge.txtの中身)は、ブランチからtrunkへのマージと同じです。
■同様のマージを繰り返す場合の注意
マージを行ったあとに再度同様のマージを繰り返す必要がある場合は、差分を取得するために指定す
る開始リビジョン番号の扱いに注意が必要です。たとえば、RELB-1.0.0からtrunkにマージしたあと、RELB-1.0.0に対して変更を行い再度trunkにマージする場合は、開始リビジョンには、RELB-1.0.0の前回マージ時の終了リビジョンを指定してください。
前回同様にRELB-1.0.0作成時のリビジョンを指定すると、同じ内容が重複することになり正しくマージされなくなります。
■コンフリクト
マージの場合も更新と同じくコンフリクトが発生することがあります。
コンフリクト時に作成されるファイル名が異なるだけで、解決方法は更新と同じです。