2010-04-16

Meadow3からgitを使う時の設定メモ

なんとかまともに使えそうな設定が見つかったのでメモ。

条件:

  • CygwinはUTF-8ではなくCP932で使う(LANG=ja_JP.CP932)。CygwinからほかのWindows用コマンドラインツールを呼び出したりするので、CygwinのツールだけUTF-8になっても困る。
  • msysgitを使う。Cygwinのgitはテキストモードのディレクトリで使えないので。Cygwinのcvsを使う以上、開発用ディレクトリ全体をテキストモードにする必要がある。cvsを使うかgitを使うかでプロジェクトごとにマウントし直すのは嫌。
  • Cygwinのsshを使う。win-ssh-askpassを使いたいので、msysgitのsshを使うわけにはいかない。
  • コミットログはUTF-8で記録したい。i18n.commitEncodingではダメ。これはUTF-8に変換してくれるわけではない。
  • Meadow上ではMagitを使う。
  • Magitからできるだけ文字化けしないようにしたい。
    • エンコーディングが違うファイル(たとえば一方はUTF-8、もう一方はCP932)の差分を文字化けせずに同時に表示したい。git diffの中で混ざってしまうので、結果をどう加工しても回避できない。
    • バッファ上部に出る最後のコミットのタイトルが文字化けしないようにしたい。magit-shell-command-to-string、magit-git-exit-code、magit-run-shell関数はsh.exe(shell-file-name)経由でgitを呼び出すので、modify-coding-system-alistでgit.exeのエンコーディングを指定しただけでは不十分。
    • Unpushed一覧などで文字化けしないようにしたい。git log –pretty:formatの%sではi18n.logOutputEncodingが効かない模様。

以上を考慮すると、Meadow-git間はutf-8にするのが無難。

cp932にしていろいろ試してみたのだけれど、git logのformatで%sを使った場合にlogOutputEncodingを考慮してくれない(gitにログをcp932で出力させられない)というところでギブアップ。magit-insert-unpushed-commitsなどがこのフォーマット指定を使っている。

utf-8にすると、今度はsh経由でgitを呼び出す部分で困る。sh経由でほかのツールを呼び出すこともあるので、shのエンコーディングを一律utf-8にするわけにはいかない。仕方ないので、defadviceでごにょごにょした。

msysgitの中で無効化するファイル。Cygwinのを使うので、削除するなり移動するなり。

  • bin/ssh*
  • bin/bash

.bashrcの関係ありそうな部分。

export TMP=/tmp
export TEMP=/tmp
export LANG=ja_JP.CP932

.emacsの関係ありそうな部分。

; *環境変数の設定。これらをシステム全体の設定にするのは抵抗があるのでここで。
;  Cygwin1.7の動作とか、win-ssh-askpassの動作とかで問題になったものを列挙。
(setenv "LANG" "ja_JP.CP932")
(setenv "TMP" "c:/app/cygwin/tmp") ;Meadowを使っている間はCygwinの/tmpを使う。
(setenv "TEMP" "c:/app/cygwin/tmp") ;Meadowを使っている間はCygwinの/tmpを使う。

; *とりあえず日本語環境が前提。
(set-language-environment "Japanese")

; *magit.elはgitを直接起動することもあるし、shell-file-nameのコマンド経由で起動することもあるので注意。
(setq shell-file-name "sh")
(setq shell-command-switch "-c") 

; *私はMeadow-Cygwin間は基本的にcp932にしている。
;  ほとんどの自作コマンドラインツールがcp932前提だし、
;  日本語版Windowsのデフォルトのロケールはcp932なわけだから、
;  Cygwin以外のWindows用ツールと連携させるならば普通はこの方がいい。
(modify-coding-system-alist 'process "sh" '(cp932-dos . cp932-unix))

; *Meadowとgitとの間はutf-8にする。
;  コミットログをutf-8で記録したい。
;  i18n.commitEncodingはコミット時に変換してくれるわけではないっぽい。
;  また、magit.el内でgit log --format %sを使っている場所があるので、
;  どうしてもutf-8でやりとりせざるを得ない。
;  この設定はshell-file-name経由でgitを呼ぶ場合には適用されないので注意。
;  そちらは.gitconfigのi18n.logOutputEncodingと、sh.exeに対する
;  process-coding-system-alistの設定で調整すること。
(modify-coding-system-alist 'process "git" '(utf-8-dos . utf-8-unix))

; magitを使えるようにする。
(cond ((locate-library "magit")
       (require 'magit)
       ;; magitがshell-file-name(sh.exe)経由でgitを呼ぶとき、utf-8で入出力する。
       ;; magitがshell-file-nameを使ってprocess-fileやcall-processする関数には次のものがある。
       ;; - magit-shell-command-to-string
       ;; - magit-git-exit-code
       ;; - magit-run-shell
       ;; これらの呼び出し時、一時的にprocess-coding-system-alistを書き換える。
       (defun add-sh-utf8-process-coding-system-alist ()
         (cons (cons shell-file-name '(utf-8-dos . utf-8-unix)) process-coding-system-alist))

       (defadvice magit-shell-command-to-string (around magit-shell-command-to-string-proc-coding activate)
         (let ((process-coding-system-alist (add-sh-utf8-process-coding-system-alist)))
           ad-do-it))

       (defadvice magit-git-exit-code (around magit-git-exit-code-proc-coding activate)
         (let ((process-coding-system-alist (add-sh-utf8-process-coding-system-alist)))
           ad-do-it))

       (defadvice magit-run-shell (around magit-run-shell-proc-coding activate)
         (let ((process-coding-system-alist (add-sh-utf8-process-coding-system-alist)))
           ad-do-it))

 ))

.gitconfig

[diff]
	external = ~/git-diff-nkf.sh
#[i18n]
#	logOutputEncoding = utf-8

~/git-diff-nkf.sh (異なる文字エンコーディングで書かれたファイルの差分をUTF-8に統一するために必要。echoの部分はちょっと怪しい。モード値だけが変わった場合にどうなるのかとかよくわかっていない)

#!/bin/sh
# $1 = 比較しているファイルのファイル名
# $2 = 旧バージョンの内容が入ったテンポラリファイル名
# $3 = 旧バージョンの内容のオブジェクト名
# $4 = 旧バージョンのファイルのモード値
# $5 = 新バージョンの内容が入ったテンポラリファイル名
# $6 = 新バージョンの内容のオブジェクト名
# $7 = 新バージョンのファイルのモード値

echo diff --git a/$1 b/$1
if test $3 == "."
then
# *新しくファイルを追加するとき。
# 例:
# diff --git a/.gitignore b/.gitignore
# new file mode 100644
# index 0000000..b25c15b
# --- /dev/null
# +++ b/.gitignore
    echo new file mode $7
    echo index 0000000..${6:0:7}
    diff -u -L /dev/null -L b/$1 $2 $5 | nkf --utf8 -d
else
# *内容が変わった場合。
# 例:
# diff --git a/readme.txt b/readme.txt
# index 353a8aa..401f140 100644
# --- a/readme.txt
# +++ b/readme.txt
    echo index ${3:0:7}..${6:0:7} $7
    diff -u -L a/$1 -L b/$1 $2 $5 | nkf --utf8 -d
fi

2010-04-24追記:上の方法は選択範囲(日本語を含む)を部分的にstageする機能が正しく動かない場合がある。それに対処するためエンコーディング調整ラッパーを使う方法を書いた。これを使うと.gitconfigとgit-diff-nkf.shは不要。

2010-04-15

Magitで複数の文字エンコーディングを扱う

プロジェクト内に2つのファイルがあって、一方はSJIS、もう一方はUTF-8で書かれている。両方修正してMagit上で差分を見ると、当然のように一方が文字化け。やれやれ。コミットログに関してはi18n.commitencodingだとかi18n.logoutputencodingだとか、それっぽいものが用意されているようだが(ちゃんと働くのか確認してないけど)、肝心のファイルの中身はどうすればいいのか分からなかった。

でも、diff.external(GIT_EXTERNAL_DIFF)でdiffを置き換えられると知って、nkfで変換することを思いついた。

.gitconfigに以下を追加。

[diff]
	external = ~/gitdiff.sh

gitdiff.shは以下のような感じ。自分はMeadowとgitの間は(今のところ)sjisにしているので–sjis。改行コードがLFになるように-d。

#!/bin/sh
echo diff --git a/$1 b/$5
echo index $3 $4
diff -u -L a/$1 -L b/$5 $2 $5 | nkf --sjis -d

echoの部分はMagitが認識できるように、できるだけgit標準の出力に近づけた。magit.el内には(looking-at "^diff –git ./\(.*\) ./\(.*\)(")という記述の下に(looking-at "^diff --cc +\(.*\))")という記述もあるのだけど、こっちはどういう状況での出力なのか分からないのでスルー。

なんかもう、Subversionでいいんじゃないかという気がしてくるのだが……。

2010-04-14

Git初体験5 コミットログの文字コード

Magitを使っている限りコミットログは普通に日本語が化けずに使えたので見過ごしそうになるけど、どうもサーバー上ではShift_JISで記録されているっぽい。これってUTF-8じゃなくても良いのだろうか。

仮にコミットログをUTF-8にしたとすると、ファイル内はShift_JIS(またはCP932)だから、Magitのバッファ内で差分表示をした時に文字化けしたりしないだろうか。

2010-04-14

Git初体験4 Meadow3上のMagitからpullできない

Magitからssh経由でpullができない。Cygwinのbashからやるとこんな感じ。

$ git pull
bash.exe: warning: could not find /tmp, please create!
Enter passphrase for key '/c/home/k-aki/.ssh/id_dsa':
Already up-to-date.

bashが/tmpが無いなどと寝ぼけたことを言ってから、パスフレーズを求めてきている。パスフレーズを求められるとMeadow上からは入力できないのでまずい(fakecygpty.exeを使えばできるのかもしれないけど)。

いろいろ調べてみたら、どうやら環境変数TMPとTEMPが設定されていないから、このwarningが出るようだ。つい先日Cygwin1.7にしたとき.bashrcはほとんどデフォルトのままいじらなかったのだけど、中を確認したらunset TMPなどとしているではないか。Windowsのテンポラリディレクトリと混ざると良くないらしい。.bashrcのunsetの後にset TMP=/tmpとset TEMP=/tmpを追加。

これでもMagitからpullできない。プロセス一覧を見るとssh.exeが起動しっぱなしで終了しないので、やはりパスフレーズが取れないで止まっているのだろう。複数のsh.exeも一緒に起動しっぱなしになっているので、msysgitのsh.exeを使えなくしてみた。すると今度はsed.exeがエラー。msysgitのsedはcygwinのパスを理解しないのでファイルが開けないらしい。msysgitのsedも無効化してCygwinのsedを使うようにしてみる。今度はgitが「git: 'Merge branch 'master' of ssh://********* is not a git command'」などという訳のわからないエラーを。う゛ー、なんだこりゃ。

たぶんCygwinのsedを使おうとしたせいで何かテキストの切り出しに失敗したのだろう。仕方ないのでshとsedはmsysgitのものを使うことにする。

となると、やはりパスフレーズが取れなくてssh.exeが止まったままになってしまう。

Cygwinのbash上からはちゃんと何も聞かれずにgit pullできるので、何が違うのだろうと環境変数一覧を比較してみた。

あ、TMPとTEMPの値が違う。Meadow3はスタートメニューから起動しているので%USERPROFILE%Tempだし、bashの方はさっき明示したとおり/tmp(私の場合c:appcygwintmp)だ。そういえばwin-ssh-askpassは/tmpの下にファイルを作ってたっけ。

というわけで、.emacsに(setenv "TMP" "c:/app/cygwin/tmp")(setenv "TEMP" "c:/app/cygwin/tmp")を追加したら、まともにpullできるようになった。いいのかこれで。

2010-04-14

Git初体験3 Magitを試す(Meadow3とCygwinとmsysgit)

M-x magit-status ……ぎゃー。

Local:  master ~/work/tmp/git_lesson/
Head:   6224579 first commit

Untracked files:
'c:Program' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。

Stashes:
'c:Program' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。

Changes:
'c:Program' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。

すぐにmsysgitを別のパスへ入れ直した。

動くようになったけど、やっぱりpcl-cvsやpsvnなんかとはちょっと違う。セクションって何だ? diffがとりたいのだけど=を押すんじゃないのか? など。

とりあえずコミットしてみようかとcを押したらNothing stagedとかなんとか言われた。staged、unstagedって何だろう。最初stag-edかと思ったらstage-dだったようだ。誰得UNIX-Blog: ステージを理解して git をもっと便利に使うを読んだ。

すげぇ、ファイル内の選択した部分だけをステージにあげてコミットできるんだ! これならこのインタフェースも納得だわ。確かに同一ファイルに違う目的の修正を施してしまって、分離するのが面倒だからそれを一度にコミットしてしまうということはよくあったんだよね。