右プロンプトにリポジトリの状態を表示する
右プロンプトにリポジトリの状態を表示する
はじめに
左ないし右プロンプトにリポジトリの状態を表示したい。zshにはvcs_infoがあってそれを使えばいろいろできるのだが、vcs_infoが使えない環境があったので、シェルスクリプトの勉強も兼ねて自作する。
仕様
- zshのcolorを使う(bashで動作するかは知らない)
- git、subversionに対応する
- gitはブランチ名を表示し、色で状態を表す
- svnは色と文字列で状態を表す。
リポジトリ判定
まず、カレントディレクトリがリポジトリかどうかを判定する必要がある。そのために、とりあえずgit statusや、svn statusを使う。
git statusとやって、”Not a git repository”とか返ってきたらそこはgitリポジトリではない。同様にsvn statusとやって、”hogehoge is not a working copy”と言われたらsvnリポジトリではない。
ただし、svnの場合は二つ注意が必要。まず、LANG=Cで無い場合には日本語で「hogehogeは作業コピーではありません」と表示されてしまう。gitは(少なくとも手元の環境では)LANGがja_JP.UTF-8とかになっていても英語で表示されているようだ。そこで、
$ LANG=C svn status
と、一時的にLANGをCにして実行する。
また、リポジトリが馬鹿でかい場合、svn statusの処理が遅い場合がある。そこで、「カレントディレクトリの状態のみ調べる」ことにする。そのためには、--depth immediatesオプションを使えば良い。
そんなわけで、git/svn判定をする関数はこんな感じになった。
function rprompt-st {
if [[ ! `git status 2>&1` =~ "Not a git" ]]; then
rprompt-git
elif [[ ! `LANG=C svn status --depth immediates 2>&1` =~ "is not" ]]; then
rprompt-svn
fi
}
git statusとやり、その結果を取得、文字列として正規表現マッチさせて「Not a git」が含まれていなかったらgitリポジトリなので、rprompt-gitを呼ぶ。svnも同様。
Gitリポジトリの処理
ブランチ名
まず、ブランチ名を取る。現在のブランチ(HEAD)が、どこへの参照になっているかをgit symbolic-ref HEADで取る。masterで実行すると
$ git symbolic-ref HEAD
refs/heads/master
と表示される。この表示のrefs/headsをsedで取り除けばブランチ名。こんな感じ。
name=`git symbolic-ref HEAD 2> /dev/null | sed -e "s/refs\/heads\///g" `
状態
状態はgit statusで取る。”nothing to commit, working directory clean”と言われたら緑、”nothing added to commit but untracked files present”と言われたら黄色、それ以外は赤太字にしよう。
色はzshのcolorsを使う。予めautoload -U colors;colorsしておく。
まず文字列stに状態を得る。
st=`git status 2> /dev/null`
で、この文字列に「^nothing to」とかがマッチするか調べるのだが、echoしてからgrepし、その結果が空文字列かどうかで判定する。こんな感じ。
st=`git status 2> /dev/null`
if [[ -z $name ]]; then
return
fi
if [[ -n `echo "$st" | grep "^nothing to"` ]]; then
color=${fg[green]}
elif [[ -n `echo "$st" | grep "^nothing added"` ]]; then
color=${fg[yellow]}
elif [[ -n `echo "$st" | grep "^# Untracked"` ]]; then
color=${fg_bold[red]}
else
color=${fg[red]}
fi
念のためブランチ名が取れなかった時(nameが空文字だった時)はreturnしている。
で、得られたブランチ名nameと、色を表現するエスケープシーケンスcolorを使って、右プロンプトを出力する。
echo "(%{$color%}$name%{$reset_color%}) "
$reset_colorは色等をもとに戻すエスケープシーケンス。
Subversionリポジトリの処理
Subversionはブランチ/タグ名はディレクトリ名と一致しているので状態だけ表示する。LANG=C svn status --depth immediatesして、ずらずら出てくる文字列に正規表現をかけ、マッチしたものを表示させる。
いろいろ出てくるが、とりえあず「C(衝突)」「A(追加)」「D(削除)」「M(修正)」だけ赤くして、それ以外でリポジトリで管理されていないファイル(?)がいる場合は黄色、そのどれでもなければ緑でN(Nothing to do)を表示しよう。こんな感じになる。
st=`LANG=C svn status --depth immediates 2>&1`
if [[ $st =~ "is not" ]]; then
return
fi
stt=""
if [[ -n `echo $st | grep "^[CADM]"` ]];then
color=${fg_bold[red]}
if [[ -n `echo $st | grep "^C"` ]];then
stt="${stt}C"
fi
if [[ -n `echo $st | grep "^A"` ]];then
stt="${stt}A"
fi
if [[ -n `echo $st | grep "^D"` ]];then
stt="${stt}D"
fi
if [[ -n `echo $st | grep "^M"` ]];then
stt="${stt}M"
fi
elif [[ -n `echo $st | grep "^\?"` ]];then
color=${fg[yellow]}
stt="?"
else
color=${fg[green]}
stt="N"
fi
echo "(%{$color%}$stt%{$reset_color%})"
念のためリポジトリでなかった場合(stに”is not …“が含まれる場合)はreturnしている。「C(衝突)」「A(追加)」「D(削除)」「M(修正)」の順番でチェックし、その順番で文字列を追加している。文字列の追加は
stt="${stt}C"
などとすれば良い。
プロンプトの表示
個人的な趣味で、右プロントにcyanでカレントディレクトリを表示している。もしカレントがリポジトリなら、さらに()をつけて状態を表示するようにする。
こんな感じ。
RPROMPT='`rprompt-st`%{$fg_bold[cyan]%}[%~]%{$reset_color%}'
setopt prompt_subst
setopt prompt_substは、プロンプトが表示されるたびに、その内容を評価せよ、というもの。rprompt-stが、gitリポジトリならその表示を、svnならそっちを、どちらでもなければ何も表示しない関数になっており、その後にカレントディレクトリを表示する形になっている。
以上を全部まとめるとこんな感じ。
autoload -U colors; colors
function rprompt-git {
local name st color
name=`git symbolic-ref HEAD 2> /dev/null | sed -e "s/refs\/heads\///g" `
if [[ -z $name ]]; then
return
fi
st=`git status 2> /dev/null`
if [[ -n `echo "$st" | grep "^nothing to"` ]]; then
color=${fg[green]}
elif [[ -n `echo "$st" | grep "^nothing added"` ]]; then
color=${fg[yellow]}
elif [[ -n `echo "$st" | grep "^# Untracked"` ]]; then
color=${fg_bold[red]}
else
color=${fg[red]}
fi
echo "(%{$color%}$name%{$reset_color%}) "
}
function rprompt-svn {
local name st color stt
st=`LANG=C svn status --depth immediates 2>&1`
if [[ $st =~ "is not" ]]; then
return
fi
stt=""
if [[ -n `echo $st | grep "^\s\?[CADM]"` ]];then
color=${fg_bold[red]}
if [[ -n `echo $st | grep "^C"` ]];then
stt="${stt}C"
fi
if [[ -n `echo $st | grep "^A"` ]];then
stt="${stt}A"
fi
if [[ -n `echo $st | grep "^D"` ]];then
stt="${stt}D"
fi
if [[ -n `echo $st | grep "^\s\?M"` ]];then
stt="${stt}M"
fi
elif [[ -n `echo $st | grep "^\?"` ]];then
color=${fg[yellow]}
stt="?"
else
color=${fg[green]}
stt="N"
fi
echo "(%{$color%}$stt%{$reset_color%})"
}
function rprompt-st {
if [[ ! `git status 2>&1` =~ "Not a git" ]]; then
rprompt-git
elif [[ ! `LANG=C svn status --depth immediates 2>&1` =~ "is not" ]]; then
rprompt-svn
fi
}
RPROMPT='`rprompt-st`%{$fg_bold[cyan]%}[%~]%{$reset_color%}'
setopt prompt_subst
これを.zshrc.vcsという名前とかで保存しておき、.zshrcに
[ -f ~/.zshrc.vcs ] && source ~/.zshrc.vcs
と書いておくと幸せになるかも。
まとめ
vcs_infoが使えたらそっちが早いと思うけれど、わけあって使えない環境だったり、vcs_infoが対応していない何かをやりたくなったら上記のスクリプトを修正して使うと良いかと。
追記(2015年9月2日)
svnの属性だけ修正した場合、Mが二桁目に出てくるので、それに対応するためsvn statusのgrep文をgrep "^\s\?[CADM]"と、文頭に空白が入っても対応するようにした。
A Robot’s Sigh