zsh で Git の作業コピーに変更があるかどうかをプロンプトに表示する方法

2012/12/13 追記

zsh 4.3.11 以降の新しい機能を使って改良しました。 -> 「zsh の vcs_info に独自の処理を追加して stash 数とか push していない件数とか何でも表示する - Qiita


Git を使ってファイルを編集した場合、それをいったんインデックスに追加(add)してその後コミットってのが基本的な流れになる。なんかいろいろやってると、ちゃんと add したのかどうかわかんなくなることがある。

そういうときは status コマンド使えばいいんだけど、以前エントリ書いた zshvcs_info の機能を使うといい感じにプロンプトに表示できるようになるので紹介する。

zshrc の書き方

こんな風に zshrc に書いておけば OK。

autoload -Uz add-zsh-hook
autoload -Uz colors
colors
autoload -Uz vcs_info

zstyle ':vcs_info:*' enable git svn hg bzr
zstyle ':vcs_info:*' formats '(%s)-[%b]'
zstyle ':vcs_info:*' actionformats '(%s)-[%b|%a]'
zstyle ':vcs_info:(svn|bzr):*' branchformat '%b:r%r'
zstyle ':vcs_info:bzr:*' use-simple true

autoload -Uz is-at-least
if is-at-least 4.3.10; then
  # この check-for-changes が今回の設定するところ
  zstyle ':vcs_info:git:*' check-for-changes true
  zstyle ':vcs_info:git:*' stagedstr "+"    # 適当な文字列に変更する
  zstyle ':vcs_info:git:*' unstagedstr "-"  # 適当の文字列に変更する
  zstyle ':vcs_info:git:*' formats '(%s)-[%b] %c%u'
  zstyle ':vcs_info:git:*' actionformats '(%s)-[%b|%a] %c%u'
fi

function _update_vcs_info_msg() {
    psvar=()
    LANG=en_US.UTF-8 vcs_info
    [[ -n "$vcs_info_msg_0_" ]] && psvar[1]="$vcs_info_msg_0_"
}
add-zsh-hook precmd _update_vcs_info_msg
RPROMPT="%1(v|%F{green}%1v%f|)"

こんな感じで、リポジトリに変更がある場合はプロンプトに + とか - とか表示される。

作業コピーを編集して、まだインデックスに追加していないとき

変更をインデックスに追加済みのとき

変更をインデックスに追加済みで、それ以外にも作業コピーに変更があるとき

+, - は好きな文字列に変えて使おう。

check-for-changes についてもうちょっと詳しく

check-for-changes ってのは zsh のバージョン 4.3.10 から入った vcs_info の機能で、今いるリポジトリにローカルで変更を加えたかどうかを通知してくれる仕組み。

設定方法は以下。

まず最初に、4.3.10 ってのはけっこう新しいバージョンなので、まだそれより古いって人もいると思う。そのときは使えないんだけど、せめて変なエラーメッセージが出ないようにしておこう。

autoload -Uz is-at-least
if is-at-least 4.3.10; then
  # この部分はバージョン 4.3.10 以降で有効になる
fi

こんな風に書いておくと、古いバージョンの時は何も設定されずにエラーが出なくなる。1つの zshrc をいろんな環境で使ってる人はこう書いておいた方がいいよ。ちなみに Mac の Snow Leopard にデフォルトで入ってる zsh は 4.3.9 (残念)。

ここまでが準備。次に肝心の中身だけど、まずはこんな風に書いてその設定を有効にする。

zstyle ':vcs_info:git:*' check-for-changes true

現時点では check-for-changes は Git でしか動かないので、Git の場合のみ有効にしてる。Mercurial(hg) 対応は今やってるとこみたい。そのうち使えるようになるだろう。

で、実際に表示する文字列を設定する。

# インデックスに追加された場合に表示される文字列
zstyle ':vcs_info:git:*' stagedstr "+"

# 作業コピーに変更があった場合に表示される文字列
zstyle ':vcs_info:git:*' unstagedstr "-"

"+"、"-" は適当な文字列に置き換えよう。デフォルトではそれぞれ "S"、"U" になってるので、そのままでいい人は書かなくても OK。

最後に、プロンプトに表示するフォーマットを指定する。

zstyle ':vcs_info:git:*' formats '(%s)-[%b] %c%u'
zstyle ':vcs_info:git:*' actionformats '(%s)-[%b|%a] %c%u'

%cstagedstr%uunstagedstr に置き換えられる。ここも適当に変更してかっこいいプロンプトにしよう。

パフォーマンスについて

この check-for-changes の機能は便利なんだけど、リポジトリのサイズによってはかなり遅くなる可能性がある(って man に書いてある)。なのでデフォルトでは無効になっている。

実際にどのくらい遅くなるのか試してみた。zsh の git リポジトリ(git://zsh.git.sf.net/gitroot/zsh/zsh)で確認。

まずは check-for-changes なしの場合

% time (vcs_info)
(; vcs_info; )  0.01s user 0.03s system 102% cpu 0.043 total

次に check-for-changes ありの場合

% time (vcs_info)
(; vcs_info; )  0.08s user 0.03s system 103% cpu 0.104 total

合計で2倍ぐらい時間かかってる。

僕が見た感じでも、ちょっと遅くなってるのが分かる。使って困るというほどでもないけど、あんまり遅すぎるようだったら無効にしよう。

参考

いい感じのプロンプトになったと思う。もっと細かく調整したい人は man zshcontrib の GATHERING INFORMATION FROM VERSION CONTROL SYSTEMS を見てね。