読者です 読者をやめる 読者になる 読者になる

msysgit で日本語ファイル名を扱えるようにする (途中)

msysgit で日本語ファイル名を含むリポジトリの clone を作ると、文字化けしちゃうことがあります。
とりあえず試したのは、

clone 元 結果
別 PC の Cygwin 版 Git 文字化け
同一 PC の msysgit 文字化けしない

と、こんな感じです。
他のパターンを試した方は是非教えてください。


で、このままではイヤなので、日本語ファイル名が扱えるように msysgit に手を入れてみました*1
ただ、まだ途中なので、もっと良い方法がある!とか、完全版にしてやんよ!って人がいたら是非!

前提条件

git clone を使うので何らかの Git を入れて、設定もしておいてください。
以下のページを参考にすると良いでしょう。
Git環境の構築(msysgit編) - なか日記
Gitでの日本語表示対応(msysgit編) - なか日記
Gitでの日本語入力対応(msysgit編) - なか日記
以降は msysgit がインストールされていることが前提です。

clone

次は msysgit のソースなどの入手です。
開発環境と msysgit 自体を clone します。


まず、適当なフォルダを作ってください。
例えば、C:\Users\bleis-tift\Documents\git-src を作ったとします。


次に、作った場所で

git clone git://repo.or.cz/msysgit.git .

を実行して、開発環境を手に入れます。
最後のドットは忘れないでください。あ、いや、忘れても良いですけど一階層深くなります。


次に、clone してできたフォルダ内にある git フォルダ*2で、

git clone git://repo.or.cz/git/mingw/4msysgit.git .

を実行します。
ここでも、最後のドットは忘れないでください。こちらは忘れると多分ビルドできなくなります。

最初のビルド

ここまでで開発環境とソースが取得できたので、一階層戻って、msys.bat を実行してください。
最初のビルドがはじまります。

修正

とりあえず、別 PC の Cygwin 版 Git で作ったリポジトリの clone で発生する文字化けを修正してみます。
404 Blog Not Found:ajax - 文字化け判定表
で試してみると、どうやら Shift_JISUTF-8 として扱ってしまっているために文字化けしてしまっているようです。
なので、ファイルの出力時にファイル名を Shift_JIS から UTF-8 に変換してしまえば、UTF-8UTF-8 として扱ってくれるので、文字化けしないはずです。


と、いうことで、compact フォルダにある mingw.c を開きます。
インクルードに "../utf8.h" を追加し、150 行付近にある mingw_open 関数の直前に

static int is_only_printable_ascii(const char *filename)
{
    int i = 0;

    while (1) {
        char c = filename[i++];
        if (c == '\0')
            break;
        if (c < ' ' || '~' < c)
            return 0;
    }
    return 1;
}

static int is_not_only_printable_ascii(const char *filename)
{
    return is_only_printable_ascii(filename) == 0;
}

と追加します。
そして、mingw_open 内で open を呼び出している直前に

if (is_not_only_printable_ascii(filename)) {
    filename = reencode_string(filename, "Shift_JIS", "UTF-8");
}

と追加し、filename を Shift_JISUTF-8 に変換します。
この作業を行ったあとの git diff の出力はこんな感じ。

diff --git a/compat/mingw.c b/compat/mingw.c
index 10d6796..1b705bb 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -4,6 +4,7 @@
 #include <winioctl.h>
 #include <shellapi.h>
 #include "../strbuf.h"
+#include "../utf8.h"

 extern int hide_dotfiles;
 unsigned int _CRT_fmode = _O_BINARY;
@@ -147,6 +148,25 @@ int mingw_mkdir(const char *path, int mode)
        return ret;
 }

+static int is_only_printable_ascii(const char *filename)
+{
+       int i = 0;
+
+       while (1) {
+               char c = filename[i++];
+               if (c == '\0')
+                       break;
+               if (c < ' ' || '~' < c)
+                       return 0;
+       }
+       return 1;
+}
+
+static int is_not_only_printable_ascii(const char *filename)
+{
+       return is_only_printable_ascii(filename) == 0;
+}
+
 #undef open
 int mingw_open (const char *filename, int oflags, ...)
 {
@@ -161,6 +181,9 @@ int mingw_open (const char *filename, int oflags, ...)
        if (!strcmp(filename, "/dev/null"))
                filename = "nul";

+       if (is_not_only_printable_ascii(filename)) {
+               filename = reencode_string(filename, "Shift_JIS", "UTF-8");
+       }
        fd = open(filename, oflags | O_BINARY, mode);

        if (fd < 0 && (oflags & O_CREAT) && errno == EACCES) {

ビルド

msys.bat を実行し、

cd git
make install

を実行します。
しばらくするとビルドが終了し、git フォルダに git.exe ができているので、インストール済みの git.exe をこれに入れ替えます。
インストール済みの git.exe を消してしまうのは怖いので、とりあえず git_.exe という名前に変更して、作成した git.exe をコピーしました。

ここまで

ここまでで、別 PC の Cygwin 版 Git で作ったリポジトリを clone しても、文字化けは発生しなくなります。
が、今度は同一 PC の msysgit で作ったリポジトリを clone できなくなっちゃいました。


それに、別 PC の Cygwin 版 Git で作ったリポジトリの clone はできるのですが、Git が内部で保持している情報と異なるファイルをチェックアウトしているため、Git は元のファイルが delete され、新しい Untracked files が存在するとみなしてしまいます。


さてさてどうしたものか・・・

追記

if (is_not_only_printable_ascii(filename)) {
    filename = reencode_string(filename, "Shift_JIS", "UTF-8");
}
fd = open(filename, oflags | O_BINARY, mode);

じゃなくて、

if (is_not_only_printable_ascii(filename)) {
    const char *encoded = reencode_string(filename, "Shift_JIS", "UTF-8");
    fd = open(encoded, oflags | O_BINARY, mode);
    if (fd == -1) {
        fd = open(filename, oflags | O_BINARY, mode);
    } else {
        filename = encoded;
    }
} else {
    fd = open(filename, oflags | O_BINARY, mode);
}

こうすれば同一 PC の msysgit で作ったリポジトリも clone できた。


上の状態からの git diff はこんな感じ。

diff --git a/compat/mingw.c b/compat/mingw.c
index 1b705bb..a9003bf 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -182,9 +182,16 @@ int mingw_open (const char *filename, int oflags, ...)
                filename = "nul";

        if (is_not_only_printable_ascii(filename)) {
-               filename = reencode_string(filename, "Shift_JIS", "UTF-8");
+               const char *encoded = reencode_string(filename, "Shift_JIS", "UTF-8");
+               fd = open(encoded, oflags | O_BINARY, mode);
+               if (fd == -1) {
+                       fd = open(filename, oflags | O_BINARY, mode);
+               } else {
+                       filename = encoded;
+               }
+       } else {
+               fd = open(filename, oflags | O_BINARY, mode);
        }
-       fd = open(filename, oflags | O_BINARY, mode);

        if (fd < 0 && (oflags & O_CREAT) && errno == EACCES) {
                DWORD attrs = GetFileAttributes(filename);

*1:Bazaar 使え?いや、Git が使いたいんです!

*2:.git フォルダじゃなくて、git フォルダ