ソフトウェアを開発する場合が典型的な例ですが、バージョン管理で保守しているデータが しばしば誰かのほかのデータに密接に関係しているか、あるいは依存している ことがあります。一般的にプロジェクトで要求されることは、プロジェクトの 安定性を損なうことなく、外部の資源によって提供されるデータをできる限り 最新に保つことです。この考え方は、あるグループによって作られた情報が もう1つのグループによって作られるものに影響を与える場合、常に成り立ちます。
たとえば、ソフトウェア開発者がサードパーティー製のライブラリを利用する アプリケーションの開発に取り組んでいるとします。SubversionはApache ポータブル 実行時ライブラリと、ちょうどそのような関係を持っています。 (Apache Portable Runtime ライブラリ項参照)。Subversionのソースコードは すべての可搬性の要求を満たすために、APRライブラリに依存しています。 Subversionの開発の初期の段階では、プロジェクトはAPR のAPIの変更を非常に 正確に追いかけていました。常に、ライブラリコードの荒波の、「 最先端」についていきました。いまではAPRもSubversionも開発が落ち着い てきたので、Subversionはよくテストされ、安定したリリース状態にある バージョンのAPRライブラリAPIとのみ同期をとっています。
もしプロジェクトが他の人の情報に依存しているなら、その情報と自分の ものとを同期させるためのいくつかの方法があります。 一番大変な方法ですが、自分のプロジェクトのすべての貢献者に対して 口頭または文書で手続きを伝えることができます。プロジェクトに必要な サードパーティーの情報の特定のバージョンを確実に手に入れることを 伝えます。もしサードパーティーの情報がSubversionリポジトリで管理 されているなら、Subversionの外部定義を使ってその情報の特定のバージョンを 作業コピーディレクトリ中のある場所へ効果的に「結びつける」こと ができるでしょう (外部定義項参照)。
しかし、ときどき自分のバージョン管理システムでサードパーティーのデータ に加えた独自の変更を管理したいこともあります。ソフトウェア開発の 例に戻って説明すると、プログラマは自分自身の目的のために、サードパーティー のライブラリに変更を加える必要があるかも知れません。このような修正は 新しい機能の追加であったりバグフィックスであったりするかも知れませんが、 それはサードパーティーのライブラリの醐キコ�なリリースの一部になるまでに 限り管理すべきものです。あるいは、変更は決してライブラリ保守担当には 伝えられず、ソフトウェア開発者の特殊な要求に合うようなライブラリを 作り上げるための独自の修正点としてずっと残り続けるかも知れません。
ここで、面白い状況に直面します。あなたのプロジェクトは、パッチファイルを 適用したりファイルやディレクトリーを完全に別のものに置き換えるような、若干 バラバラな方法でサードパーティーのデータへ独自の修正を加えることができました。 しかし、このようなやり方ではすぐに保守する上で頭痛の種になるので、 あなたの独自の変更をサードパーティーのデータに適用する仕組みが必要となります。 さらにあなたが追跡するサードパーティーのデータのそれぞれの連続したバージョンに それらの変更を再生する仕組みも必要となります。
この問題に対する解決は、ベンダーブランチを 使うことです。ベンダーブランチはサードパーティーあるいはベンダー よって提供された情報を含んでいる、こちらのバージョン管理 システム中のディレクトリツリーです。それぞれの バージョンのベンダーのデータで、自分のプロジェクトに取り込もうと 考えているもののことを、ベンダードロップと いいます。
Vendor branches provide two benefits. First, by storing the currently supported vendor drop in your own version control system, the members of your project never need to question whether they have the right version of the vendor's data. They simply receive that correct version as part of their regular working copy updates. Secondly, because the data lives in your own Subversion repository, you can store your custom changes to it in-place—you have no more need of an automated (or worse, manual) method for swapping in your customizations.
ベンダーブランチの管理は一般的にはこんな感じでやります。 最上位ディレクトリを作り(/vendor
のようなもの) そこにベンダーのブランチを置きます。それから最上位ディレクトリの サブディレトクリにサードパーティーのコードをインポートします。 それからそのサブディレトクリを、適当な場所にある、自分の主系開発の ブランチにコピーします(たとえば/trunk
など)。 ローカルな変更は常に主系開発ブランチに対して行います。 追いかけているコードの新しいリリースのたびに、それをベンダーブランチに 持っていき、変更点を/trunk
にマージします。そして、 ローカルの変更と、ベンダーの変更の間の競合を解消します。
多分、例をあげると、このステップをはっきりさせることができるかも 知れません。あなたの開発チームがサードパーティーの複素数値計算 ライブラリ libcomplex を使った計算プログラムを作っているとします。 まず、ベンダーブランチの初期生成をし、それから最初のベンダードロップ をインポートします。ここではベンダーブランチのディレクトリを libcomplex
と呼び、私たちのコードドロップは current
と呼ばれる私たちのベンダーブランチの サブディレクトリの中に置かれます。svn importは 必要なすべての中間的な親ディレクトリを作るので、このような複数の ステップを実際には一つのコマンドで実行することができます。
$ svn import /path/to/libcomplex-1.0 ¥ http://svn.example.com/repos/vendor/libcomplex/current ¥ -m 'importing initial 1.0 vendor drop' …
これで、libcomplexのソースコードを/vendor/libcomplex/current
に持ってくることができました。このバージョンにタグ付けし、 (タグ項参照)、主系開発ブランチにコピーしま す。私たちのコピーは既存のcalc
プロジェクトディレクトリ 中のlibcomplex
という新しいディレクトリを作ります。 これが新たに独自の修正を加えるためのベンダーデータのコピーになります。
$ svn copy http://svn.example.com/repos/vendor/libcomplex/current ¥ http://svn.example.com/repos/vendor/libcomplex/1.0 ¥ -m 'tagging libcomplex-1.0' … $ svn copy http://svn.example.com/repos/vendor/libcomplex/1.0 ¥ http://svn.example.com/repos/calc/libcomplex ¥ -m 'bringing libcomplex-1.0 into the main branch' …
プロジェクトの主系のブランチをチェックアウトします。これは最初の ベンダードロップのコピーを含んでいます—そして、libcomplex コードの修正に入ります。もう知っているように、これで修正されたlibcomplex は、私たちの計算プログラムに完全に統合されています。 [25]
何週間かたって、libcomplexの開発者はライブラリの新しいバージョンを リリースしました—バージョン1.1としましょう—それは われわれがほしかったいくつかの機能と関数を含んでいます。この新しい バージョンにアップグレードしたいものですが、既に手元にあるバージョンに 対する修正を失いたくはありません。既に示唆したように、 本質的にわれわれがやらなくてはならないのは、libcomplex1.1 のコピー でlibcomplex1.0 を置き換え、前にやった独自の修正を、新しいライブラリ のバージョンにも再び適用することです。しかし実際には私たちはこの問題に 対して別の方法で対処したいのですが、それはバージョン1.0 と 1.1 の間 にlibcomplexにおきた変更点を私たちの修正されたコピー上に適用するという ものです。
To perform this upgrade, we check out a copy of our vendor branch, and replace the code in the current
directory with the new libcomplex 1.1 source code. We quite literally copy new files on top of existing files, perhaps exploding the libcomplex 1.1 release tarball atop our existing files and directories. The goal here is to make our current
directory contain only the libcomplex 1.1 code, and to ensure that all that code is under version control. Oh, and we want to do this with as little version control history disturbance as possible.
1.0 のコードを 1.1 のコードに置き換えた後では、 svn status はローカルな修正を加えたファイルの一覧と バージョン化されていないファイル、あるいは失われたファイルも表示 するでしょう。いままで述べたような手順を実行していたのなら、バージョン 化されていないファイルは libcomplex の 1.1 のリリースで新しく導入され たようなものだけのはずです—そのようなファイルをバージョン管理下 に置くのにsvn addを実行します。失われたファイルは 1.0 では存在していたが、1.1 では存在しないようなものに対応しているので svn deleteを実行します。最後に、私たちの current
作業コピーが libcomplex 1.1 のコードのみ を含むようになれば、いまの変更をコミットして、つじつまを合わせます。
私たちのcurrent
ブランチはこれで新しい ベンダードロップを含むようになります。新しいバージョンに(バージョン 1.0のベンダードロップに対して前にしたのと同じ方法で)タグづけをして、 それから前のバージョンのタグと新しい現在のバージョンとの間の差分を 私たちの開発ブランチにマージします。
$ cd working-copies/calc $ svn merge http://svn.example.com/repos/vendor/libcomplex/1.0 ¥ http://svn.example.com/repos/vendor/libcomplex/current ¥ libcomplex … # resolve all the conflicts between their changes and our changes $ svn commit -m 'merging libcomplex-1.1 into the main branch' …
簡単な場合だと、この新しいバージョンのサードパーティーツールは、 ファイルとディレクトリの観点から見ると、前のバージョンと同じように 見えます。libcomplex のどのソースファイルも削除されたり名称変更された り別の場所に移動されたりはしません—新しいバージョンは単に前の ものからテキストの内容の修正を受けただけに見えます。理想的な状況では 私たちの修正は新しいライブラリのバージョンに対してきれいに適用され、 複雑なことや、競合は一切起きません。
しかし、ものごとというものは常に単純であるとは限りません。 実際、ソースファイルはソフトウェアのリリース間であちこち 動くのが普通です。これはわたしたちの修正が新しいバージョンのコード でも正しいということを確認する作業を複雑にしますし、新しいバージョン での修正を手でもう一度やる必要がある状況に、簡単に落ち込んでしまう ことがあります。Subversionが、与えられたソースファイルの(以前の位置を含めての) 履歴について知っていればライブラリの新しいバージョンのマージのステップは とても単純になります。しかし、わたしたちは、Subversionに ソースファイルのレイアウトがベンダードロップ間でどんな風に 変わったかを教えてやる責任があります。
いくつかのファイルの削除、追加、移動があったベンダードロップは サードパーティーデータのアップグレードの手順を複雑にします。 それでSubversionはこの手続きを支援するために svn_load_dirs.plスクリプトを用意しています。 このスクリプトは一般的なベンダーブランチの管理手続きで言ったような インポートのステップを自動化し、間違いを最小にすることができます。 サードパーティーデータの新しいバージョンを主系開発ブランチにマージする ためのマージコマンドを使う責任はまだ残っているものの、 svn_load_dirs.plはより早く簡単にこの処理まで 到達する助けになります。
簡単に言って、svn_load_dirs.pl は svn import の拡張で、いくつかの重要な 特徴を持っています:
いつでも、このプログラムを実行して、 リポジトリにあるディレクトリを、完全にそれに一致した外部 ディレクトリに持って行き、必要なすべての追加、削除を実行し、 さらにオプションで移動処理も行います。
このプログラムは、Subversionが必要とする中間的なコミット間で 必要な複雑な一連の処理を注意深く実行します—たとえば ファイルやディレクトリの名称変更を二回やる前など。
それは、オプションで新しいインポートされたディレクトリを タグ付けします。
それはオプションで、正規表現にマッチするファイルとディレクトリ に任意の属性を追加します。
svn_load_dirs.pl は三つの必須パラメータを とります。最初の引数は作業対象となるベースになるSubversionディレクトリのURL です。この引数のあとにはURLが続きます—最初の引数に相対的な 形で—ベンダードロップはそこにインポートされます。最後に 三番目の引数はインポートするローカルディレクトリです。前の例を 使うと、典型的なsvn_load_dirs.plの実行は こんな感じになります:
$ svn_load_dirs.pl http://svn.example.com/repos/vendor/libcomplex ¥ current ¥ /path/to/libcomplex-1.1 …
-t
オプションにタグ名称を指定して、新しいベンダードロップ をタグ付けするようにsvn_load_dirs.pl に指示することが できます。
$ svn_load_dirs.pl -t libcomplex-1.1 ¥ http://svn.example.com/repos/vendor/libcomplex ¥ current ¥ /path/to/libcomplex-1.1 …
When you run svn_load_dirs.pl, it examines the contents of your existing 「current」 vendor drop, and compares them with the proposed new vendor drop. In the trivial case, there will be no files that are in one version and not the other, and the script will perform the new import without incident. If, however, there are discrepancies in the file layouts between versions, svn_load_dirs.pl will ask you how to resolve those differences. For example, you will have the opportunity to tell the script that you know that the file math.c
in version 1.0 of libcomplex was renamed to arithmetic.c
in libcomplex 1.1. Any discrepancies not explained by moves are treated as regular additions and deletions.
このスクリプトはまたリポジトリに追加される (正規表現にマッチするような)ファイルとディレクトリの属性を設定する ために、別の設定ファイルを受け付けることができます。 この設定ファイルはsvn_load_dirs.pl で -p
オプションを使って指定されます。 設定ファイルの各行は空白で区切られた二つまたは四つの値です: 追加されるパスに対してマッチさせるPerlスタイルの正規表現、 制御キーワード(break
または cont
)、そして、オプションで属性名と 属性値がきます。
\.png$ break svn:mime-type image/png \.jpe?g$ break svn:mime-type image/jpeg \.m3u$ cont svn:mime-type audio/x-mpegurl \.m3u$ break svn:eol-style LF .* break svn:eol-style native
追加されるパスが正規表現にマッチしたとき、その行の属性 がマッチしたパスに追加されていきます。ただし制御の指定がbreak
の場合は属性の追加はその行で打ち止めになります(これはそれ以上の属性変更はこのパスに 行わないことを意味しています)。もし制御指定がcont
—continue
の省略形ですが—の場合は マッチング処理は設定ファイルの次の行に続いていきます。
正規表現中のすべての空白、属性名、属性値はシングルまたはダブルクォート でくくる必要があります。空白を囲むために利用しているわけではないクォート文字はバックスラッシュ文字(¥
) を前に付ける ことでエスケープできます。バックスラッシュは設定ファイルを解析するとき だけクォートするので、正規表現中で必要なもの以外のほかの文字には 使わないでください。