ksaitoの日記

日々試したことの覚え書き

URLエンコードとSubversion

SubversionApacheを組み合わせたソースコードリポジトリの場合、URLエンコードが原因でおかしな動きをすることがあります。

現象

ローカルの作業エリアでは、差分やステータスを見るのは問題ないのですが、コミットしようとすると下記のようなエラーが出ます。

$ echo bbb >> test.txt 
$ svn st
M       test.txt
$ svn diff
Index: test.txt
===================================================================
--- test.txt	(リビジョン 5)
+++ test.txt	(作業コピー)
@@ -1 +1,2 @@
 aaa
+bbb
$ svn commit -m "update"
送信しています              test.txt
ファイルのデータを送信しています .svn: コミットに失敗しました (詳しい理由は以下のとおりです):
svn: '/svn/sandbox/jp/%E3%83%86%E3%82%B9%E3%83%88/test.txt' の MERGE レスポンスはマージ先 ('/svn/sandbox/jp/%e3%83%86%e3%82%b9%e3%83%88') の子ノードではありません
$ 

再度、コミットしようとするとリポジトリが更新されているのでupdateするように言われます。

$ svn commit -m "update"
送信しています              test.txt
svn: コミットに失敗しました (詳しい理由は以下のとおりです):
svn: ファイルまたはディレクトリ 'test.txt' はリポジトリ側と比べて古くなっています。update を実行してみてください
svn: resource out of date; try updating
$ 

updateすると、なぜかマージされたGというステータスで更新されます。

$ svn update
G    test.txt
リビジョン 6 に更新しました。
$ 

なぜでしょう?

原因

チェックアウトしたファイルは、"http://svn.local/svn/sandbox/jp/テスト/"という全角文字を含むURLでした。

$ svn ls http://svn.local/svn/sandbox/jp/テスト/
test.txt
$

実は、この作業エリアをチェックアウトした人がURLを間違えないようにブラウザを使ってアクセスできることを確認し、ブラウザのアドレスバーからURLをコピペしてチェックアウトしていました。

ところが利用するブラウザによってアドレスバーのURLのコピーが異なります。
手元にあるブラウザで試してみると...

ブラウザ コピーされる文字列
Safari 4.0.4 日本語文字列 http://svn.local/svn/sandbox/jp/テスト/
firefox 3.5 URLエンコードでアルファベット大文字 http://svn.local/svn/sandbox/jp/%E3%83%86%E3%82%B9%E3%83%88/
IE 7 URLエンコードでアルファベット小文字 http://svn.local/svn/sandbox/jp/%e3%83%86%e3%82%b9%e3%83%88/

RFCでは、URLエンコードは、一文字毎に%XX(XXは、1文字の16進数文字列)となっておりA〜Fまでの大文字小文字の指定はなく、どちらかと言えば大文字を使うのが良いとされているようです。
そのためApacheは、どのURLでも同じコンテンツを表示します。

$ svn ls http://svn.local/svn/sandbox/jp/テスト
test.txt
$ svn ls http://svn.local/svn/sandbox/jp/%E3%83%86%E3%82%B9%E3%83%88/
test.txt
$ svn ls http://svn.local/svn/sandbox/jp/%e3%83%86%e3%82%b9%e3%83%88/
test.txt
$ 

サーバ側は、URLの違いを気にしないためコミットのリクエストを受け付けてコミット自体は成功します。
一方で、Subversionクライアントは、大文字小文字を区別し、かつコミット時にはURLエンコードは大文字が望ましいという慣例に従っているため小文字のURLエンコードでチェックアウトしていた場合、URLが違うと認識して今回のような現象となります。

判別方法

判別方法は、.svn/entriesに記録されているチェックアウトもとのURLで確認できます。
下記のように小文字を使ってURLエンコードされていたらアウトです。

$ grep http .svn/entries 
http://svn.sa.local/svn/sandbox/jp/%e3%83%86%e3%82%b9%e3%83%88
http://svn.sa.local/svn/sandbox
$ 

対処方法

コミットでエラーが出ますが、コミット自体は正しく行われます。
なので、他のディレクトリに正しいURL(全角文字列か大文字のURLエンコード)でチェックアウトして、コミットが正しく反映されているかdiffします。
問題なければ、小文字のURLエンコードの作業ディレクトリは、削除すれば対処できます。