2014年2月27日木曜日

gitブランチ名とステータスをzshの右プロンプトで表示

ターミナルからgitコマンドでリポジトリを操作するときに、マージしてから自分が現在いるブランチを間違えていた事に気づいてやりなおす・・・なんてミスは僕もやったことがある。それに現在いるブランチを確認するのにgit branchをそのたびごとに打つかわりに、常に表示できていたら便利じゃないだろうか。

実は、gitの配布ファイルの中には、bashとzsh向けに、gitリポジトリの中にいるときにだけ、プロンプトにブランチ名とステータスを表示するためのスクリプトが含まれている。これを使えば、上で上げたようなミスも防げるし、現在の変更状態も把握できて作業しやすくなる。ここではzshについてやり方を書こうと思う。

まず、gitの配布ファイルを解凍してできたディレクトリの contrib/completion/git-prompt.sh を適当な場所にコピーする。(gitの配布ファイルは例えばここからダウンロードできるし、githubから直接落としてきてもいい。)ここでは ~/.zsh/git-prompt.sh にコピーしたとしよう。

そうしたらまず、~/.zshrcに次の行を追加して読み込ませる。

source ~/.zsh/git-prompt.sh

このスクリプトにはブランチ名を表示する __git_ps1という関数が定義されており、gitリポジトリ内にいるかどうかを自動で判別して、いるときだけ動作する。

zshの場合、プロンプトを書き換えるにはシェル変数PS1を変更するのだが、通常のプロンプトに加えてブランチ名まで表示すると、ちょっと長過ぎる。そういう時、zshでは「右プロンプト」が利用できる。これは、入力中の行の画面右端に表示されるプロンプトだ。なお、入力文字列が長くなった時は自動的に消えてくれる優れものだ。

右プロンプトを設定する時は、シェル変数RPROMPTを設定する。また、zshにはプロンプト表示直前に実行されるprecmd()を定義でき、git-prompt.sh内のコメントによるとそこで実行したほうが若干速いらしい。ということで、以下の定義を ~/.zshrc に追加する。

setopt PROMPT_SUBST
setopt TRANSIENT_RPROMPT
precmd () {
    RPROMPT='$(__git_ps1 "[%s]")'
}

ここで setopt PROMPT_SUBST は、プロンプトに指定した文字列で変数展開を有効にするという意味。こうすると$(...)内部をスクリプトとして展開してくれる。setopt TRANSIENT_RPROMPT は、右プロンプトをコマンド実行後に消すという意味。また、__git_ps1 の引数には%sを含む文字列を指定できて、%sの部分がブランチ名とステータスを表示してくれる。

ここまでの設定だけだと、実はブランチ名しか表示されない。それだけでも便利だが、さらにオプションのシェル変数を設定することで、各種ステータスを記号で表示できる。設定できるシェル変数には次のようなものがある。
  • GIT_PS1_SHOWDIRTYSTATE
    空の値以外を指定すると、未ステージの変更がある時は「*」を、ステージ済の変更がある時は「+」をブランチ名の後に表示する。
  • GIT_PS1_SHOWSTASHSTATE
    空の値以外を指定すると、スタッシュが存在する時は「$」をブランチ名の後に表示する。
  • GIT_PS1_SHOWUNTRACKEDFILES
    空の値以外を指定すると、追跡されていないファイルが存在する時は「%」をブランチ名の後に表示する。この設定はリポジトリごとの設定 bash.showUntrackedFilesにより上書きされる。
  • GIT_PS1_SHOWUPSTREAM
    アップストリーム・リポジトリで対応するブランチとのコミットの差分状態を表示する設定で、次のものを指定できる。
    "auto" : アップストリーム側に新しいコミットがある場合は「<」を、ローカル側に新しいコミットがある場合は「>」を表示する。
    "verbose" : アップストリーム側の新しいコミット数を「u-数字」で、ローカル側の新しいコミット数を「u+数字」で表示する。
    他にも"name" "legacy" "git" "svn"があるようだけど説明は省略。詳しくはgit-prompt.sh内のコメント参照。この設定はリポジトリごとの設定 bash.showUpstream により上書きされる。
  • GIT_PS1_DESCRIBE_STYLE
    特定のコミットやタグをチェックアウトしている時の表示方法を指定できる。"contains" "branch" "describe" "default" から選べる。詳しくはgit-prompt.sh内のコメント参照。
  • GIT_PS1_SHOWCOLORHINTS
    空の値以外を指定すると、GIT_PS1_SHOWDIRTYSTATE を有効にしている時に色付きで表示してくれる。ただし、__git_ps1 を PROMPT_COMMAND または precmd 経由で実行している時に限る。
今回は次のように設定した。

GIT_PS1_SHOWDIRTYSTATE=1
GIT_PS1_SHOWSTASHSTATE=1
GIT_PS1_SHOWUNTRACKEDFILES=1
GIT_PS1_SHOWUPSTREAM="auto"
GIT_PS1_DESCRIBE_STYLE="default"
GIT_PS1_SHOWCOLORHINTS=1

以上まとめると、設定は以下のようになる。

source ~/.zsh/git-prompt.sh
setopt PROMPT_SUBST
setopt TRANSIENT_RPROMPT
precmd () {
    RPROMPT='$(__git_ps1 "[%s]")'
}
GIT_PS1_SHOWDIRTYSTATE=1
GIT_PS1_SHOWSTASHSTATE=1
GIT_PS1_SHOWUNTRACKEDFILES=1
GIT_PS1_SHOWUPSTREAM="auto"
GIT_PS1_DESCRIBE_STYLE="default"
GIT_PS1_SHOWCOLORHINTS=1


これで、gitリポジトリ内にいる時は右プロンプトでブランチ名とステータスが表示されるようになった。便利便利。

※2014/03/27追記(2014/08/29一部修正)

なんか時々表示がおかしくなるなーと思ってたら、どうやら追跡されていないファイルが存在する時の「%」が、本来ならZSHだと「%%」とエスケープされるはずのところ、単独の「%」のまま出力されることがあるらしい。しかも、ログイン直後は大丈夫だけど、しばらくgitコマンドを使っているとそのうちおかしくなるという意味不明な状況。いろいろ調べたけど原因はわからず、仕方ないので下記のように強制的に置き換えて対処してます。

precmd () {
    RPROMPT=`echo $(__git_ps1 "[%s]")|sed -e s/%/%%/|sed -e s/%%%/%%/|sed -e 's/\\$/\\\\$/'`
}

最初の「%」を「%%」に置き換えますが、もし最初からエスケープされてる場合は「%%%」になってしまうので、その場合は「%%」に戻してます。sedだと正規表現に先読み・戻り読みが使えないのでこうしてます。さらにスタッシュの存在を示す「$」が変数展開と解釈されないようエスケープしてます。スマートじゃないな・・・。もっと良いやり方知っている人いたら教えてください。

その他の設定も若干変更したので該当部分の設定を再度載せておきます。

source ~/.zsh/git-prompt.sh
setopt TRANSIENT_RPROMPT
precmd () {
    RPROMPT=`echo $(__git_ps1 "[%s]")|sed -e s/%/%%/|sed -e s/%%%/%%/|sed -e 's/\\$/\\\\$/'`
}
GIT_PS1_SHOWDIRTYSTATE=1
GIT_PS1_SHOWSTASHSTATE=1
GIT_PS1_SHOWUNTRACKEDFILES=1
GIT_PS1_SHOWUPSTREAM="auto"
GIT_PS1_DESCRIBE_STYLE="branch"
GIT_PS1_SHOWCOLORHINTS=0

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうそ。

0 件のコメント:

コメントを投稿