vimdiff で git diff を使う

エディタに Vim を使っているので、当然差分表示には vimdiff (vim -d) を使っているが、これは内部の差分判定に diff を使っている。

これだと差分が細切れなるので、Git の diif コマンドで patience, histogram アルゴリズムを使う方法『 vimdiffでより賢いアルゴリズム (patience, histogram) を使う』が参考になる。

下記のスクリプトを使うと、git diff で何か無限ループになる模様

更に上記サイトで現在の vim では設定だけで指定する方法か書かれているので、この記事自体の存在意義はない

下記のスクリプトはまだダメダメなようです。なので下記内容は無視して下さい。後日上手く直せたら、再度アップします。

直せたので、スクリプトを差し替え。

ただこの方法だと、差分を取るファイルの文字コードによっては上手く動作しない。私が遭遇したのは、EUC-JP のファイル。

そこで Ruby スクリプトを *NIX なら標準ではいっているであろう sh, tail, sed, awk と同じくターミナルで日本語を使うなら追加で入れているであろう nkf のみ使うシェルスクリプトにしてみた。

当然予め nkf でここの環境標準の文字コードに変換してから、上記サイトのスクリプトに渡してやったり、Ruby スクリプト内でそういった処理をしてやれば問題ないけど、それならここの単機能プログラムを組み合わせてやったほうが楽だし速いだろうなということで。

#!/bin/sh # git diff で差分をしつつ ed 形式に出力 # ただし ed 形式の正式な形が不明なので付け焼き刃的 case "$(uname)" in Darwin|*BSD) sed="sed -E";; *) sed="sed -r";; esac opt="--no-color --no-index -U0 " for arg in "$@"; do case "$arg" in -b | --ignore-space-change ) opt="--no-color --no-index -U0 --ignore-space-change " shift ;; -* ) echo "No $arg option" exit 1 ;; *) esac done git --no-pager diff $opt "$1" "$2" \ | tail -n +5 \ | $sed -e 's/^\+/> /g' \ -e 's/^-/< /g' \ | nkf \ | awk 'BEGIN { before="" } { now=substr( $0, 0, 1 ) if ( now ~ /@/ ){ split( $0, in_new, "[@+ \t-]+" ); range=in_new[2]; i=index( range, "," ); if ( i == 0 )in_out=range; # , が無かった→加えて範囲指定は必要なし else{ # , が見つかった in_out=substr( range, 0, i - 1 ); in_size=substr( range, i + 1, length( range ) - i ); if ( in_size != "0" )in_out=in_out","( in_out + in_size - 1 );#範囲指定は必要あり } range=in_new[3]; i=index( range, "," ); if ( i == 0 )new_out=range; # , が無かった→加えて範囲指定は必要なし else{ # , が見つかった new_out=substr( range, 0, i - 1 ); new_size=substr( range, i + 1, length( range ) - i ); if ( new_size != "0" )new_out=new_out","( new_out + new_size - 1 );#範囲指定は必要あり } if( in_size == "0" )print in_out"a"new_out else if( new_size == "0" )print in_out"d"new_out else print in_out"c"new_out } else if ( before ~ />/ && now ~ /</ )print "---\n"$0 else if ( before ~ /</ && now ~ />/ )print "---\n"$0 else print $0 before=now }' exit 0

まあ問題は、Windows だと Ruby の導入より面倒くさいよな、という点である。

具体的な設定例は、上記サイトを参考にして欲しい。

nkf ではなく iconv を使う方法も考えたけど、変換エラーが起きずに正しいと思われるエンコードが複数でてくるので、nkf を使うことにした。ただ vim から使うだけなら、そもそも nkf でフィルタを掛ける必要はない。差分を取るファイルの文字コードによっては警告が出るが、それもスクリプト下から 3 行目の

}'

}' 2> /dev/null

と捨ててやれば良い