UTF-8ファイル名対応版 Git for Windows

注: 本家 msysGit が UTF-8 ファイル名に対応したため、本ページの内容は古くなっています。

はじめに

現時点では、Git for Windows は残念ながら UTF-8のファイル名を正しく扱うことができません。Shift-JIS で無理やり日本語を扱う こともできますが、このリポジトリを Linux などで取り出すと当然ながら文字化けします。

この問題は msysGit 本家のほうでも認識されています(Issue 80番を参照)が、本家は対処する気なさげな感じです (Priority が low になってるし)

そこで、Git for Windows にパッチを当て、UTF-8 ファイル名を正しく取り扱えるようにしました。 一応、TortoiseGit などから普通に日本語ファイル名を扱えることを確認できています。

ダウンロード

テスト版ですので、使用上不具合が起こる可能性が皆無ではありません。ご了承下さい。不具合などがある場合は github の Issues にお願いします。

ソースコード

github にて公開しています(utf8-filepathブランチ)。msysGit 本家にはパッチを送付済み ('11/8月現在、work/tm/utf8 ブランチに取り込まれましたが、devel ブランチにはまだマージされていません)

既知の問題

技術内容解説

そもそもなぜ文字化けがおこるのか

Windows の API で文字列を扱うものには、ANSI 版/Unicode版の2種類があります。ANSI 版は Win16 時代の API を引きずっていて、日本語Windows の場合は CP932 (Shift-JIS の変種)を文字エンコーディングとして使用します。文字列はマルチバイトになりますので、char の配列で表現する形になります。

これに対して、Unicode版 の API は Windows NT で導入されたもので、文字列は UTF-16 で表現します。文字の単位は char ではなく wchar_t、すなわち 16bit を1単位として表現します。通常使用するたいていの文字は16bitで表現できますが、一部の文字(具体的には Unicode の BMP 外の文字)はサロゲートペアを使用して 32bit で表現されます。

例えば fopen() は ANSI 版の API で、UNICODE 版は _wfopen() という API になっています。また、Win32 API に関しては、例えば ANSI 版は CreateFileA、UNICODE 版は CreateFileW というように、末尾に A か W が付いています。

このあたりの詳細は Wikipedia の Windows API - 4.1 Unicode対応あたりをみていただくとわかりやすいかと思います。

さて、POSIX のシステムコール (上に上げた fopen とか)は、Windows API では ANSI版のほうに対応づけられているため、文字列は ANSI (日本語の場合は CP932)で渡さなければなりません。このため、これらのシステムコールに UTF-8 の文字列を渡してしまうと文字化けが発生します。

修正内容について

以下のような方針で修正しています。

私が行った修正では、例えば fopen() を再定義し、_wfopen() を呼び出すということを行っています。このようにすることで、オリジナルのソースにはできるだけ修正が入らないようにしています。(これは UTF-8 Cygwin に記述されているのとだいたい同じような方針です)

ただし、fputs() は UTF-8 ではなく ANSI で文字列を出力するようになっています。これはコマンドプロンプトが UTF-8 に対応していないのと、何より TortoiseGit が git.exe の出力を ANSI と仮定して動作しているためです。もし git 側が UTF-8 で文字列を出力してしまうと TortoiseGit 側で文字化けを起こしてしまいます。

構成図

msysGit と TortoiseGit がどのようにファイル名の文字エンコーディングを扱っているかを以下の図に示します。

左側が修正前、右側が修正後です。

encodings

修正前の段階では、git はワーキングツリーのファイルを fopen などの標準 C API で処理しています (Win32 API も内部では使ってますが)。これらの API は ANSI 用なので、ファイル名はすべて ANSI (日本語なら CP932) で扱われます。そのため、ローカルリポジトリ (.git ディレクトリ以下のオブジェクト)に記録されるファイル名はすべて ANSI で記録されますし、リモートリポジトリに Push するファイルもすべて ANSI になります。

このため、リモートリポジトリが UTF-8 でファイル名を扱っている場合(通常は UTF-8 を使うはずです)、文字化けを起こします。Clone してくる場合は、UTF-8 の文字列を無理やり ANSI として扱ってファイルに書きだそうとするのでファイル名が化けます。

修正後の状態では、先に説明したとおり UNICODE 版の API にすげ替えを行っています。これは win32utf8.c というソース内に入っています。これにより、ワーキングツリーのファイル名は UTF-8 として扱われ、自動的にローカルリポジトリに保存されるファイル名も UTF-8 になります。また、リモートリポジトリに Push するファイルが UTF-8 になりますので、UTF-8 で管理しているリポジトリとの間で正常にファイルの受け渡しができるようになります。

さて、ここで問題になるのは TortoiseGit などのフロントエンドです。TortoiseGit の場合、msysGit を Pipe でつないで情報をやりとりする場合と、ローカルリポジトリに直接ファイルアクセスする場合の2通りがあります。

前者に関しては、修正後の msysGit はコマンドライン引数を ANSI から UTF-8 に変換するようにし、また fputs を ANSI で出力するようにしてあるので問題なくファイル名をやりとりできます。が、後者のほうが問題です。TortoiseGit はリポジトリ内に格納されているファイル名の情報がANSI だと思っているため、文字コードが合わずに文字化けが起こります。具体的には、Show Log したときにファイル名が化けるという現象が起こっています。これを修正するためには、TortoiseGit 側にも修正が必要になるでしょう。


Takuya Murakami : <tmurakam at tmurakam.org>

トップページ