ロック

Subversion の 「コピー・修正・マージ」 モデルは プログラムソースコードのように行を基本としたテキストファイルからなる プロジェクト上で共同作業する場合には最適です。しかし ロックが必要な場合で議論した ように、時には Subversion の標準的な共同作業モデルのかわりに 「ロック・修正・ロック解除」モデルを使わなくてはならないことも あります。ファイルがバイナリデータから構成されている場合、異なるユーザに よる二つの修正をマージするのは困難であったり不可能なことがよくあります。 このため Subversion 1.2 とそれ以降ではロック、 あるいは他のバージョン管理システムでは「保護されたチェックアウト (reserved checkouts)」として知られている機能を提供しています。

Subversion のロック機能は主に二つの目標があります:

Subversion のロック機能は現時点ではファイルだけに制限されています— ディレクトリツリー全体へのアクセスに対する利用はまだできません。

ロックの作成

Subversion リポジトリではロックとは あるユーザがファイルを修正する排他的な権限を与える小さなメタデータ です。このユーザはロックの所有者と呼ばれます。 ロックごとにユニークな識別子があり、普通これは長い文字列の形を したもので、 ロック・トークンと言われます。 リポジトリは独立したテーブル中にロック情報を管理し、コミット操作の最中に 強制的にロックをかけます。コミットのトランザクションがファイルを修正 または削除しようとした場合(あるいはファイルの親を削除しようとした場合)、 リポジトリは二つの情報を要求します:

  1. ユーザ認証。 コミットを実行しようとするクライアントはロック所有者として認証されなくては なりません。

  2. ソフトウェアによる認可 。ユーザの作業コピーはコミットと共にロック・トークン を送信しなくてはならず、これによってどのロックを利用中であるかを正しく知る ことができます。

以下の例によって順序よく説明していきます。Harry が JPEG 画像を修正することに 決めたとしましょう。他の人たちがそのファイルに対する修正をコミットしないように 彼はリポジトリ中のファイルをsvn lockコマンドによって ロックします:

$ svn lock banana.jpg --message "Editing file for tomorrow's release."
'banana.jpg' locked by user 'harry'.

$ svn status
     K banana.jpg

$ svn info banana.jpg
Path: banana.jpg
Name: banana.jpg
URL: http://svn.example.com/repos/project/banana.jpg
Repository UUID: edb2f264-5ef2-0310-a47a-87b0ce17a8ec
Revision: 2198
Node Kind: file
Schedule: normal
Last Changed Author: frank
Last Changed Rev: 1950
Last Changed Date: 2005-03-15 12:43:04 -0600 (Tue, 15 Mar 2005)
Text Last Updated: 2005-06-08 19:23:07 -0500 (Wed, 08 Jun 2005)
Properties Last Updated: 2005-06-08 19:23:07 -0500 (Wed, 08 Jun 2005)
Checksum: 3b110d3b10638f5d1f4fe0f436a5a2a5
Lock Token: opaquelocktoken:0c0f600b-88f9-0310-9e48-355b44d4a58e
Lock Owner: harry
Lock Created: 2005-06-14 17:20:31 -0500 (Tue, 14 Jun 2005)
Lock Comment (1 line):
Editing file for tomorrow's release.

前の例には新しい話がたくさん含まれています。まず Harry は svn lock--message オプションを 渡しています。svn commitと同様、svn lockコマンドは( --message (-m)または --file (-F)オプションによって)コメントをつけてそのファイル をロックした理由を説明することができます。svn commitと違うのはsvn lockは 自分の好きなエディタによる メッセージを常に要求するわけではないところです。ロックのコメントは オプションですが、コミュニケーションを円滑にするためにつけることを お勧めします。

つぎにロックが成功しています。これはそのファイルはまだロックされて いなかったこと、そして Harry がそのファイルの最新バージョンを得たことを 意味しています。もし Harry の作業コピー中のファイルが古いものであれば リポジトリはその要求を拒否し、Harry に対してまずsvn update を実行してから再びロックコマンドを発行するように要求します。

リポジトリ中にロックを作成した後、作業コピーはロックについての情報 をキャッシュすることにも注意してください—そのうち最も重要なのは ロック・トークンです。ロック・トークンの存在は非常に重要です。作業コピー はそれによって後でロック機能の認可を受けるからです。 svn statusコマンドはファイル名のとなりに (locKed の省略として)Kを表示しロック・トークンが存在 していることを示します。

これで Harry は banana.jpg をロックし、Sally は そのファイルを修正したり削除したりできなくなりました:

$ whoami
sally

$ svn delete banana.jpg
D         banana.jpg

$ svn commit -m "Delete useless file."
Deleting       banana.jpg
svn: Commit failed (details follow):
svn: DELETE of
'/repos/project/!svn/wrk/64bad3a9-96f9-0310-818a-df4224ddc35d/banana.jpg':
423 Locked (http://svn.example.com)

しかし Harry は banana の色合いをもう少し黄色くしたあと、その変更点を コミットすることができます。理由はロック所有者としての認可を受けている からであり、彼の作業コピーにはそのための正しいロック・トークンがあるためです:

$ whoami
harry

$ svn status
M    K banana.jpg

$ svn commit -m "Make banana more yellow"
Sending        banana.jpg
Transmitting file data .
Committed revision 2201.

$ svn status
$

コミット実行後 svn status がロック・トークンが もう作業コピーに存在していないことを示しているのに注意してください。 これが svn commitの普通の動作です: 作業コピーを (あるいは一覧表を用意していた場合はそのリストを)調べてコミット トランザクションの一環として検出したすべてのロック・トークンをサーバに 送信します。コミットが成功した後で今回関係していたリポジトリ中のすべて のロックは解除されます—そしてこれはコミット対象とは ならなかったファイルにたいしてもそうなります。 この理由はユーザがみだりにロックしないようにすること、そしてあまり長く ロックし続けないようにするためです。たとえば Harry はおおざっぱに imagesという名前のディレクトリ中にある 30 個のファイル にロックしたとします。どのファイルを変更したいのかはっきりしていなかった からです。最終的に、彼は 4 個のファイルに対してだけ修正を加えました。 svn commit imagesを実行するときそのプロセスは残りファイルも 含めた 30 個すべてのロックを解除するでしょう。

この動作は svn commit--no-unlockオプション を指定することで上書きできます。これは修正をコミットしたいが、 さらに別の変更する計画があり、ロックを残しておく必要があるような場合に 一番よく使われます。この動作は実行時 configファイル にno-unlock = yesを設定することによって半永久的に 調整することもできます(実行時設定領域項 を見てください。)

もちろんファイルをロックした後、修正を必ずコミットしなくてはならない という義務はありません。ロックは単にsvn unlock コマンドを利用していつでも解除することもできます:

$ svn unlock banana.c
'banana.c' unlocked.

ロック状況の調査

誰かがロックしているせいでコミットに失敗した時には、原因は割と簡単に 調べることができます。一番簡単な方法は svn status --show-updates を実行することです:

$ whoami
sally

$ svn status --show-updates
M              23   bar.c
M    O         32   raisin.jpg
       *       72   foo.h
Status against revision:     105

この例では Sally は自分の作業コピーの foo.h が古いだけでなくコミットしようと思っている二つの修正したファイルの 片方はリポジトリ中でロックされていることもわかります。 Oの記号は 「Other(他の)」 の意味で ロックがファイル上に存在していてそれをしたのは誰か別の人である という意味になります。彼女がコミットしようとしてもraisin.jpg 上のロックが邪魔をするでしょう。Sally はさらにロックしたのは誰で、いつ、 どうしてロックしたのかも知りたいとします。今度は svn info が答えを教えてくれます:

$ svn info http://svn.example.com/repos/project/raisin.jpg
Path: raisin.jpg
Name: raisin.jpg
URL: http://svn.example.com/repos/project/raisin.jpg
Repository UUID: edb2f264-5ef2-0310-a47a-87b0ce17a8ec
Revision: 105
Node Kind: file
Last Changed Author: sally
Last Changed Rev: 32
Last Changed Date: 2005-01-25 12:43:04 -0600 (Tue, 25 Jan 2005)
Lock Token: opaquelocktoken:fc2b4dee-98f9-0310-abf3-653ff3226e6b
Lock Owner: harry
Lock Created: 2005-02-16 13:29:18 -0500 (Wed, 16 Feb 2005)
Lock Comment (1 line):
Need to make a quick tweak to this image.

svn infoは作業コピー中のオブジェクトの調査にも利用 できますが、リポジトリ中のオブジェクトに対しても調査することができます。 svn infoの引数で作業コピーのパスを指定した 場合には作業コピーにキャッシュされているすべての情報が表示されます; ロックに関する上記メッセージのすべては作業コピーがロック・トークンを 持っていることを示しています。(ファイルが別のユーザか別の作業コピーに よってロックされている場合、作業コピーパスでのsvn info はロックに関する情報をまったく表示しません)。 svn infoの引数が URL なら情報はリポジトリ中のオブジェクトの 最新バージョンに関するものになります; ロックについての表示はそのオブジェクト の現在のロック状況を示しています。

それで今回の具体的な例の場合、Sally は Hally が「ちょっとした 修正」のために 2 月 16 日にそのファイルをロックしたことが わかります。今は 6 月であるので、Sally は多分 Hally は自分がロックしたことを 忘れてしまっているのではないかと考えます。彼女は Harry に電話してロック をはずしてくれるように頼むかも知れません。彼がつかまらなければ彼女は 自分で強制的にロックを解除するか、システム管理者にそうしてもらうように頼む かも知れません。

ロックの解除と横取り(steal)

リポジトリのロックは不可侵のものではありません; それはロックした人によっても あるいはまったく別の人によっても解除することができます。ロック作成者以外 の別の人がロックを取り除いた場合、ロックは解除された と言います。

管理者にとってはロックを解除するのは簡単です。svnlooksvnadmin プログラムはリポジトリに対して直接 ロック状況を表示したり解除することができます。(これらのツールに関しての より詳しい情報は 管理者用ツールキット項を見てください。)

$ svnadmin lslocks /usr/local/svn/repos
Path: /project2/images/banana.jpg
UUID Token: opaquelocktoken:c32b4d88-e8fb-2310-abb3-153ff1236923
Owner: frank
Created: 2005-06-15 13:29:18 -0500 (Wed, 15 Jun 2005)
Expires: 
Comment (1 line):
Still improving the yellow color.

Path: /project/raisin.jpg
UUID Token: opaquelocktoken:fc2b4dee-98f9-0310-abf3-653ff3226e6b
Owner: harry
Created: 2005-02-16 13:29:18 -0500 (Wed, 16 Feb 2005)
Expires: 
Comment (1 line):
Need to make a quick tweak to this image.

$ svnadmin rmlocks /usr/local/svn/repos /project/raisin.jpg
Removed lock on '/project/raisin.jpg'.

さらに興味深いオプションがあって、ネットワーク越しに人のロックを 解除することができます。これには unlock コマンドに対して --forceを渡すだけです:

$ whoami
sally

$ svn status --show-updates
M              23   bar.c
M    O         32   raisin.jpg
       *       72   foo.h
Status against revision:     105

$ svn unlock raisin.jpg
svn: 'raisin.jpg' is not locked in this working copy

$ svn info raisin.jpg | grep URL
URL: http://svn.example.com/repos/project/raisin.jpg

$ svn unlock http://svn.example.com/repos/project/raisin.jpg
svn: Unlock request failed: 403 Forbidden (http://svn.example.com)

$ svn unlock --force http://svn.example.com/repos/project/raisin.jpg
'raisin.jpg' unlocked.

サリーが最初に unlock に失敗したのは自分の作業コピー中のファイルに対して 直接 svn unlockを実行したのに、そこにはロックが存在 していなかったためです。リポジトリから直接ロックを取り除くには svn unlock に URL の引数を渡す必要があります。 URL をロック解除しようという最初の試みには失敗していますが、それは ロック所有者の認可を受けていない(し、ロック・トークンも持っていない)ため です。しかし --force オプションを渡すと、認証と認可の 要求は無視され、他の人によって作成されたロックは解除されます。

もちろん単にロックを解除するだけでは十分ではないでしょう。上記の例では Sally は Harry がずっと長いこと忘れていたロックを解除したいだけではなく 自分自身の作業のためにそのファイルを再ロックしたいのが普通でしょう。 svn unlock --forceのあとで svn lockを実行すれ ばうまくいきます。しかし、この二つのコマンドの間に誰か別の人がロックして しまうわずかな可能性があります。もっと簡単な方法はロックを 横取りする(steal)ことであり、これはロック解除と 再取得を不分割な一まとまりの処理として実行します。これには svn lock--forceオプションを 指定します:

$ svn lock raisin.jpg
svn: Lock request failed: 423 Locked (http://svn.example.com)

$ svn lock --force raisin.jpg
'raisin.jpg' locked by user 'sally'.

ロックを解除しようと横取りしようと、Harry はびっくりするかも知れません。 Harry の作業コピーにはまだ最初にロックを取得したときのロック・トークンがある のにロックそのものはもう存在していないからです。そのロック・トークンは 無効になった(defunct)と言います。 そのロック・トークンによって表されているロックは解除されたか(すでに リポジトリに存在していない)、横取りされたか(別のロックに置き換わった) のいずれかです。どちらの場合も Harry はリポジトリに対して svn statusコマンドを実行することで様子をつかむことが できます:

$ whoami
harry

$ svn status
     K raisin.jpg

$ svn status --show-updates
     B         32   raisin.jpg

$ svn update
  B  raisin.jpg
 
$ svn status

$

リポジトリロックが解除された場合、 svn status --show-updatesはファイルの隣に B (Broken の意味)の記号を表示します。 古いトークンに変わって新たしいロックが存在している場合だと T (sTolen)の記号を表示します。 また svn update は無効になったすべての ロック・トークンを表示し、作業コピーから取り除きます。

ロックのコミュニケーション

svn locksvn unlock がどうやってロックを作ったり解放したり強制解除したり横取りしたりするか を見てきました。これは特定のファイルに対する直列化したコミットをしたい という目標を満足するものです。しかし、作業時間を無駄にしないという、 より大きな問題についてはどうなのでしょうか?

たとえば Harry がある画像ファイルをロックしてから編集し始めたとしましょう。 いっぽう、かなり離れた場所にいる Sally も同じことがしたかったとします。 彼女は svn status --show-updatesを実行することを知らないので Harry が 既にそのファイルをロックしていることを知ることができません。彼女は そのファイルを何時間かかけて編集し、その自分の修正点をコミットしようとして はじめてそのファイルはロックされているか、彼女のファイルが最新ではない ことに気づきます。どうであれ、彼女の変更は Harry のものとマージすることが できません。二人のうちのどちらかが自分の作業を捨てなければならず 多くの時間が無駄になります。

この問題に対する Subversion での解決策は編集を始める前に ユーザにそのファイルをまずはロックすべきであることを思い起こしてもらうための 仕組みを提供することです。

この仕組みは特殊な属性をsvn:needs-lock用意することで 実現しています。この属性がファイルにつくと(この場合の属性値はどのようで あってもかまいません)、ファイルは読み込み専用のパーミッションを持つように なります。ユーザがファイルをロックし、ロック・トークンを取得するとファイルは 読み書き可能となります。ロックが解放されると—これは明示的にロック解放する かコミットを通じて自動的に解放されるかのどちらかですが— ファイルは再び 読み込み専用に戻ります。

こうすることで、画像ファイルにこの属性がついている場合、Sally は 編集のためにファイルを開いた時に何かおかしなことになっていることに すぐに気づくはずです。彼女が使っているアプリケーションはそのファイル が読み込み専用であることを伝えます。これで彼女は編集前にそのファイルを ロックしなくてはならないことを思いだし、こうして既に存在しているロック に気づくことになります:

$ /usr/local/bin/gimp raisin.jpg
gimp: error: file is read-only!
$ ls -l raisin.jpg
-r--r--r--   1 sally   sally   215589 Jun  8 19:23 raisin.jpg
 
$ svn lock raisin.jpg
svn: Lock request failed: 423 Locked (http://svn.example.com)
 
$ svn info http://svn.example.com/repos/project/raisin.jpg | grep Lock
Lock Token: opaquelocktoken:fc2b4dee-98f9-0310-abf3-653ff3226e6b
Lock Owner: harry
Lock Created: 2005-06-08 07:29:18 -0500 (Thu, 08 June 2005)
Lock Comment (1 line):
Making some tweaks.  Locking for the next two hours.

最良の方法」は、ユーザも管理者もお互いにマージすることが できないようなファイルには常に svn:needs-lock 属性をつけておくというものです。この技法はロック機能を利用する上での 良い習慣であり、無駄な作業を防ぐことができます。

この属性はロックシステムとは独立して機能するコミュニケーション用の 仕組みであることに注意してください。言いかえるとどのようなファイル も、このプロパティーがあるかどうかにかかわらずロックすることが できます。逆にこの属性の存在だけで、コミット時にリポジトリから常に ロックを要求されるということにはなりません。

これで完璧とはいきません。ファイルがこの属性を持っていたとしても 読み込み専用の警告機能が常に動作するとは限りません。アプリケーション の間違った動作によっては、警告を出さずに黙ってそのファイルに対する 編集を許し、保存してしまう結果、読み込み専用ファイルを「乗っ取って」 しまうこともあるでしょう。残念なことにこのような状況に対して Subversion ができることはあまり多くはありません。