Bazaar チュートリアル

はじめに

もし、もう分散型バージョン管理に慣れ親しんでいるなら、 “Bazaarに自己紹介する” の節までとばしてください。 もし、分散型でないバージョン管理に慣れ親しんでいるけれど分散型バージョン管理はよくわからないのであれば、”分散バージョン管理と分散でないバージョン管理の違い”までとばしてください。 それ以外の場合、コーヒーか紅茶(訳注:日本茶でもいいですよ)を用意して、リラックスして読み始めてください。

バージョン管理の目的

なにかのテキストデータ(プログラムのソースコード、Webサイト、Unixシステムでの設定ファイルなど)を扱う作業をしているとしましょう。 なにかミスをして、重大なデグレードを引き起こしてしまうかもしれません。 例えばメールサーバーの設定ファイルを消してしまったり、だいじなプロジェクトのソースコードを壊してしまったりすることがあります。 だいじな情報を失って、何が何でも取り戻したいと思った経験があるのであれば、きっとあなたはBazaarを使う準備ができています。

Bazaarをはじめとするバージョン管理システムは、ディレクトリに起こった変更をブランチ (branch) というディレクトリよりも複雑なものの中に入れて管理します。 ブランチは今ディレクトリの中に何が入っているかだけでなく、過去のいろいろな時点でのディレクトリの中身を格納しています。 なので、望まぬ変更をしてしまったときには過去の時点の状態に戻すことができます。

バージョン管理システムは、ユーザーが “リビジョン (revision) をコミット” することで変更をブランチに保存します。 このときに生成されるリビジョンとは、本質的にいって、前回保存したときからの変更点になります。

リビジョンはそれ以外のものも持っています(原文:These revisions have other uses as well.) たとえば、オプションのログメッセージをつけることで、変更が何を意味しているのかというコメントを残すことができます。 実際に利用されるログメッセージは、 “Webテンプレートをテーブルを閉じるように修正” とか “SFTPに対応した。 #595 を修正。” といったものです。

こういったログが保存されているおかげで、たとえば後になって SFTP に問題が発生したときに、問題が発生するようになったのがどの時点なのか目星をつけることができます。

分散バージョン管理と分散でないバージョン管理の違い

多くのバージョン管理システムはサーバーに配置されています。 バージョン管理されているコードに対して作業をしたい場合、サーバーに接続してコードを “チェックアウト (checkout)” する必要があります。 そうすると、変更やコミットができるディレクトリができます。 バージョン管理システムのクライアントは、バージョン管理システムのサーバーにコミットされた変更を保存します。この方式は集中型として知られています。

集中型にはいくつかの欠点があります。 集中型バージョン管理システムは、動作するためにサーバーとの接続を要求します。 このことは、サーバーがどこかインターネット上にあって、あなたのマシンがインターネットに接続できないときに問題になります。 もしくは、あなたのマシンがインターネットに接続できてもサーバーが落ちているときにもやはり問題になります。

分散バージョン管理システムは、ブランチをクライアントと同じマシンに置くことでこの問題を解決しています。 Bazaarの場合、ブランチはバージョン管理されているコードと同じディレクトリに保存されます。 これにより、ユーザーは(たとえオフラインであったとしても)好きなときに変更を保存(コミット (commit))することができます。 インターネット接続が必要になるのは、どこか別の場所にあるブランチの変更にアクセスするときだけです。

多くの人が必要としていることは、ディレクトリ内でおこったファイルやサブディレクトリの変更を追跡することです。 この追跡を手でおこなうのは面倒で不恰好な作業です。 これは、Bazaarのようなバージョン管理システムの目的のひとつです。 バージョン管理システムはユーザーが指示したときにディレクトリツリーの リビジョン (revision) を作ることでこの作業を自動化します。

バージョン管理システムは単に保存したりundoしたりするだけではありません。 たとえばBazaarでは、ソフトウェアのあるブランチから変更を取り出して、関連する別のブランチに適用することができます。このとき、変更を取り出すブランチは別の人のものであってもかまいません。これにより、開発者のグループはお互いに書き込み権を与えることなく共同作業することができます。

Bazaarではリビジョンの ‘’親 (ancestry)’’ 、つまりそのリビジョンの元になったリビジョンを記録しています。 ひとつのリビジョンは複数の、それぞれ別の変更を含む子供リビジョンを持つことがあり、それはツリーの進化が分岐していることを意味しています。 Bazaarではブランチを作ることによって、複数の人が厳しい lock-step をとらなくても協力することができます。 ブランチを作ることは個人での開発でも便利です。

Bazaarに自己紹介する

Bazaarは bzr という新しいコマンドをひとつインストールします。 他の全ては bzr のサブコマンドになります。 bzr help コマンドでいくつかのヘルプを見られます。 幾つかの話題は topic にまとめられていて、 bzr help topics で利用可能なトピックの一覧を見られます。

バージョン管理システムの一つの機能は、誰が何を変更したのかを追跡することです。 分散型バージョン管理システムでは、各開発者がグローバルユニークなIDを持つ必要があります。 ほとんどの人はこのIDとして利用できる eメールアドレス を持っています。 Bazaarはコンピュータのユーザー名とホスト名から自動でメールアドレスを生成します。Bazaarが自動で作成したメールアドレス以外のものを使いたい場合、3つの選択肢があります。

  1. bzr whoami を使ってメールアドレスを設定します。これはグローバルなIDを設定する最も簡単な方法です。グローバルなIDを設定するには:

    % bzr whoami "Your Name <email@example.com>"
    

    特定のブランチでべつのアドレスを使いたい場合、そのブランチのディレクトリのなかで次のコマンドを実行します:

    % bzr whoami --branch "Your Name <email@example.com>"
    
  2. ?/.bazaar/bazaar.conf [1] の中のメールアドレスを、以下のようにして設定します。 [DEFAULT] の部分が大文字と小文字を区別するので注意してください:

    [DEFAULT]
    email=Your Name <email@isp.com>
    

    特定のブランチにおける設定は、 ?/.bazaar/locations.conf にブランチのセクションを作成して次のように書くことができます。

    [/the/path/to/the/branch]
    email=Your Name <email@isp.com>
    
  3. 環境変数 $BZR_EMAIL もしくは $EMAIL ($BZR_EMAIL の方が優先されます)にメールアドレスを設定することで、上の二つの方法で設定されたオプションを上書きすることができます。

[1]Windowsではユーザー設定ファイルはアプリケーションデータディレクトリにおかれます。なので、設定ファイルの場所は ?/.bazaar/branch.conf ではなくC:\Documents and Settings\<username>\Application Data\Bazaar\2.0\branch.conf になります。 同じことが locations.conf, ignore, plugins ディレクトリも同じです。

ブランチを作る

履歴はデフォルトではブランチの .bzr ディレクトリの中に保存されます。

既存のディレクトリの中で bzr init をすると新しいブランチを作成できます:

% mkdir tutorial
% cd tutorial
% ls -a
./  ../
% pwd
/home/mbp/work/bzr.test/tutorial
%
% bzr init
% ls -aF
./  ../  .bzr/
%

ファイルには3つのクラス、 unknown, ignored, versioned があります。 add コマンドはファイルを versioned にし、そのファイルへの変更の記録を開始します:

% echo 'hello world' > hello.txt
% bzr status
unknown:
  hello.txt
% bzr add hello.txt
added hello.txt
% bzr status
added:
  hello.txt

もし間違えたファイルを add してしまった場合、そのファイルを unversioned 状態に戻すために bzr remove してください。 この場合の bzr remove は、ほかの場合 [2] とちがってファイルを削除しません。

[2](1, 2) bzr remove はファイルがバージョン管理されていて何も変更されていない場合に、そのファイルを削除します。 --keep オプションで常にファイルを残すことができます。 --force オプションで常にファイルを削除することもできます。

ブランチの場所

すべての履歴はブランチに格納されます。ブランチとは、管理用のファイルを含んだただのディレクトリです。デフォルトでは、svnやsvkのような、分離したリポジトリやデータベースはありません。分離したリポジトリを作成することもできます(bzr init-repo コマンドを参照してください)。大規模なブランチを利用する場合や、中規模のブランチをたくさん利用する場合にはリポジトリを分離するといいでしょう。

自分のコンピュータのファイルシステム上にあるブランチを参照するときはブランチを格納しているディレクトリ名で指定できます。 bzr は SSH, HTTP SFTP などを経由してブランチにアクセスすることもできます。例:

% bzr log bzr+ssh://bazaar.launchpad.net/~bzr-pqm/bzr/bzr.dev/
% bzr log http://bazaar.launchpad.net/~bzr-pqm/bzr/bzr.dev/
% bzr log sftp://bazaar.launchpad.net/~bzr-pqm/bzr/bzr.dev/

プラグインをインストールすれば、 rsync プロトコルを使ってブランチにアクセスすることもできます。

ブランチを指定した場所に置く方法については、 ブランチを公開する 節をご覧ください。

変更をレビューする

一仕事終えたら、それを履歴に コミット (commit) しましょう。 新しい機能を追加したり、バグを直したり、コードやドキュメントを更新したらいつでもコミットするのは良いことです。 すべてのリビジョンが良い状態であるようにするために、コミットする前にコードをコンパイルしたりテストスイートを実行するのも良い習慣です。 コミットする前にコミットしようとしているものを確認するためにレビューすることができます。

この目的で便利な二つのコマンドがあります。 statusdiff です。

bzr status

status コマンドは、今の作業ツリーが最後のリビジョンからどのように変更されたのかを教えてくれます:

% bzr status
modified:
   foo

bzr status は変更が無かったり無視されているファイルを隠します。 status コマンドはオプションとして、確認対象となる複数のファイル名やディレクトリ名を渡すことができます。

bzr diff

diff コマンドは通常の unified diff フォーマットですべてのファイルのテキストの変更を表示します。 このコマンドの出力を pipe 経由で、’‘patch’‘, ‘’diffstat’‘, ‘’fileterdiff’‘, ‘’colordiff’’ といったコマンドに渡すことができます。

% bzr diff
=== added file 'hello.txt'
--- hello.txt   1970-01-01 00:00:00 +0000
+++ hello.txt   2005-10-18 14:23:29 +0000
@@ -0,0 +1,1 @@
+hello world

-r オプションをつけると、作業ツリーを古いリビジョンと比較したり、二つのリビジョン間の差分を見ることができます。

% bzr diff -r 1000..          # everything since r1000
% bzr diff -r 1000..1100      # changes from 1000 to 1100

--diff-options オプションをつけると、 bzr は外部の diff プログラムにオプションをつけて起動します。 例:

% bzr diff --diff-options --side-by-side foo

プロジェクトによっては二つのファイルに接頭辞がついた patch が好まれます。 --prefix オプションでそのような接頭辞をつけることができます。 ショートカットとして、 bzr diff -p1patch -p1 コマンドが受け付ける形で差分を出力します。

変更をコミットする

作業ツリーの状態に満足したら、ブランチに コミット (commit) しましょう。 コミットとは作業ツリーの状態のスナップショットを保持するリビジョンを新しく作ることです。

bzr commit

commit コマンドはそのリビジョンの変更を説明するメッセージを受け取ります。 また、あなたのユーザーID、今の時間とタイムゾーン、ツリーの内容をあわせて記録します。 コミットメッセージは -m もしくは --message オプションで指定できます。 複数行のコメントも利用できます。多くのシェルはクォートを開いたままで改行することで複数行の入力が可能です。

% bzr commit -m "added my first file"

メッセージをファイルで渡すには -F オプションを使います。 コミットメッセージを先に作成し、それとdiffを合わせてレビューすることで、 コミットメッセージとコミット内容が一致していることを確認する人もいます。 (このファイルは休憩から戻ってきて作業を思い出すときにも役に立つでしょう)

エディタからメッセージを入力する

-m オプションも -F オプションも指定しなかった場合、 bzr はメッセージを入力するためにエディタを立ち上げます。 このエディタは $VISUAL$EDITOR 環境変数で制御することができます。 この環境変数を `` /.bazaar/bazaar.conf`` 内の editor を設定して上書きすることができ、さらに $BZR_EDITOR 環境変数がそれらすべてを上書きします。 もし何も変更せずにエディタを閉じたなら、コミットはキャンセルされます。

エディタで開かれるファイルには水平線が含まれています。この線より下の部分は参考用であり、コミットメッセージには含まれません。 水平線の下にはコミットで変更されるファイルのリストが表示されます。 メッセージは水平線の上に書く必要があります。そうしたら、ファイルを保存してエディタを終了してください。

commit コマンドに --show-diff オプションをつけると、コミットされる変更の diff を見ることができます。この diff はエディタが開いたときに水平線やコミットされるファイルの情報よりも下に含まれます。 なので、コミットメッセージを書くときに diff を見ることができますが、コミットメッセージ自体には diff が含まれません。 コミットメッセージに diff を含めたい場合は、水平線より上にコピーペーストしてください。

解決したバグの記録をつける

プロジェクトにおいて多くの変更はバグの修正のために行われます。 Bazaar は、コミットするときに解決したバグについてメタデータに記録することが できます。 これを行うには、 --fixes オプションを使います。 このオプションは次のような形の引数を取ります。

% bzr commit –fixes <tracker>:<id>

<tracker> の部分にはバグ管理システムを指定するIDを書き、 <id> の部分にはそのバグ管理システム上で管理されているバグの IDを書きます。 <id> はたいてい数値になるでしょう。 Bazaar は最初からいくつかの有名なバグ管理システムを知っています。 bugs.launchpad.net, bugs.debian.org bugzilla.gnome.org です。 これらは、それぞれ独自のIDとして lp, deb, gnome を持っています。 例えば、 bugs.launchpad.net 上のバグ #1234 を解決する場合は、 その解決をコミットするときに次のようなコマンドを利用できます。

% bzr commit -m "fixed my first bug" --fixes lp:1234

For more information on this topic or for information on how to configure other bug trackers please read Bug Tracker Settings.

このトピックに着いてのさらなる情報や、他のバグ管理システムを設定する方法 については、 Bug Tracker Settings を参照してください。

選択コミット

commit コマンドにファイル名やディレクトリ名を渡したとき、それらのファイルの変更だけがコミットされます。 例

% bzr commit -m "documentation fix" commit.py

デフォルトでは bzr は、サブディレクトリから実行される場合でもすべての変更をコミットします。 カレントディレクトリ以下だけをコミットする場合は、次のようにします

% bzr commit .

コミットされていない変更を削除する

不要な変更がある場合、 revert コマンドで最後のリビジョンの状態に戻ることができます。 revert するまえに bzr diff で何が削除されるのかを確認しておくと良いでしょう。 デフォルトでは revert コマンドはツリー全体を revert します。ファイル名やディレクトリ名が指定されている場合は、そのファイルだけが revert されます。 bzr revert はマージ待ちリビジョンのリストも削除します。

ファイルを無視する

.bzrignore ファイル

多くのソースツリーはバージョン管理する必要のないファイルをたくさん含んでいます。 たとえば、エディタのバックアップファイルや、オブジェクトファイル、バイトコード、 ビルドされたプログラムなどです。 こういったファイルを単に add しないこともできますが、そうすると毎回 unknown file としてたびたび出現することになります。 ツリートップにある .bzrignore とよばれるファイルにそれらのファイルを追加することで、bzrにそれらのファイルを無視させることができます。

このファイルは行ごとにワイルドカード (もしくは”glob”) のリストを含みます。 典型的な内容の例です:

*.o
*?
*.tmp
*.py[co]

glob がスラッシュを含む場合、ツリーのトップからのパス全体にマッチします。 そうでない場合は、単にファイル名にマッチします。 なので、上の例はすべてのサブディレクトリの .o 拡張子を持つファイルを無視しますが、次の例ではツリーのトップにある config.h ファイルと、 doc/ ディレクトリ以下のHTMLファイルだけを無視します:

./config.h
doc/*.html

どのファイルがどのパターンにマッチして無視されているのかを、 bzr ignored コマンドで表示することができます:

% bzr ignored
config.h                 ./config.h
configure.in?            *?

バージョン管理されているファイルが無視パターンにマッチしたり無視リストに入っていても大丈夫です。無視パターンはバージョン管理されたファイルには影響しません。バージョン管理されていないファイルを ‘unknown’ として扱うか’ignored’ として扱うかを決めるためだけに使われます。

.bzrignore ファイルは普通はバージョン管理されます。なのでそのブランチのコピーでも同じパターンが無視されます。

% bzr add .bzrignore
% bzr commit -m "Add ignore patterns"

bzr ignore

.bzrignore ファイルを直接編集する代わりに、 bzr ignore コマンドを 利用することができます。 bzr ignore コマンドはファイル名かパターンを 引数に受け取って、それを .bzrignore ファイルに追加します。 .bzrignore ファイルが存在しない場合、 bzr ignore コマンドは 自動的にそのファイルを作成してバージョン管理に追加します。

% bzr ignore tags
% bzr status
added:
  .bzrignore

.bzrignore ファイルを自分で修正したときと同じく、コマンドを実行したあとに .bzrignore ファイルをコミットしなければなりません。

% bzr commit -m "Added tags to ignore file"

グローバルの無視設定

エディタの一時ファイルや個人の一時ファイルなど、幾つかの無視ファイルはプロジェクト依存ではなくてユーザー依存です。 こういったファイルを全プロジェクトで無視設定するかわりに、グローバルの無視設定ファイル ~/.bazaar/ignore を利用できます。 これはプロジェクトの ignore ファイルと同じ文法で記述します。

履歴を閲覧する

bzr log

bzr log コマンドは過去のリビジョンのリストを表示します。 bzr log --forward コマンドは同じ内容を、時系列順で最新のものが最後にくるように出力します。

bzr diff と同じように、 bzr log-r 引数をサポートします:

% bzr log -r 1000..          # リビジョン r1000 とそれ以降すべて
% bzr log -r ..1000          # r1000 とそれ以前のすべて
% bzr log -r 1000..1100      # r1000 から r1100 まで
% bzr log -r 1000            # リビジョン r1000 だけ

ブランチの情報

bzr info コマンドは作業ツリーとブランチの履歴に関する情報の要約を表示します。

ディレクトリをバージョン管理する

bzr はファイルとディレクトリを、名前の変更を追跡して賢くマージできるようにバージョン管理します:

% mkdir src
% echo 'int main() {}' > src/simple.c
% bzr add src
added src
added src/simple.c
% bzr status
added:
  src/
  src/simple.c

ファイルを削除する

ファイルを削除するのは、単純に作業ツリーからファイルを削除するだけでできます。 これは、 cvs remove が必要な CVS とは少し異なります。

bzr remove はファイルをバージョン管理対象からはずしますが、作業ツリーから削除することも削除しないこともできます。 [2] これは間違ったファイルを追加してしまったり、あるファイルをバージョン管理するのをやめる場合に便利です。

% rm -r src
% bzr remove -v hello.txt
?       hello.txt
% bzr status
removed:
  hello.txt
  src/
  src/simple.c
unknown:
  hello.txt

もし間違ってファイルを削除してしまった場合、 bzr revert でリストアできます。

ブランチを作る

自分でプロジェクトを始めるのではなく、既存のプロジェクトに変更を加えたい場合があります。 この場合、既存のブランチのコピーを取得する必要があります。 このコピーは新しいブランチになるので、このコマンドは branch という名前になっています:

% bzr branch lp:bzr bzr.dev
% cd bzr.dev

これでブランチの完全な履歴をコピーできたので、すべての操作 (log, annotate, branch の作成とマージなど) をローカルで実行できます。 履歴の一部だけを取得するオプションも追加される予定です。

既存のブランチをコピーするには、普通にディレクトリをコピーしたり、tarballを展開したり、リモートから rsync のような方法でコピーすることもできます。

上流の変更を追いかける

変更を “pull” することで手元のブランチを上流のブランチに対して最新に保つことができます。

% bzr pull

このコマンドを実行した後、ローカルディレクトリはpull元のミラーになります。 ミラーするものには、 ‘’revision-history’’ を含みます。つまり、別のブランチからマージするのではなくて、このブランチに対してコミットした履歴になります。

このコマンドはローカルの(pull先)ブランチが親ブランチの古いコピーで独自のあたらしいリビジョンを一切含まないか、ローカルブランチへの最新のコミットが親ブランチにマージされているときにのみ成功します。

関連ブランチからマージする

二つのブランチが分岐している(互いに異なる変更を持っている)とき、 bzr merge コマンドの出番です。 merge はマージ元ブランチにあって手元のブランチにない変更を自動で探して、その変更を手元に適用しようと試みます。

% bzr merge URL

マージ中に衝突(conflict)が発生した場合、同じ基本名(basename)をもつ3つのファイルが作成されます。 共通の祖先になるファイルのファイル名には “.BASE” が、 手元のブランチの変更を含むファイル名には “.THIS” が、 マージ元の変更を含むファイル名には “.OTHER” が末尾に追加されます。 kdiff3 のようなプログラムを利用してこれらのファイルをひとつにマージすることができます。 コミットするには、マージされた “.THIS” ファイルを元のファイル名にリネームします。 衝突の解決を完了するには、 resolve コマンドを使います。 このコマンドは “.OTHER” と “.BASE” ファイルを削除します。 .BASE, .THIS, .OTHER ファイルが残っている場合、 commit コマンドはエラーを報告します。

% kdiff3 file.BASE file.OTHER file.THIS
% mv file.THIS file
% bzr resolve file

[TODO: explain conflict markers within files]

ブランチを公開する

bzrのブランチを公開するには特別なサーバーは必要ありません、普通のWebサーバーが使えます。 .bzr ディレクトリを含めてファイルをサーバーにミラーしてください。 ブランチをpush(やブランチに対する操作)するのに以下の3つの方法があります。

  • 最良の方法は bzr 自体を使うことです。

    % bzr push bzr+ssh://servername.com/path/to/directory
    
  • 別の選択肢は bzrtools に含まれる rspush プラグインを利用することです。 これはリモートの履歴と作業ツリーに変更を push するのに rsync を利用します。
  • tarball を送ったり rsync を使ったりほかの転送方法を利用して、手動でファイルをコピーすることもできます。 これはたいてい push ほど安全ではありませんが、場合によって高速だったり、簡単だったりするかもしれません。

変更をツリー間で移動する

どんな人にでもありえることですが、適切ではないツリー上で変更してしまうことがあります。 単純に作業するディレクトリを間違えたり、変更が予想よりも大きくなってしまったりして、 その変更のために新しいブランチを作りなおすことがあります。

変更をあるツリーから別のツリーに移動するには

% cd NEWDIR
% bzr merge --uncommitted OLDDIR

これですべてのコミットされていないOLDDIR上の変更がNEWDIRに適用されます。 コミットされていない変更は、通常のmergeでNEWDIRに適用することができる場合でも適用されません。 OLDDIR上の変更はそのまま残りますが、NEWDIRの状態に満足したら bzr revert OLDDIR で削除することができます。

NEWDIRはOLDDIRのコピーである必要はありませんが、関連しているべきです。 両者の違いが大きければそれだけ衝突が起こる可能性が大きくなります。