VC (Version Control) を使おう

意外と知られていないようですが (気のせい?) Emacs で RCS (や SCCS) を使うことができます (もちろん M-! ci foo [RET] とかじゃなくてね)。 Emacs で RCS を使うと非常に便利です。 なにせ RCS 関連のたくさんのコマンドを覚えずに済みます。 ところが、私の探し方が悪かったのか、日本語で解説が書かれている web page が見あたりませんでした。 ってなわけで、書いてみました。

読む際には以下の点について注意して下さい :

ちなみに私は ci, co, rcsdiff くらいは知ってますが、細かい option とか、lock 云々は良く分かりません f(^_^; (自分が勉強するつもりじゃなきゃこんな文章書きませんって :-) まぁその程度の知識で良いってことです。ハイ。

なお、この文章は、Emacs の Info の Version Control を参照しつつ書いています。 必要に応じて (あるいはこの文章がアヤしいと思ったら :-) そちらも参照して下さい。


1. 最初の一歩

RCS を使うってことは、

  1. RCS/ ディレクトリーを作る (作らなくても問題ありませんが、作っておいた方が良いでしょう)
  2. ファイルを RCS に登録する
  3. 適当に ci (checkin), co (checkout) する
ですね。 大雑把すぎですか? でもこれで大体事足りますよ。 まぁそれはさておき、Emacs ではこれらを以下のコマンドで行ないます。 RCS/ ディレクトリーはすなおにシェルで mkdir RCS として下さい。 (M-! mkdir RCS とか、dired で + でもいいですけど)。

C-x v i (vc-register)
現在編集しているファイルを登録 (regist) します。
C-x C-q (vc-toggle-read-only)
チェックイン / チェックアウトをします。
チェックイン時にはコメントを入れろと言われるので適当に入力して C-c C-c (vc-finish-logentry) します。 何も書かないと文句を言われるので、ちゃんと何か書きましょう。 このとき M-n, M-p, M-s, M-r が使えます。
参考 :
もともと (古い Emacs では?) C-x C-q は toggle-read-only です (たしか...)。
現在の Emacs でも登録してないファイルで C-x C-q としてみると、 mode line (Emacs の下の方の反転してるところ) の ----- もしくは ---**- のようになっているところが、--%%- とか --%*- のように、あるいはその逆になります。 要するに書きこみ可 / 不可の変更をします (そのまんまか)。
C-x v v (vc-next-action)
チェックイン / チェックアウトをします。 現在編集しているファイルが登録されていない場合には RCS に登録されます。
何も考えずに常にこれを使ってると楽かもしれません。

とりあえず、これだけで最低限の RCS に関する作業はできるでしょう。

なお通常、ファイルはチェックアウト時に lock され、他の人が編集できない状態になります (つまりファイルを編集できるのは 1 人だけ)。 チェックイン時には lock がはずされ、他の人が (lock をして) そのファイルを編集できる状態になります。

2. 古い revision との比較

古い revision との比較をするためには主に以下のコマンドを使います。

C-x v ~ (vc-version-other-window)
別の buffer に、今編集中のファイルの、入力した revision の内容が表示されます (どうでもいいけど nroff mode になるんですが...)。
C-x v = (vc-diff)
現在の buffer と、最後にチェックインした revision との比較をします。
C-u C-x v = (vc-diff)
指定したファイルの、2 つの revison の比較をします。

このとき directory を指定すると、その directory 以下で登録されているすべてのファイルの差分を取ります

なお差分を取るさいのオプションは変数 diff-switches で指定できます。

3. スナップショットを撮る

3.1 スナップショットとはなんじゃらほい

スナップショットって何か御存知ですか? まず、それを説明しましょう。なぜかって? それは私がこれを書くまで良く知らなかったからです :-)

ここでのスナップショットというのは、あるディレクトリー以下の登録された ファイル群の、あるバージョンのことです。 自分で言うのもなんですが、さっぱり分かりませんね。 では、Emacs Manual での原文を見てみましょう。

A snapshot is a named set of file versions (one for each registered file) that you can treat as a unit.
やっぱり良く分かりません。 え? 分かりました? そういう人は次に進んで下さい。 分からない人の為に、具体的に説明しましょう。

いま、foo というアプリケーションを作成しているとします。 この foo は foo.c, bar.c, foo.h の 3 つのファイルから構成 (というか make されるというか) されるものとします。 これらのファイルが、あるディレクトリーに、

    drwxr-xr-x   2 p-katoh  user      512 Feb 15 19:14 RCS
    -r--r--r--   1 p-katoh  user     2513 Feb 15 19:14 bar.c   (Rev. 1.1)
    -r--r--r--   1 p-katoh  user    10196 Feb 15 19:09 foo.c   (Rev. 1.3)
    -r--r--r--   1 p-katoh  user      875 Feb 15 18:11 foo.h   (Rev. 1.2)
のように存在しているとします (最後に書いてあるのはそれぞれの revision)。

さて、これらを (例えば beta release として) 配布したいとしましょう。 それぞれ revision が異なるので、管理が面倒くさそうですね。 そんなときにスナップショットを作ります。 この「状態」 (bar.c の revison が 1.1 で, foo.c が 1.3 で...) に名前を付けます。 例えば snapshot1 (安易だなぁ) としましょう。 これが「アプリケーション foo」の「snapshot1」という名前の スナップショットということになります。

当然、この後もこれらのファイルに変更が加えられるでしょう。 そしてまた、ある程度まとまったら次のスナップショットを作ります。 もちろん「snapshot2」という名前のスナップショットですね :-)

こうやっていけば、一群のファイルの revision を統一することなく管理できます。 便利便利。 しかも、「snapshot1 と snapshot2 の差分を取る」なんてこともできます (C-x v = (vc-diff) で revision を入力するときに snapshot の名前を入力するだけです)。 さぁ、スナップショットを活用しましょう。

3.2 今度こそスナップショットを撮る

スナップショットを作る為のコマンドは次の通りです :

C-x v s (vc-create-snapshot)
カレントディレクトリー以下にある登録されたファイルの最後の revision をスナップショットとして名前を付けます。
C-x v r (vc-retrieve-snapshot)
カレントディレクトリー以下の、入力した名前に対応する、 全ての登録されているファイルをチェックアウトします。

3.3 でも最後に注意事項

(*1) 通常の使い方をしていれば、チェックインした時点で lock がはずされます (たぶん)。

4. Change Log を作る

よく、(特に GNU の) アーカイヴを持ってきて展開すると ChangeLog ってのがありますね。 これって一々自分で作ってると思いますか? 実は私はそう思っていました f(^_^; もちろんそうやってつくっている人もいるでしょうけど、 Emacs には ChangeLog を作るための機構が (VC とは別に) あるのです。 それについての詳細はここでは説明しませんので、info の File: emacs, Node: Change Log などを参照して下さい。

VC を使っているときには、以下のコマンドであっという間に Change Log ができあがります。

C-x v a (vc-update-change-log) (RCS を使っているときのみ)
カレントディレクトリーの Change Log ファイルを読みこんで、 Change Log の最後のエントリー以降の変更を追加します。

なお、Change Log は

     Wed Apr  1 08:57:59 1992  Nathaniel Bowditch  <nat@apn.org>
     
             * vc.texinfo: Fix expansion typos.
     
             * vc.el, vc-hooks.el: Don't call expand-file-name.
のようにエントリーを 1 行おきに表示します。 これを、
     Wed Apr  1 08:57:59 1992  Nathaniel Bowditch  <nat@apn.org>
     
             * vc.texinfo: Fix expansion typos.
             * vc.el, vc-hooks.el: Don't call expand-file-name.
のようにしたい場合には、 チェックインのときの log を入力する際に、
{expand} Fix expansion typos.
のように書きます (他のファイルについても同様に書きます)。 (`{expand}' の意味するところは私は知りません。誰か教えて下さい。)

またチェックインのときに入力した log が # ではじまっている場合は Change Log には書かれません。

5. ここまできたら次はカスタマイズ --- variables

ここまできたら、いろいろカスタマイズしたいですよね。 それは当然の欲求です。 というわけで関係する変数 (の一部) です。

大雑把に、変更すると便利そうな変数から、特に変更の必要のなさそうな順番に 並べてあります (当然私の主観による判断です)。

text-mode-hook, vc-log-mode-hook
log の編集する際に呼ばれる hook です。 特に text-mode-hook が呼ばれるというのは知っておくに値するでしょう。
diff-switches
差分を取る際のオプションです。 Unified diff 形式が好きな人は、
(setq diff-switches "-u")
などととしておくと良いでしょう。 名前から分かるように VC 固有の変数ではないことに注意しましょう。
vc-header-alist
vc-insert-headers の際に入れる header です。 デフォルトでは
    '((SCCS "%W%") (RCS "$Id$"))
    
となってます。
vc-static-header-alist
各ファイルタイプに対応するヘッダーのテンプレートです。
    (("\\.c$" . 
      "\n#ifndef lint\nstatic char vcid[] = \"\%s\";\n\
    #endif /* lint */\n"))
    
のように書きます。 これは「ファイル名が .c で終わっているときに M-x vc-insert-headers とすると、
    #ifndef lint
    static char vcid[] = "$Id$";
    #endif /* lint */
    
が挿入される (RCS の場合)」ことを意味します。 この例でも分かるように、`%s' は vc-header-alist で指定されたヘッダーで置き換えられます。
vc-make-backup-files
non-nil にすると、VC を使っているときでも backup file を作るようになります。 (デフォルトでは backup file を作りません)。
vc-initial-comment
non-nil のときは、C-x v i で initial version に対するコメントを聞いてきます。
vc-checkin-switches
チェックイン時にコマンドに渡されるオプションです。
vc-suppress-confirm
non-nil にすると、C-x C-q や C-x v i, C-x v u でファイルをセーブする際に 確認しません。
vc-command-messages
non-nil の場合、どのコマンドが実行されたかを mini-buffer に表示します。 が、一瞬にして消えます (おいおい)。 ちなみに、
Running ci on /home/p-katoh/foo.txt...
Running ci...OK
のように表示されます。
vc-default-back-end
使うシステムを指定します。 nil の場合は、インストールされていれば RCS を、されていなければ SCCS を使います。 この値を指定するときには
(setq vc-default-back-end 'SCCS)
のようにします。
vc-keep-workfiles
non-nil の場合、チェックイン時に workfile (編集しているファイル) を消しません。 特に理由が無い限り、何も考えずに t (default) にしておくのが良いでしょう。

また、変数ではありませんが、コメントに日本語を使いたい場合は、

(define-program-coding-system nil "ci" (cons *euc-japan*  *euc-japan*))
(define-program-coding-system nil "rcs" (cons *euc-japan*  *euc-japan*))
のようにすると良いようです (mule 2.3 の場合。それ以外は分かりません)。

6. それ以外のコマンドたち

ここまでで出てこなかったコマンドについてまとめて紹介しましょう。

C-x v h (vc-insert-headers)
ヘッダーを入れます。 デフォルトでは RCS の場合 $Id$ です。
変数 vc-header-alistvc-static-header-alist も参照して下さい。
C-u C-x v v (vc-next-action)
チェックインする際に revision を指定します。
同様に登録の際に revision を指定するには C-u C-x v i でもできます。
C-x v u (vc-revert-buffer)
現在編集中のファイルを破棄して、最後にチェックインされた状態に戻します。
C-x v c (vc-cancel-version)
最後の登録された revision を削除します。 つまり最後のチェックインを undo します (あんまり言い換えになってないな...)。 これを使うときには十分注意しましょう。 大事な変更点が失なわれるかもしれません (と言うか、最近失ないました (;_;))。
C-x v l (vc-print-log)
今までの log を表示します。
C-x v d (vc-directory)
カレントディレクトリーで登録されているファイルを dired で表示します。 prefix をつけると (つまり C-u C-x v d とすると) カレントディレクトリーの下も見にいきます。
vc-rename-file
ファイル名を付け替えます。 チェックインしてない時や他の人が lock しているときには使えません。 スナップショットもちゃんと update してくれます。

7. 最後に

思いのほか長くなってしまいましたが (こんなに詳しく書くつもりは全然無かったのですが...)、いかがですか? RCS のコマンド群を覚えなくても revision control ができるなんてとっても嬉しいですね。 え、その分 Emacs のコマンドを覚えなきゃならない? そんなことありません。 最低でも C-x v v だけ知っていれば十分です。 あとは必要になったときに使っていけば、自然に覚えていくもんです。 なんでもかんでも Emacs 上でやる。 これが基本です (ちょっと嘘かも)。

さて随所に書きましたが、私自身 RCS に詳しいわけではないので、この文書にも 随分誤りがあるのではないかと思われます。 特にスナップショットに関しては、少々自信がありません。 間違っているところを発見されましたら、ぜひ p-katoh@shiratori.riec.tohoku.ac.jp までメールして下さい。

A. お次は CVS (Concurrent Versions System) ?

Emacs 上で使うために pcl-cvs というのがあります。 (新しめの) CVS のアーカイヴに含まれています。 そのうち、pcl-cvs についても書く予定です (がいつになるか...)

Appendix. 関連リンク

いくつかの関係するページへのリンクです。
p-katoh@shiratori.riec.tohoku.ac.jp
Back to Home page

Last modified: Thu Aug 3 15:33:55 JST 2000