Hatena::ブログ(Diary)

彷徨えるフジワラ このページをアンテナに追加 RSSフィード

2011-12-15

Mercurial の文字コード設定

※ 2012年までの「追記」の表示を破棄/IntelliJ IDEA に関する注記を追加(@2013-12-30)

このエントリは、Mercurial Advent Calendar 2011 の 15 日目です。相変わらず話が長くなってしまって御免なさい .... orz

追記でではありますが、一応『ここまで読めば十分!』ポイントを明示しましたので、読む際の目安にして下さい。

※ このエントリでは、文字集合体系とその符号化に関する包括的な領域を、慣習的な点から「文字コード」と表現します。

Mercurial における日本語対応

Mercurial では、『Windows 上で Shift-JIS 形式で記録した日本語ログメッセージを、Unix/Linux 上で UTF-8 形式で参照』したり、その逆も可能です。

また、テキスト系ファイルの内容に関しては、(後述する) 明示的な変換の設定が無ければ、保存した際の文字コードのまま記録されます。

そのため、Mercurial での日本語対応が問題とされるのは、『日本語ファイル名のファイルを構成管理したい』という状況に限定される、と言っても良いでしょう。

ファイル名のための文字コード設定と、コミットログ等の記録に用いる文字コード設定は、同じ設定値が用いられることから、文字コード設定は『日本語ファイル名の使用』を軸に考えることになります。

なお、Mercurial に対する文字コード設定の際には:

  • コマンド起動やログ参照を行うターミナルソフト (or コマンドプロンプト@Win)
  • コミットログ入力に使用するエディタ
  • 子プロセスとして hg コマンドを起動するツール(例: Emacs の hg-mode)

などの周辺ツール群に対する文字コード設定との整合性にも注意が必要です。

# これは Mercurial に限った話では無いのですが....

文字コード設定早見表

以下の条件を両方満たすか否かで、Mercurial における文字コード設定の方針が異なります。

  • Windows とそれ以外 (Unix/Linux/MacOS X 等) のプラットフォーム間で成果を共有
  • ファイル名に日本語を使用

上記の両条件が必須となる場合、ファイル名文字コードには UTF-8 を採用する必要があります。

それ以外の場合、ファイル名に日本語を使わないのであれば、複数プラットフォーム間での作業成果は問題無く共有できますし、ファイル名に日本語を使う場合でも、作業プラットフォームに応じて:

  • Windows 限定 なら cp932 (いわゆる Shift-JIS) で運用
  • Windows 以外限定なら UTF-8 で運用

という選択が推奨となります。

Mac OS X との連携を想定した場合、EUC-JP などの文字コードは選択から除外されます。

それに加えて、Mac OS X のファイルシステムには、ファイル名正規化の問題があるため、ファイル名文字コードに同じ UTF-8 を使っている他のプラットフォームとの連携であっても、障害が発生する可能性がありますので、注意してください。

各環境/文字コードの組み合わせ毎に、以降で説明する確認項目との対応関係を以下に示します。

環境文字コード確認項目
Windows 以外UTF-8・環境変数設定
Windowscp932 ・環境変数設定
・Windows での cp932 固有設定
WindowsUTF-8 ・環境変数設定
・Windows での UTF-8 固有設定

なお、『作業プラットフォームが Windows 限定』というのは、あくまで『作業領域の使用 (= ファイルの改変や閲覧) が Windows 限定』なだけで、共有リポジトリは Unix/Linux/MacOS 上で運用可能です。

環境変数設定

環境変数による Mercurial への文字コード指定方法は、大きく以下の2種類に分類されます。

  • HGENCODING 環境変数
  • ロケール系環境変数設定

『ロケール系環境変数』とは、LC_ALL や LC_CTYPE、LANG といった環境変数です。

Mercurial 固有の環境変数である HGENCODING と異なり、ロケール系環境変数の設定は、国際化対応している他のコマンド群の挙動にも影響を及ぼします。

ロケール系環境変数が適切に設定されているならば、Mercurial を使うに当たって、改めて環境変数を設定する必要はありません。

しかし『他のコマンド挙動への影響を回避したい』などといった理由から、ロケール設定を無効化したいような場合は、HGENCODING 環境変数を使って Mercurial にピンポイントで文字コードを指定する必要があります。

環境変数による指定の場合、シェルの起動時設定ファイル (${HOME}/.bashrc 等) に、以下のような記述を行うのが一般的でしょう (※ 記述例は Bourne Shell 系のもの):

HGENCODING=utf-8; export HGENCODING

    あるいは

LC_CTYPE=ja_JP.utf-8; export LC_CTYPE

Windows の場合は、『コントロールパネル』⇒『システム』⇒『詳細設定』⇒『環境変数』経由で、ユーザ環境変数等に追加してください。

HGENCODING の場合は文字コード名のみの設定ですが、ロケール系環境変数の設定は、『言語名』 + "_" + 『国名』 + "." + 『文字コード』という構成要素を正しく選択する必要があります(LANG の場合は、言語設定のみでも大丈夫そうですが……)。

また、ロケール系環境変数の設定は、設定する変数に応じて有効範囲や優先度が定められています。あまり理解せずに設定すると、予期せぬ挙動に出くわす可能性がありますから、注意が必要です。

『Mercurial への文字コード指定』に限定すれば、設定の優先順位は以下のようになっています(LANGUAGE 環境変数は言語設定にのみ影響)。

  1. LC_ALL
  2. LC_CTYPE
  3. LANG

# 特にこだわりが無ければ、"LC_ALL=ja_JP.utf-8" とかで十分だと思いますが....

HGENCODING とロケール系環境変数の両方が設定されている場合、HGENCODING の設定が優先されます。

Windows でのロケール設定

Windows ネイティブ版の hg コマンド (※ TortoiseHg 付属のものを含む) のように、Windows ネイティブの Python 処理系を使用する場合は、ロケール系環境変数の設定内容は反映されません。

その代わり、OS レベルでの文字コード設定が有効になります (※ 日本語 Windows の場合のデフォルトは cp932)。

Cygwin でのロケール設定

1.7 版以降の Cygwin では、国際化対応強化の一環から、ファイル名指定時の文字コードとして UTF-8 を使用するようになりました。

# Cygwin のバージョンは "uname -a" で確認できます

そのため、ファイル名指定における文字コードのデフォルトは UTF-8 に設定されています。

例えば、explorer 等で作成した「あ」という名前のファイルにアクセスする場合でも、ロケール設定による文字コード指定に応じて:

  • cp932 なら 0x82 0xa0 の 2 バイト
  • UTF-8 なら 0xe3 0x81 0x82 の 3 バイト

という具合に、異なるバイト列でファイル名を指定する必要があります。

逆に言えば、本来 cp932 で扱わざるを得なかった Windows 上における日本語ファイル名も、Cygwin 環境上の Mercurial からであれば UTF-8 で扱うことが可能なわけです。

Windows での cp932 固有設定

Windows ネイティブ版の hg コマンドを使用する場合

Windows ネイティブ版の hg コマンドで、ファイル名の文字コードに cp932 を使用する場合、win32mbcs エクステンションを有効にする必要があります。

設定ファイルに以下のような記述を追加するか:

[extensions]

hgext.win32mbcs =

TortoiseHg の GUI 経由であれば、コンテキストメニューから『TortoiseHg』⇒『Global Settings』⇒『エクステンション』経由で win32mbcs エクステンションにチェックを入れてください。

『Repository Setting』経由による設定で、win32mbcs の要否をリポジトリ単位設定しても良いのですが、設定漏れによる失敗からの復旧等を考えた場合、『Global Settings』でのユーザ毎設定で有効化しておくことをお勧めします。

先述したように、日本語 Windows の場合はデフォルトの文字コードが cp932 に設定されているので、HGENCODING 環境変数の設定は不要です。

※ 注意: IntelliJ IDEA で Mercurial 連携を使用する場合は、環境変数 HGENCODING明示的な設定が必要です

Cygwin 環境の hg コマンドを使用する場合

先述したように、Cygwin の 1.7 版から、ファイル名の文字コードのデフォルト値が UTF-8 になりました。

そのため、ファイル名の文字コードに cp932 を使用する場合、ロケール系環境変数において文字コード cp932 の使用を明示する必要が有ります。

ファイル名の文字コードに影響を与えるロケール系環境変数が適切に設定されていれば、HGENCODING 環境変数の設定は特に必要ありません。

なお、Cygwin 環境の hg コマンドの場合は、文字コードに cp932 を使用する場合でも、win32mbcs エクステンションを有効化する必要はありません

# 有効化したくても、保護コードが入っているので有効になりませんが....

後述する『Windows 予約文字利用判定』に関する修正の一環で、cygwin 上でも win32mbcs エクステンションが利用できるようになりました。

但し、修正の取り込み先が default ブランチのため、エンドユーザが利用できるのは 2.1 版がリリースされる 2012/02 上旬からになります。

2.1 版以降で cp932 を使用する場合は、cygwin 環境でも win32mbcs を有効にすることをお勧めします。

Windows での UTF-8 固有設定

Mercurial では、ファイル名の文字コード設定と、コミットログの記録/参照で使用する文字コードが同じでなければなりません。

しかし、日本語 Windows 環境では、コマンドプロンプト経由での入出力を初めとして、多くの処理が cp932 を前提としています。

Windows 環境での Mercurial の文字コードとして UTF-8 を採用する場合は、環境整備等に関して、ある程度の知見が必要となりますので注意してください (TortoiseHg 等の GUI 操作に閉じる場合は、この限りではありません)。

Windows ネイティブ版の hg コマンドを使用する場合

Windows ネイティブ版の hg コマンドで、ファイル名文字コードに UTF-8 を使用するには、fixutf8 エクステンションを使用する必要があります。

しかし、Mercurial 本体の開発進行に対して、fixutf8 の対応状況は、正直あまり活発とは言えません。

例えば本エントリ執筆時点では、 2.0.x 版に対応出来ていないことから、1.9.x 版を使用せざるを得ない状況です。

かと言って、fixutf8 エクステンションを使わずに UTF-8 をファイル名文字コードとして使用した場合、Mercurial 以外の Windows ツール群からは、作業領域中のファイルを正しいファイル名で参照することが出来なくなってしまいます。

以上のことから、Windows ネイティブ版の hg コマンド使用における UTF-8 文字コードの採用は、私個人としては、積極的にお勧めできるものではありません。

もっとも、余程致命的なバグが見つかりでもしない限り、構成管理ツールは頻繁にアップデートする必要が無いことから、一旦動作確実なバージョンを導入してしまえば、比較的安定して使用できる、というのも事実です。

また、単に『最新の開発状況に追従していない』だけで、『実運用に耐えない』というわけでもありません。> fixutf8

運用上『どーしても Windows 上で UTF-8 を使いたい!』という方は、Mercurial 日本語 ML 宛てに問い合わせて頂ければ、僕よりも fixutf8 事情に詳しい方々から助言がもらえると思います(笑)。

ちなみに、fixutf8 を使用する場合は、HGENCODING 環境変数への UTF-8 設定を確実に実施してください。

Cygwin 環境の hg コマンドを使用する場合

繰り返しになりますが、1.7 版からの Cygwin 環境のファイル名文字コードは、UTF-8 がデフォルトになっています。

そのため、ファイル名の文字コードに UTF-8 を使用する場合は、特に新たな設定を行う必要はありません。

なお、この場合はコミットログ等の文字コードも UTF-8 となりますので、入力画面等の文字コード設定の確認も忘れないようにしてください。

ファイル名文字コードに UTF-8 を採用したリポジトリから、Cygwin 上の hg コマンドを使用して作業領域に取り出した日本語ファイル名のファイルは、explorer を初めとする他の Windows ツール群からも正しいファイル名で参照することが出来ます。

但し、同じリポジトリを (fixutf8 を有効化していない) TortoiseHg 等の Windows ネイティブな Mercurial ツールで参照した場合、当然ですが日本語ファイル名を正しく扱うことはできません。そのため:

Windows 環境上では、Cygwin 上の hg コマンドによる CUI (Character User Interface) 操作で十分

という割り切りが可能であれば、UTF-8 の採用を考えても良いでしょう。

以上で必要な設定は終了です!

ここまでの説明に従って環境設定を終えたなら、Mercurial のコミットログやファイル名に日本語が使用できる筈です。

想定通りの挙動とならない場合は、以下の点を再度確認した上で、適宜現象報告をお寄せください。

  1. これまでの説明に対して、設定内容が妥当か?
  2. エントリ末尾の既知の問題一覧に、該当するものが無いか?

環境変数設定系の問題の場合、『何を設定したか?』も重要ですが、『変更しないまま残っているものの影響は無いか?』という点も重要ですので、確認の際には参考にして下さい。

例: LC_CTYPE=ja_JP.cp932 は設定したが、LC_ALL=en_US.UTF-8 が残っている

※ 複数環境混在等が無いシンプルな運用の場合、以降の内容の重要度はそれ程高くありません

混在環境における日本語ファイル名使用問題の運用回避

作業上の都合で:

  • どうしても日本語ファイル名を使用したい、且つ
  • 非 Windows 環境から当該ファイルを参照/変更したい

というような要求を満たす必要があるケースは、個人的な経験から推測するに:

IT 錬度が低いメンバーが、Windows 環境上での GUI による作業を希望する

という事情が根本原因である場合が大多数では無いかと思います。

ちなみに『Windows での作業 ⇒ IT 錬度が低い』ではありません。あくまで『IT 錬度が低い ⇒ Windows + GUI での作業を希望』であって、むしろ Windows + GUI の強力な吸引力に感嘆するぐらいです(笑)。

このようなケースでは、ファイル名文字コードに UTF-8 を採用して、Windows 側で作業する錬度の低いメンバーの導入障壁を上げるよりは、非 Windows 環境側での作業を希望するメンバー側で、以下のような運用を考えてみてはいかがでしょうか?

  • 仮想化のゲスト OS として Windows を運用
  • ホスト側のディスク領域を Samba/CIFS 等で共有
  • ホスト側の文字コード設定は UTF-8
  • ゲスト側の文字コード設定は cp932
  • リポジトリ操作の実施は、ゲスト側に限定
  • ファイルの参照/改変は、ホスト/ゲスト双方で実施可能

UTF-8 設定のホスト側でうっかり update や commit 等を実施してしまわないように、『cp932 設定以外での実行禁止』フックを設定すれば更に安心です。

非 Windows 環境での作業を希望するメンバーは、一般に IT 錬度が高いですから、この程度であれば難なく運用できることでしょう。

ちなみに、Windows と Mac OS の混在環境で、いずれの環境の利用メンバーも IT 錬度が低い場合は....どうしましょうね?(^ ^;;;)

他の Unix 系 OS のファイルシステムなら、ファイル名が cp932 でも強引に運用できなくもないのですが、Mac OS X の HFS+ は仕様がちょっと独特なんですよねぇ。

いずれかの環境で統一した方が早い気もしますが、『開発系 = Windows』+『デザイン系 = Mac OS X』といった組み合わせの場合は、それも難しいでしょうし....

ひょっとしたら、Advent Calendar の 14 日目で紹介されている command server の機能を応用して:

  • 作業領域は、ファイル共有したリモート (or 仮想化ゲスト) 上の領域を使用
  • ファイル名の文字コードには UTF-8 を採用
  • ファイルの参照/改変は、ローカル側で実施
  • リポジトリ操作は、ローカルのフロントエンドからの要求を、リモート上のコマンドサーバで実施
  • 「ローカルフロントエンド 〜 リモートコマンドサーバ」での通信において、文字コードの変換を実施

みたいな感じのシステムを組めば、上手く運用することが出来るかも?という気がしますが、『IT 錬度が低い』人でも使えるようにするには、色々ブラックボックス化が必要なんですよねぇ。

# log とか diff で属性情報/ファイル名/内容が混在する場合は面倒かも...

まぁでも、今時開発に使用する機器のスペックなら、仮想マシン上の Unix 系ゲスト OS の応答性能は問題ないだろうから、『Mercurial の作業領域管理用の専用仮想マシン』というパッケージにしてしまう、という手はありかもしれません。

本体への日本語対応修正がひと段落した時点で、検証用のプロトタイプを作成してみようかなぁ。

ファイル内容文字コードの変換

※ 以下の内容を実践で適用するのは、ある程度 Mercurial 利用に習熟してからにしましょう

エントリ冒頭で述べたように、Mercurial の履歴管理では、ファイル内容の文字コードに関しては一切関知しません。

しかし、複数の文字コード設定の環境が混在している場合、これが問題となるケースがあります。

例えば、Unix 系 OS のコンパイラ/アセンブラは、文字コードに cp932 を採用したソースファイルを正しく処理できない可能性があります (具体例は後述)。

その一方で、UTF-8/EUC-JP といった文字コードは、既存の ASCII 文字との衝突が無いため、Windows 上のコンパイラ等でも処理可能です。

そのため、マルチプラットフォーム対応のソースコードに対して、日本語でコメントを付与する場合、文字コードには UTF-8/EUC-JP 等を使用するのが妥当です。

# 場合によっては ISO-2022-JP (JIS) の利用も視野に入れる必要がありますが....

しかし、『マルチプラットフォーム』を謳うのであれば、当然 Windows 上でも構成管理作業が発生します。

その際の Mercurial 利用における文字コードは、日本語 Windows 上の標準的な文字コードである cp932 を使用するのが一般的ですが、この場合の "hg log -p" 等の出力は:

  • コミットログ等の属性情報は cp932 で出力
  • ファイル内容は UTF-8 や EUC-JP で出力

という文字コード混在状態になってしまい、利便性がよろしくありません。

回避策としては:

  • hg コマンドへの "--encoding" グローバルオプション指定で、属性情報出力の文字コードを、一旦ファイル内容の文字コードにあわせる
  • hg コマンドの出力を iconv 等で変換することで、環境本来の文字コードに戻す

という組み合わせが考えられますが、都度このようなコマンド起動を行うのは面倒ですし、TortoiseHg や Emacs の hg-mode 等を経由して hg コマンドを実行する場合は、そもそもこのような手法自体に無理があります。

また、作業領域中のファイルに対して、一般的な grep コマンドを適用する場合でも、通常利用で想定される cp932 とは異なる文字コードの出力結果となってしまいます。

このような状況の改善策として、以下のような設定により、作業領域への取り出し/履歴記録におけるファイル内容の文字コードを変換する運用が考えられます。

[decode]
*.c = pipe: iconv -f utf-8 -t cp932
[encode]
*.c = pipe: iconv -f cp932 -t utf-8

上記の設定は、以下の文字コード変換を意味します:

  • .c 拡張子のファイルを変換
  • 作業領域への書き出し (decode) の際に、ファイル内容の文字コードを cp932 に変換
  • 履歴記録への取り込み (encode) の際に、ファイル内容の文字コードを utf-8 に変換

# iconv コマンドが使用可能であることを前提としています

この設定によって、hg コマンドがファイル内容を (ファイル or 標準出力に) 書き出す際の文字コードとして、cp932 を想定することが可能になります。

変更内容を履歴記録する際には、utf-8 に変換してから記録してくれますから、他の環境との作業成果の共有でも問題ありません。

さて、一見便利そうに見えるファイル内容の文字コード変換ですが、必ずしも万能ではありません。例えば、ファイルによっては以下のような文字コード種別が明示的に埋め込まれている可能性があります:

  • HTML や XML ファイルにおける charset 指定記述
  • Emacs 向けの local-variable 記述による文字コード指定コメント

# 多分 Vim とかにもあるんじゃないかなぁ.... > 文字コード指定用コメント記述

このような明示的な文字コード指定が埋め込まれたファイルに対して、内容の文字コードだけを変換した場合、両者の食い違いが元で、ツールによっては内容を正しく処理できない可能性があります。

それなら『明示的な文字コード指定記述込みで変換してしまえ』という話になるのですが、十分な検討をしない文字列 (and/or 正規表現) 置換は、想定外の変換結果を招く可能性があります。

# 最悪、不可逆変換だったりすると、目も当てられません....

以上のような理由から、手放しでお勧めできるわけではありませんが、ファイル内容の文字コード変換は、運用上の利便性向上策の選択肢の一つとして覚えておいて損はありません。

cp932 の問題

※ ここで述べる話は Mercurial に限った話ではありませんが、Mercurial における日本語処理の問題を理解する上で必要となる知識です

様々な文字コードに対応するためには、色々な配慮が必要なのですが、日本語環境で一般的に使用される cp932 は、とりわけ色々な配慮が必要な文字コードです。

一番根本的な問題は、この文字コードが以下のような特徴を持っていることに尽きます:

2バイト文字の2バイト目に、アルファベットや記号等の、ASCII コードで一般的な文字と同じ値のバイトが使用されている

例えば、カタカナの『ソ』は、cp932 なら 0x83 0x5c の2バイトで表現されますが、2バイト目の 0x5c は、ASCII で言うバックスラッシュ (\) に相当します。

バックスラッシュは:

  • Windows 上では、パスの区切り記号
  • 多くのプログラミング言語 (+ 正規表現) では、直後の文字のエスケープ

という、極めて特殊且つ重要な用途を持つ文字です。

cp932 を正しく認識できない環境で、このような文字を使用した場合、厄介な問題が発生します。

Windows 上で文字コード cp932 を意識せずに単純にバックスラッシュでパスを区切った場合、『ソ』の2バイト目で区切られてしまいますから、想定外のファイルへアクセスしようとしてしまいます。

# 例: 『重要なソフトウェア』⇒『重要な』+ 0x83 と『フトウェア』

また、以下のように、行末にたまたま 0x5c を含む文字が来てしまった場合:

    // コメントでゲソ
    result = doit();

0x5c によって改行がエスケープされることで、実質的には以下のようなソースとして解釈されてしまい、doit() 呼び出しは実施されません。

    // コメントでゲソ    result = doit();

あるいは、hg などのコマンドが起動される前の段階で、bash 等によるバックスラッシュの解釈が走ってしまう点にも注意が必要です。

例えば『ソa』(0x83 0x5c 0x61) という引数を指定した場合、bash が『ソ』の2バイト目のバックスラッシュが、それに続く 'a' に対するエスケープ文字だと解釈してしまうため、この引数が hg コマンドに渡される時点では、『ヂ』(0x83 0x61) になってしまいます:

$ echo ソa
ヂ
$ echo 'ソa'
ソa
$

引数をコマンドに正しく渡すためには、bash による解釈を抑止するために、シングルクォート等で囲む必要があります。

# ダブルクォートの場合、閉じクォートがエスケープされてしまう危険があります

これは、LC_CTYPE 等による cp932 の使用を明示していたとしても、回避することができません (少なくとも現行の bash 実装では回避できません)。

Emacs の hg-mode や、TortoiseHg などの GUI を経由する場合、hg コマンド起動に先立って余計な解釈が実施される余地がありませんので、このような問題は発生しませんが、コマンドラインで作業する場合は注意が必要です。

もう一つ、別の問題の例も見てみましょう。

『ア』 (0x83 0x41) と『ヂ』 (0x83 0x61) の場合、1バイト目が同一で、2バイト目にそれぞれ 'A' (0x41) と 'a' (0x61) に相当する文字を使用していることから、cp932 を意識せずに ASCII アルファベットの大文字化/小文字化を実施すると、『ア』と『ヂ』が同一と判定されてしまう、という問題を抱えています。

Mercurial の場合、単にログ検索等のキーワード指定で問題になるだけでなく、大文字/小文字を区別しないファイルシステム (case insensitive filesystem) のために、ファイル名の衝突を判定する処理において、本来衝突しない筈のファイル名間で不当な衝突判定をしてしまう、という問題の原因となります。

まぁ、こういった問題のある文字コードを使っているお陰で、日本語圏のプログラマは可搬性等に対して比較的意識が高い、という副作用もあるのですが、出来ればこういった苦労はしたくないものですねぇ (^ ^;;;;)

Mercurial の文字コード設定に関する背景

※ ここで述べる話は、通常の Mercurial 利用においては、特に必須のものではありません

Mercurial における文字コード設定は、以下のような影響があります。

対象履歴への記録形式文字コード設定の影響
属性情報UTF-8あり
ファイル名生データあり
ファイル内容生データなし

『属性情報』は、ユーザ名やコミットログといった、履歴記録の際に付加的に記録される情報を指します。

上記の挙動一覧において、『属性情報』や『ファイル内容』と文字コード設定の関係は、単純でわかりやすいと思います。

『属性情報』は、コマンド行等で指定された文字列を、文字コード設定に従って UTF-8 化したものを履歴に記録します。

参照の際には UTF-8 からの文字コード変換が実施されますので、実行する環境において Mercurial の文字コード設定が適切に実施されていれば、履歴記録時点と異なる文字コードで情報を参照することも可能です。

『ファイル内容』の場合は、逆に文字コード設定の影響を全く受けませんので、どの環境においても、同じ内容を参照することができます。

その一方、これまで見て来たように、ファイル名と文字コードの関係は少々複雑です。

履歴への記録の際には、後から任意の文字コードに変換できる UTF-8 のような形式ではなく、記録時点の環境における文字コードのままの「生データ」で記録されます。

しかし、同じ「生データ」を記録する『ファイル内容』とは異なり、文字コード設定の影響は (多少なりとは言え) 受けてしまうのです。

この点が『Mercurial は日本語ファイル名の扱いが云々』との批判を受ける原因なのですが、Mercurial の開発リーダーである Matt Mackall 氏が以下のような方針を持っていることから、少なくとも半年〜一年ぐらいのスパンでは、この仕様が変更されることは無いと思われます。

ファイル名の文字コード変換を行う場合、make や ANT の設定ファイルや、HTML でのリンクのように、ファイル名が記載されたファイル内容に関しても文字コード変換をしないと、成果物全体での整合性が取れない。

単に『unicode 管理して都度変換すれば良い』という問題では無い。

ファイル名の文字コードと、ファイル内容の文字コード間での不整合に関しては、先述したように、作業領域への取り出しの際に、ファイル内容の文字コードを変換してしまう、という手が使えなくも無いのですが、単なる文字コード変換では不十分なケースも多々あるため、確かに一筋縄では行かない問題なんですよねぇ。

本エントリ公開後に、Matt から開発者 ML へ、以下のようなメールが投函されました!

『親リビジョンのファイル名が全て UTF-8 valid だった場合に限定して、Windows 環境の unicode API を叩く』ってのが、妥当なんじゃねぇ?(超意訳)

『This is a pretty severe change in terms of internal API』と書かれているので、早々に対応が完了するものでは無いとは思いますが、2回年を越える事は無いんじゃないですかね?かなり期待が高まります!

Mercurial のコアチーム内では、上記『Unicode API 利用』に関する修正作業は進行していない、とのことなので、日本人開発者で対応修正を実施することになりました!

開発にあたっての議論/情報交換は、mercurial-devel-ja ML で行っていますし、検討過程/結果等は bitbucket 上のリポジトリで公開しています。

現在作業中/対応予定の日本語関連の修正

とりあえず、現時点で私の把握している Mercurial の日本語対応関連での問題点を列挙しておきます。

2.0.1 版時点の実装で cp932 を使用した場合、以下の問題が発生する可能性があります:

  • (1) log コマンドでのキーワード指定 (-k/--keyword) 等で、大文字/小文字無視判定の影響により、合致する筈の無いリビジョンで合致判定してしまう
  • (2) grep コマンドの正規表現指定において、特殊文字との誤判定により、正規表現が正しく評価されない

# これらは win32mbcs エクステンション併用でも解消されません

また、Cygwin 環境限定ですが、日本語ファイル名を cp932 で使用した場合に以下の問題が発生する可能性があります:

  • (3) 日本語ファイル名同士の衝突判定で、不当な衝突判定を下してしまう
  • (4)「Windows 予約文字の使用」と誤認され、不当な警告 (or 処理中断) が発生する

(3) に関しては、現在私自身が case insenstive filesystem 対応強化の一環で修正作業を実施している最中ですので、上手くいけば年末にリリースされる 2.0.2 版あたりで改善される筈です。

# されるといいなぁ.... (^ ^;;;)

日本語ファイル名に関する修正は概ね取り込まれましたが、NTFS や HFS+(MacOS X) などの case insensitive filesysmte 上で Mercurial Queue (MQ) を多用し、且つ文字大小の混在するファイル名を使用する場合、問題が発生する可能性があるため、2.1 版のリリースを待つことをお勧めします。

(1) は mercurial/commands.py以下のあたりの "lower()" 適用を、encoding.lower() 適用に置き換える作業と、修正確認のテストを書けば良いので、遅くても次の次のリリース (2012/02 頭) には間に合うかな?

if opts['user'] and not [k for k in opts['user']
                         if k.lower() in ctx.user().lower()]:
    return
if opts.get('keyword'):
    for k in [kw.lower() for kw in opts['keyword']]:
        if (k in ctx.user().lower() or
            k in ctx.description().lower() or
            k in " ".join(ctx.files()).lower()):
            break
    else:
        return

log -k/-u 向けをはじめとする修正が default ブランチに取り込まれましたので、2012/02 初旬にリリース予定の 2.1 版以降で改善されます。

(2) は正規表現が絡むので、ちょっと厄介かなぁという印象が。

正規表現パターンを unicode 変換しちゃった場合、多バイト文字との合致を正しく行うためには、判定対象文字列も unicode 化しないと駄目ですよねぇ? > python 使いの皆様

そうなると、実行性能低下要因となってしまうので、何か回避策を考えないと Matt が受理してくれない可能性が....

(4) に関しては mercurial/util.py以下の箇所で、文字列 n から「1バイト」毎に切り出している for ループ処理を、「1文字」毎に切り出す処理にしてやる必要が。

for c in n:
    if c in _winreservedchars:
        return _("filename contains '%s', which is reserved "
                 "on Windows") % c

書いてて思い付いたけど、合致判定対象の _winreservedchars は ':*?"<>|' という結構特殊な内容なので、別に「1文字切り出し」にこだわらなくても、UTF-8 化した結果を1バイトづつ処理すれば、判定としては十分なのかな?それなら、1文字毎にコード変換を実施する必要がなくなるから、大きな性能劣化は回避できるかも。

パッチを投函したところ、『それって win32mbcs で改善される話じゃねぇ? by Matt』(意訳) とういことで、cygwin 固有な事情で実装を変更するのではなく、win32mbcs を cygwin 上でも利用可能にする方向で対応することに。

上記のものに関して、『そんな程度の修正なら、俺様がパパッとやってやるぜ!』という男気溢れる方は、是非是非修正パッチの投函を!(笑)

以上の箇所以外にも、潜在的な問題を洗い出すために、文字列に対して replace/split 系の処理をしている箇所や、正規表現を処理に使用している箇所に関しては、個別に確認をする必要はあるかなぁ、などと考えてます。

# lower/upper に関する見直しは (3) 絡みで実施済み

とりあえず、なんだか尻すぼみになってしまいましたが、上記以外にも『これってバグじゃねぇ?』という挙動に心当たりのある方は、以下のようなルート経由で、是非ともお知らせ下さい。もちろん日本語利用とは関係無い不正挙動情報も歓迎です!

はてなユーザーのみコメントできます。はてなへログインもしくは新規登録をおこなってください。