日々の利用

概論

ようやく初期インポートが終わった。ここから先は単純だ。どんな バージョン管理システムでも、基本的な操作はたった二つしかない。それ は:

  • プロジェクトツリーのファイルに修正を加えて

  • それをコミットする

これだけだ。この二つの操作を基本とした上で、じゃあ他のアーカ イブのデータを取り込むことができるのか、二人以上の人間が同じ場所に 変更したときうまく調停してくれるか、ネットワークをまたいでマージで きるか、複数のファイルの変更をひとまとまりの変更としてまとめて取り 扱ってくれるか、ドキュメントは充実しているか、サポートセンターはあ るか、ロゴ入りマークのTシャツと携帯ストラップは扱っているか、作者 はヤンキースファンであるかどうか、などの話が続く。

最初の操作。つまりプロジェクトツリーのファイルに対する修正は、 GNU arch とは無関係に行う処理になる。テキストファイルであればエディ タを使って修正するかも知れないし、一括置換用のスクリプトを流してバッ チ的に多くのファイルを変更するかも知れない。バイナリファイルであれ ば、たとえば画像ファイルならその画像を編集するためのお絵かきソフト か何かで編集してから保存する。どんな修正をしようと君の勝手だし、こ こが大切なところなのだが、GNU arch はそれらの修正に対して一切口を 出さない。いや、口を出さない、というのは正確ではない。GNU arch は 能動的に何かをしないだけではなく、受動的に何かを監視するようなこと すらしない。unix の言葉で言えば何かデーモンプロセスのようなものが 走っているわけではない。「ははー、こいつ、いまこんな修正してやがる。 じゃあ次のコミットに備えてこうしておこう」- その手の処理は一切しな い。GNU arch は君が何をしているか知らないのだ。

GNU arch が君の変更を知るのは二番目にあげた処理、つまりコミッ トするために次に tla commit が実行された時だ。このコマンドが呼び出 されると GNU arch は最後に tla commit あるいは tla import が呼び出 された時点でのツリーの様子と現時点での様子の違いを完全に把握するた めにプロジェクトツリー全体をスキャンする。君は言う。そんなことしたっ て無駄だ。だって直前のツリーなんてもうどこにも存在しちゃいない。い ま修正を加えたばかりだぞ、と。正論だ。実はtla はこの比較のために、 commit/import するたびに、その時点のツリー全体を丸ごとコピーして {arch}管理領域にもう1セット保存しておくのだ。このコピーのことをプ リスティンコピーと言う。まあ、世の中それなりにうまくできているのだ。

tla changes(1)

あまり話しを遠くまで進める前に、前のサンプルにちょっとイタズラして みよう。main.c を適当にいじって保存し、tla changes コマンドを叩い てみる:

$ tla changes
* looking for octopus@bluegate.org--2004/test--proj--1.0--base-0 to compare with
* comparing to octopus@bluegate.org--2004/test--proj--1.0--base-0
M  main.c
$ 

M という文字で始まる行によって、main.c に修正が入ったことが わかる。実際の修正内容を見たい場合には --diffs というオプションを つけると良い:

$ tla changes --diffs
* looking for octopus@bluegate.org--2004/test--proj--1.0--base-0 to compare with
* comparing to octopus@bluegate.org--2004/test--proj--1.0--base-0
M  main.c

* modified files

--- orig/main.c
+++ mod/main.c
@@ -1,5 +1,8 @@
 #include "./inc/tools.h"
 
+/*
+    main function:
+*/
 int main()
 {
         hello_world();

$ 

このように GNU diff 形式で表示してくれる。今回は 3 行コメン トを入れたことがわかる。どんな変更をしたか確認したいことはよくある。 たとえば昨日の晩徹夜で作業を続け、次の日の午後コンピュータの前でよ だれを垂らして気を失っている自分に気がついたとする。最後に何をした かなんてもちろん誰も覚えてはいない。そんなときにはこのコマン ドを使って変更点を調べよう。コミットする直前に、おそらく 90 % 以上 の確立で私はこのコマンドを叩く。一番よく利用されるコマンドかも知れ ない。

プロジェクトツリー内のファイルに対する修正についてもう少し詳 しく考えてみよう。修正にはファイルの内容の変更のほかにももう少しい ろいろな修正が考えられる。まずファイルのパーミッションを修正するよ うな場合。それから、既存のファイルを削除したり、新しいファイルを追 加したりするような場合だ。このうちファイルを新たに追加した場合には やっかいなことがおこる可能性がある。前章で少し説明したように、新し いファイルの名前が =tagging-method が適切に設定されていなければ今 回のコミットに伴うスキャンで GNU arch がそれをソースファイルと認識 できない可能性があるのだ。

またいたずらしてみよう。まず main.c の修正はそのままにしてお いて、cc.sh のパーミッションを変更してから tla changes を実行して みる:

$ ls -l
total 36
-rwxrwxr-x    1 octopus  octopus        40 May  5 11:54 cc.sh
-rwxrwxr-x    1 octopus  octopus     13667 May  5 12:07 hw
drwxrwxr-x    2 octopus  octopus      4096 May  5 11:53 inc
drwxrwxr-x    2 octopus  octopus      4096 May  5 11:53 lib
-rw-rw-r--    1 octopus  octopus       110 May  7 12:17 main.c
drwxrwxr-x    4 octopus  octopus      4096 May  5 19:04 {arch}
$ chmod -x cc.sh
$ ls -l cc.sh
-rw-rw-r--    1 octopus  octopus        40  5月  5 11:54 cc.sh
$ tla changes
* looking for octopus@bluegate.org--2004/test--proj--1.0--base-0 to compare with
* comparing to octopus@bluegate.org--2004/test--proj--1.0--base-0
-- cc.sh
M  main.c
$ 

-- cc.sh という行が増えている。-- はパーミッションに変化があっ たことを示す表示だ。やはり --diffs オプションをつけると詳しい状況 がわかる:

$ tla changes --diffs
* looking for octopus@bluegate.org--2004/test--proj--1.0--base-0 to compare with
* comparing to octopus@bluegate.org--2004/test--proj--1.0--base-0
-- cc.sh
M  main.c

* file metadata changed

    ./cc.sh
        --permissions 775
        => --permissions 664

* modified files

--- orig/main.c
+++ mod/main.c
@@ -1,5 +1,8 @@
 #include "./inc/tools.h"
 
+/*
+    main function:
+*/
 int main()
 {
         hello_world();

$ 

main.c の修正のほかに、--permissions のような行が表示され、 775 から 664 に変化したことを教えてくれている。このあたりの話はす べてプロジェクトツリー上でおきていることで、アーカイブ領域とは何の 関係もない。

undo と redo

さあ、イタズラはこのくらいにして、元に戻そう。main.c をエディ タでもう一度いじって、cc.sh に対しても chmod しなおせば良いのだが もっとスマートな方法がある。tla undo だ。このコマンドは修正する前 に、最後に実行した import/commit コマンド時点の状態までツリーを戻 してくれる:

$ tla undo
* linting the source tree
* reverting changes
M   main.c
--  cc.sh
$ ls
,,undo-1  cc.sh  hw  inc  lib  main.c  {arch}
$ ls -l cc.sh
-rwxrwxr-x    1 octopus  octopus        40  5月  7 13:19 cc.sh
$ 

main.c の内容を cat なり more なりで確認してほしい。cc.sh の パーミッションも元に戻っているのがわかるだろう。問題は ,,undo-1 と いう、奇妙なファイルがあることだ。このファイルの正体は、実はなんの 変哲もないただのディレクトリで、チェンジセットと呼ばれるデータが入っ ている。,, でディレクトリ名が始まるのは GNU arch がジャンクファイ ルに分類するためだ。この形のファイルはいつなんどき勝手に削除されて も文句は言えない。そういう決まりがある。ここにはついさっきやったツ リーに対するイタズラの詳細が記録されている。絵に書くとこんな感じだ:

図 3.5. base-0, base-0 への修正を加えたツリー、,,undo-1 の間の関 係

base-0, base-0 への修正を加えたツリー、,,undo-1 の間の関 係

どこかで見た図だとは思わないだろうか? そう、第一章で説明した GNU diff と GNU patch の絵に似ている。はっきり言ってそっくりだ。あ そこでは GNU diff/GNU patch が取り扱っていたのは一つのファイルだっ た。tla undo は GNU diff のツリーバージョンなのだ。ツリーとツリー の間の差分を計算して、それを ,,undo-1 に保存する。これが tla undo だ。とすると、それと反対のことをするコマンド、つまり GNU patch の ツリー版も存在しなくては理屈に合わない。まったくその通りで、tla redo というコマンドがある。これは tla undo の結果を取り消す、つま り、もう一度イタズラをした状態にツリーを戻すためのコマンドだ。やっ てみよう:

$ tla redo
* linting the source tree
M   main.c
--  cc.sh
$ ls
cc.sh  hw  inc	lib  main.c  {arch}
$ tla changes
* looking for octopus@bluegate.org--2004/test--proj--1.0--base-0 to compare with
* comparing to octopus@bluegate.org--2004/test--proj--1.0--base-0
-- cc.sh
M  main.c
$ 

tla changes の結果が、イタズラしたときと同じになっているだろ う。同時に ,,undo-1 というディレクトリが消えている。ツリーを元に戻 すために tla redo が利用したからだ。いろいろ説明してきたが、我々は まだアーカイブ領域には一切手をつけていない。tla undo も tla redo もプロジェクトツリーにのみ作用し、アーカイブには一切書き込みしない ことに注意してほしい。

修正のコミット

話を進めようと思う。今回のいたずらを実際の修正点と見なしてコ ミットしてみよう。コミットする一番簡単な方法は -L オプションの後に ログメッセージをつけて単に tla commit を呼び出すことだ:

$ tla commit -L'hoge'
* update pristine tree (octopus@bluegate.org--2004/test--proj--1.0--base-0 => test--proj--1.0--patch-1)
* commited octopus@bluegate.org--2004/test--proj--1.0--patch-1
$ 

base-0 の次のリビジョン、patch-1 ができた。'hoge' は、日本版 'foo' のことだ[13]。-L の後の文字列がロ グメッセージとして今回のリビジョン patch-1 に付随したメッセージと して保存される。アーカイブの状態を確認したい場合は例によって tla abrowse を実行する。オプションはたくさんあると言ったが、たとえば --desc オプションなどをつけると、かなりいろいろな情報をコンパクト に表示できる:

$ tla abrowse --desc
octopus@bluegate.org--2004
  test
    test--proj
      test--proj--1.0

        base-0    (initial import)
          Wed May  5 19:04:43 JST 2004      octopus <octopus@bluegate.org>
          initial import

        patch-1    (simple changeset)
          Fri May  7 14:01:34 JST 2004      octopus <octopus@bluegate.org>
          hoge

$ 

hoge の文字が表示されているのがわかると思う。tla abrowse は さまざまな引数をとり、いろいろな側面からアーカイブの内容を表示する 機能を持っている。いずれもアーカイブに対する参照だけなので、ぜひ試 してみてほしい。tla abrowse と同様の表示をする tla changelog とい うコマンドもある。似たようなコマンドが二つあるのは単に歴史的な理由 からだ。tla abrowse の方が後にできた新しいコマンドなので、こちらを 利用するのがおすすめだ。

ところで -L オプションではたいした量のログメッセージは書けな い。腰をすえてじっくりログメッセージを作りたい人は、まず tla make-log コマンドを使ってログファイルの元になるファイルを作る。見 るもおぞましいファイル名をもったログのテンプレートファイルがプロジェ クトツリーのルートに作成される。

$ tla make-log
/home/octopus/proj/++log.test--proj--1.0--octopus@bluegate.org--2004
$ ls
++log.test--proj--1.0--octopus@bluegate.org--2004  hw	lib	{arch}
cc.sh						   inc	main.c
$ 

tla make-log はプロジェクトツリー上での実際の修正を済ませ てこれからコミットするという直前に作っても良いし、修正前にあらかじ め作っておいても良い。more でこのファイルの中身をのぞいてみよう:

$ more ./++log.test--proj--1.0--octopus@bluegate.org--2004
Summary: 
Keywords: 

$ 

ファイルの中には Summary: とKeywords: の二つの文字列がある。 ログのタイトルとなる要約の文字列はSummary: の後に書く。Summary: の 後に最低一つは空白を置いてやるほうが良い。ログメッセージが長くなる 場合には、Keywords: の行の下に一行空行をいれて、その後にログの本文 を書いてやる。たとえばこんな具合だ(XXX:日本語の問題):

$ more ./++log.test--proj--1.0--octopus@bluegate.org--2004
Summary: add an empty file.
Keywords: 

add an empty file for the test of 'tla changes'.

$ 

このファイルの Summary: と Keyword: の文字そのものや、 Keyword: の後に一行空行を入れることに注意してほしい。このファイル は RFC822 準拠の形式をしているので、これに従わない場合にはエラーと なってしまう。もう一度コミットしてみよう。GNU arch では、コミット は少なくともツリーになんらかの変更がされない限り受け付けない。そこ で、今度はイタズラで空のファイルを追加してみる:

$ touch sub.c
$ ls
++log.test--proj--1.0--octopus@bluegate.org--2004  hw	lib	sub.c
cc.sh						   inc	main.c	{arch}
$ tla changes
* looking for octopus@bluegate.org--2004/test--proj--1.0--patch-1 to compare with
* comparing to octopus@bluegate.org--2004/test--proj--1.0--patch-1
A  sub.c
$ 

sub.c というファイルを追加した。touch コマンドは空のファイ ルを作るためのコマンドだ。その後 tla changes の出力を見てみる。'A' で始まる行は追加されたファイルを示している。'M' と 'A' が出てきた が 'D' というのもあってファイルが削除された場合にはこの表示が出る。 ではコミットしてみよう。ログファイルがあるので今回は -L は不要だ:

$ tla commit
A  sub.c
* update pristine tree (octopus@bluegate.org--2004/test--proj--1.0--patch-1 => test--proj--1.0--patch-2)
* commited octopus@bluegate.org--2004/test--proj--1.0--patch-2
$ 

リビジョン patch-2 ができて、嫌なログファイルも消えてくれ た。tla abrowse してみよう:

$ ls
cc.sh  hw  inc	lib  main.c  sub.c  {arch}
$ tla abrowse --desc
octopus@bluegate.org--2004
  test
    test--proj
      test--proj--1.0

        base-0    (initial import)
          Wed May  5 19:04:43 JST 2004      octopus <octopus@bluegate.org>
          initial import

        patch-1    (simple changeset)
          Fri May  7 14:01:34 JST 2004      octopus <octopus@bluegate.org>
          hoge

        patch-2    (simple changeset)
          Fri May  7 16:50:28 JST 2004      octopus <octopus@bluegate.org>
          add an empty file.

$ 

ちょっとくどいが、'D' の出力を見るために sub.c を削除して からもう一度だけコミットを練習してみよう。こんな感じだ:

$ rm sub.c
$ ls
cc.sh  hw  inc	lib  main.c  {arch}
$ tla changes
* looking for octopus@bluegate.org--2004/test--proj--1.0--patch-2 to compare with
* comparing to octopus@bluegate.org--2004/test--proj--1.0--patch-2
D  sub.c
$ tla commit -L'delete sub.c'
D  sub.c
* update pristine tree (octopus@bluegate.org--2004/test--proj--1.0--patch-2 => test--proj--1.0--patch-3)
* commited octopus@bluegate.org--2004/test--proj--1.0--patch-3
$ tla abrowse --desc
octopus@bluegate.org--2004
  test
    test--proj
      test--proj--1.0

        base-0    (initial import)
          Wed May  5 19:04:43 JST 2004      octopus <octopus@bluegate.org>
          initial import

        patch-1    (simple changeset)
          Fri May  7 14:01:34 JST 2004      octopus <octopus@bluegate.org>
          hoge

        patch-2    (simple changeset)
          Fri May  7 16:50:28 JST 2004      octopus <octopus@bluegate.org>
          add an empty file.

        patch-3    (simple changeset)
          Fri May  7 16:54:08 JST 2004      octopus <octopus@bluegate.org>
          delete sub.c

$ 

何か重要な変更をした後でも、コミットが済んだらひと安心だ。 君の修正はすべてアーカイブに保存されたことになるし、アーカイブは 追加される一方で、削除されることはないからだ。ディスクがクラッシュ せず、アーカイブ用のファイルシステムを GNU arch 以外のプログラム で直接いじらない限り、君の修正は永久に保存されたことになる。プロ ジェクトツリーは作業領域に過ぎないから、削除しても大丈夫だ。既に 紹介した tla get コマンドでいつでもプロジェクトツリーを復元する ことができる。ぜひためしてみてほしい。

プリスティン・コピー

一番よく利用されるのは、tla commit だ。これは作業中のプロジェ クトツリーの内容と、作業開始時点の内容、つまりアーカイブバージョン の中の最新リビジョンとの間の差分を計算する。GNU arch では差分の結 果は単なるパッチファイルではなく、チェンジセットというデータ構造と して求めるのだった。tla commit はこのチェンジセットをアーカイブの 該当バージョン用の領域に追加し、リビジョンを増やす。リビジョンには commit のオプションで特に指定しなければ、patch-10 のような形のリビ ジョンを作る。patch-10 には二つの意味がある。ひとつは前のリビジョ ンと作業コピーとの差分を元にして計算したチェンジセット、もう一つは その結果追加されたリビジョンのことだ。

ところで、ここでよく考えてみてもらいたいのだが、最後のリビジョ ンとプロジェクトツリーの間の差分を比較するためには、本来であればリ ビジョンにアクセスしなくてはならない。ひとたびできたリビジョンは永 続的なデータであり、それはアーカイブにある。後で説明するが、アーカ イブは別にプロジェクトツリーのあるマシンと同じマシンになければなら ないという制約はない。もしリモートアーカイブなら、ネットワークにつ ながっていなくてはアクセスできない。これは不便だ。君は桜の木の下で ラップトップを広げてひと仕事したかったのだ。

そこでプロジェクトツリーは自分が最後にアクセスした最新リビジョ ンのコピーを{arch}管理領域配下にデフォルトでは1世代だけキャッシュしてい る。これをプリスティン・コピーと言う。だから今話してきた差分の表示 はプロジェクトツリーにアクセスできさえすれば実行できるのだ。

tla changes --diffs の結果が気に入らない場合、最後のリビジョ ンにプロジェクトツリーの内容を戻すことができる。これは普通は今言っ たプリスティン・コピーをプロジェクトツリーの内容に上書きすることで 行う。いま話したのと同じ理由で、この操作もアーカイブにアクセスする ことなしに実行できる。これには tla undo コマンドを使う:



[13] しかし不思議なことに日本には'bar', 'baz' に当たるものはない。