集中型ワークフローのチュートリアル

概要

この文書では、 Bazaar を使用することで実現可能なワークフローについて説明します。 つまり、分散バージョンコントロールシステムである Bazaar を、集中型のやり方で使用するためのワークフローです。 Bazaar は、非常にフレキシブルに設計されており、完全な分散型からほとんど集中型のワークフローまで、いくつかの異なるワークフローを受け入れることができます。 このワークフローは、初めてのユーザでも簡単に Bazaar を使いこなすことができ、分散型と集中型のオペレーションを組み合わせた業務にも対応できるようになっています。

基本的に、この文書はCVSや Subversion のような集中型バージョンコントロールシステムの経験があるユーザ向けに書かれています。 これらの環境では、一般的に、コードベースをホストする単一の中央サーバが存在し、複数の人々がこのコードベース上で作業して、お互いの作業の同期をとることになります。 また、このワークフローは、一人の開発者が複数のマシーン上で作業をする場合にも適用できます。

初期セットアップ

Bazaar がうまく動くようにするための、まあまあ簡単なセットアップ手順があります。

ユーザのEメールの設定

ユーザIDは各コミットに保存されます。これは正確だったり一意だったりする必要はありませんが、ログメッセージや注釈で使用されるため、何かしら実在する値を設定しておいた方がよいでしょう。

% bzr whoami "John Doe <jdoe@organization.com>"

ローカルリポジトリのセットアップ

Bazaar のブランチは、通常は履歴のコピーを持っています。そのおかげで分散型での作業ができるわけです。 最適化の方法の一つとして、関連するブランチ同士の情報を統合して、新しいブランチを作るたびに履歴情報を丸ごとコピーしなくてもいいようにすることができます。

そのための一番いい方法は、 共用リポジトリ を作成することです。 一般に、 共用リポジトリ のサブディレクトリ内に複数のブランチがある場合、お互いの記憶領域を共有します。 ですので、ホームディレクトリに 共用リポジトリ を作成するようにしましょう。 そうすれば、その下に作成したすべてのブランチは、履歴情報の領域を共有することになります。

% bzr init-repo --trees ~

リモートリポジトリのセットアップ

作業用の領域とは別に、データを蓄積しておく領域が欲しいというのはよくあることです。 集中型のシステム(CVS/SVN)では、このようなワークフローが必要になります。 たいていは、これらは別々のマシーンに分けて配置されます。(そうでないこともあります。) 実際のところ、これは、特に職場ではとてもよい設定です。 蓄積されたデータはバックアップを確実にとることができ、開発者のマシーンに何かトラブルが起こってもコミットされたデータはけっして無くなりません。

それでは、プロジェクト内で共有する centralhost というマシーンを用意しましょう。 先ほども言いましたが、ディスクの使用量を節約するために、 共用リポジトリ を使用します。

% bzr init-repo --no-trees bzr+ssh://centralhost/srv/bzr/

この手順は、新しくcvsrootやSubversionのリポジトリを作るのと同じようなものだと考えることができます。 --no-tree オプションの指定によって、ワーキングツリーを作らないようになります。 中央リポジトリ内のブランチを直接変更することは無いので、このオプションを指定しておくとよいでしょう。

ここで、 bzr+ssh というURLを使っていますが、これは、 SSH セキュアシェ ル上の Bazaar 独自プロトコルを意味しています。 bzr+ssh サーバーのセットアップに関 する 情報に着いては、管理者向けガイドを参照してください。

既存のプロジェクトからの移行

リポジトリができましたので、プロジェクトの作成にかかりましょう。 たいていの場合、 Bazaar でバージョン管理したい作業中のコードがすでにあるはずです。 もし、そのコードがもともとソース管理されていたのであれば、履歴の情報を保ったまま Bazaar に変換する方法がたくさんあります。 しかしながら、それについてはここでは説明しません。詳しくは、 Tracking Upstream の”Converting and keeping history”セクションを見てください。

Developer 1: 最初のリビジョンを作成する

まず最初に、プロジェクトをホストするリモートリポジトリに、ブランチを作成したいと思います。 仮に、”sigil”というプロジェクトをバージョン管理しようとしているとしましょう。

% bzr init bzr+ssh://centralhost/srv/bzr/sigil

これは、CVSで言うところの”HEAD”ブランチ、Subversionなら”trunk”にあたるものだと考えてかまいません。 これを dev ブランチと呼ぶことにしましょう。

他のファイルとの競合を避けるために、ホームディレクトリのサブディレクトリ内で作業するのが私の好みです。 同じように、最終的にでき上がるすべてのブランチを格納できるプロジェクトディレクトリも欲しいですね。

% cd ~
% mkdir work
% cd work
% mkdir sigil
% cd sigil
% bzr checkout bzr+ssh://centralhost/srv/bzr/sigil dev
% cd dev
% cp -ar ~/sigil/* .
% bzr add
% bzr commit -m "Initial import of Sigil"

前のセクションでは、空のブランチ(sigil ブランチ)を centralhost に作成して、それをクライアント端末にチェックアウトしたあと、元々あったプロジェクトのファイルを追加しています。 作業ディレクトリをセットアップするための方法はたくさんありますが、この方法を使うと機能追加用/バグフィックス用のブランチを使った作業が簡単にできます。 そして、複数のブランチをとてもうまく扱えるというのが、 Bazaar の長所の一つなのです。

この場合、リモートブランチのチェックアウトを持っているので、 ~/work/sigil/dev/ に対してコミットした内容が、ローカルと centralhost の両方に自動的に保存される訳です。

Developer N: プロジェクトの作業コピーを取得する

プロジェクトの作成に関するすべての作業は1人目の開発者がしてしまっているので、他のみんなは単にそのブランチをチェックアウトするだけです。 ただし、 ユーザのEメールの設定 ローカルリポジトリのセットアップ は見ておいてください。

現在開発中のツリーのコピーを取得するためには:

% cd ~/work/sigil
% bzr checkout bzr+ssh://centralhost/srv/bzr/sigil dev

今、二人の人が bzr+ssh://centralhost/srv/bzr/sigil のチェックアウトを持っている状態なので、片方のチェックアウトが最新のものより古くなってしまうタイミングが出てきます。 コミットの時に、チェックアウトが古いければ Bazaar がエラーを返して、コミットは失敗します。 チェックアウトを最新にするには、よそで変更されたツリーに対して bzr update を使用します。 この時、もし同じファイルが変更されていれば、競合の解決が必要になるかもしれません。

別ブランチでの開発

ここまでは、全員が同じブランチで作業して、変更をコミットしていました。 これはつまり、全員が適正かつ定期的にアップデートを行い、他の人の変更を取り扱う必要があるということです。 また、誰か一人が何かコードを壊すような変更をコミットして、それが同期されれば、全員が問題を抱えることになります。

たいていの場合、別のブランチ上で開発を行い、コードが安定してからメインのブランチに統合するというやり方の方が優れています。これが、CVSやSVNとの一番大きな違いです。 CVSやSVNも別ブランチでの開発はできますが、マージのアルゴリズムがかなり貧弱なので、ブランチ間できちんと同期がとれた状態に保つのは難しいことです。 Bazaar は、何がマージ済みかを記憶し、たとえファイルが変名されてる場合でもちゃんと変更を適用することができます。

新しいブランチを作成してそこで作業する

まだ変更が完了していない場合でも、他の人がその変更内容にアクセスできるようにしておきたいですよね。 そこで、 centralhost 上に新しい公開ブランチを作成して、それを手元に持ってくることにしましょう。

% cd ~/work/sigil
% bzr branch bzr+ssh://centralhost/srv/bzr/sigil \
             bzr+ssh://centralhost/srv/bzr/sigil/doodle-fixes
% bzr checkout bzr+ssh://centralhost/srv/bzr/sigil/doodle-fixes doodle-fixes
% cd doodle-fixes

これで、 doodle に必要な修正を当てるための場所ができました。 また、他の部分の修正をする人に邪魔されることもありません。 チェックアウトを持っているため、 ~/work/sigil/doodle-fixes/ に対してコミットした内容はcentralhost 上にも現れます。 [1] dev ブランチと同じように、このようなブランチ上で二人の開発者が共同で作業することもできます。 [2]

[1]あるブランチのサブディレクトリに別のブランチがあるというのはおかしなことに見えるかもしれませんが、これは何もおかしくありません。入れ子になったブランチは外側のブランチから派生しているという点で、名前空間の階層と同じようなものだと考えることができます。
[2](1, 2) たくさんの独立したブランチを使っている場合、毎回フルURLを入力するのは大変です。 このURLの入力を簡単にするために、ブランチエイリアスなどたくさんの方法を検討しています。 今のところ、 bzrtools プラグインが bzr cbranch コマンドを提供しています。 これは、ベースとなるブランチを指定して、新しく公開ブランチを作成し、そのチェックアウトを作成することを少ない入力でできるように設計されています。 cbranch の使い方についてはこの文書の範囲外ですが、最後のコマンドについてはこんな感じです。:
% bzr cbranch dev my-feature-branch

変更内容をマージする

doodle-fixes での変更内容をメインのブランチにマージすることになったら、単にこうするだけです。:

% cd ~/work/sigil/dev
% bzr merge ../doodle-fixes

これで、変更内容は dev ブランチ上でもアクセスできるようになりますが、まだコミットはされていません。 最終的な変更内容をレビューして、コードがちゃんとコンパイルでき、テストをパスすることを確認したいならこの時です。 bzr status コマンドと bzr diff コマンドがここで役立ちます。 また、競合を解決するのもこの時です。Bazaar では、競合を解決するまではコミットできないようになっています。 そのため、間違って競合マーカーをコミットしてしまうことはありません。 bzr status コマンドを使えば、他の変更内容と一緒に競合の情報も表示されます。 bzr conflicts なら、競合の情報だけが表示されます。 競合を解決し終わったら、bzr resolve file/namebzr resolve --all を実行してください。 [3] もし、解決が特に難しい競合がある場合は、 bzr remerge コマンドを使いたいと思うかもしれません。 このコマンドで、別のマージアルゴリズムを試してみることができ、さらに元のソース行を表示することもできます。(--show-base)

[3]マージ実行中に競合の解決をさせるシステムもあります。 私たちは、ファイルごとに競合を解決するよりも、ツリー全体を見ながら競合を解決する方がたいていは簡単であることに気づきました。 そうすれば、よりたくさんの情報を得られますし、問題を解決したときにテストを実行することもできます。

推奨のブランチ構成

とても一般的なブランチ構成として、開発者ごとにひとつずつ専用のブランチを割り当て、中央サーバにも開発者ごとの作業領域を用意するという方法があります。これは以下のコマンドでできます。:

% bzr branch bzr+ssh://centralhost/srv/bzr/sigil \
             bzr+ssh://centralhost/srv/bzr/sigil/user-a
% bzr branch bzr+ssh://centralhost/srv/bzr/sigil \
             bzr+ssh://centralhost/srv/bzr/sigil/user-b

これで、開発者ごとに専用の作業用ブランチを割り当てています。 さらに、開発者自身で [2] を使用して新しく新機能開発用ブランチを作成することも簡単にできます。

% bzr branch bzr+ssh://centralhost/srv/bzr/sigil/user-a \
             bzr+ssh://centralhost/srv/bzr/sigil/user-a/feature
% cd ~/work/sigil
% bzr checkout bzr+ssh://centralhost/srv/bzr/sigil/user-a/feature myfeature

用語解説

共用リポジトリ

Bazaar には、”共用リポジトリ”という概念があります。これは、CVSやSubversionのようなの他のRCSが持つ旧来の概念に似ています。 たとえば、Subversionでは、サーバ上のリポジトリにすべての履歴情報が保存されていて、手元には履歴情報は全くなく、作業ツリーのファイルのチェックアウトがあるだけです。 ここで言う”共用”というのは、ブランチ同士の間で共用するという意味であることに注意してください。 開発者間でも共用される かも知れません が、開発者間での共用はスタンドアロンブランチでも可能です。

Bazaar の用語では、”共用リポジトリ”とは複数のブランチが履歴情報を 共有 できる場所のことです。 分散型のワークフローに対応するためには、それぞれのブランチが履歴情報を持っている必要があります。 しかし、しばしばこれは非効率です。関連するブランチ同士は履歴を共有しており、ディスクも共有した方がいいためです。