zshでGitのステータス情報をプロンプトに表示するにはvcs_infoを使う方法が一般的ですが、vcs_infoで得られる情報には限りがあります。そこで、git statusを使ってプロンプトを表示する方法を調べてみました。なお、OS XのGit 2.3.2で確認しています。
git statusで得られる情報
git status --porcelain --branch
を実行するとブランチ、ワーキングツリー、インデックスの状態が得られます。--porcelain
を付けることで機械処理に適したフォーマットが得られます。このコマンドの実行結果は下記の2つの部分で構成されます。
ローカルブランチの状態(1行目)
ローカルブランチの状態 | 表示 |
---|---|
リモートブランチと同期している | ## master...origin/master |
リモートブランチより古い | ## master...origin/master [behind 1] |
リモートブランチより新しい | ## master...origin/master [ahead 1] |
ファイルの状態(2行目以降)
更新されたファイルの一覧が表示されます。ファイルパスの左側には状態を表す2文字のフラグが表示されます。1文字目はインデックス、2文字目はワーキングツリーの状態を表します。フラグの意味は下表の通り定義されています。
フラグ | ファイルの状態 |
---|---|
M |
変更された |
A |
追加された |
D |
削除された |
R |
リネームされた |
C |
コピーされた |
U |
変更されたがマージされていない |
? |
インデックスもしくはワーキングツリーに追加されていない |
例
既存のファイルを変更したが git add
でインデックスを更新していない場合は下記になります。
% git status --porcelain --branch ## master...origin/master M README.md
この後にgit add
でインデックスを更新すると下記になります。
% git add README.md % git status --porcelain --branch ## master...origin/master M README.md
新しくファイルを追加したがまだgit add
していない場合は下記になります。
% git add newfile % git status --porcelain --branch ## master...origin/master ?? newfile
リポジトリがない場所でgit statusを実行すると、下記のエラーメッセージが表示されて、終了ステータスが0以外になります。
% git status fatal: Not a git repository (or any of the parent directories): .git
git statusの情報を利用する
zshでgit statusの情報を利用するには、まず実行結果を行単位に区切って配列に入れると扱いやすくなります。
git_status=("${(f)$(git status --porcelain --branch 2> /dev/null)}")
ローカルブランチの状態は1行目で得られます。先頭に##
が付くので取り除いておきます。
typeset -A git_info git_info[branch]="${${git_status[1]}#\#\# }"
ファイルの状態は2行目以降で得られます。ここでは下記の情報を算出します。
- changed: 更新されたファイルの数(先頭に
??
以外が付いているファイル) - untracked: 追加されたがリポジトリで追跡されていないファイル(先頭に
??
が付いているファイル) - clean: ワーキングツリーがクリーンな状態かどうか
shift git_status git_info[changed]=${#git_status:#\?\?*} git_info[untracked]=$(( $#git_status - ${git_info[changed]} )) git_info[clean]=$(( $#git_status == 0 ))
これでプロンプトに必要な情報が揃いました。連想配列git_info
に入っている情報をもとにプロンプトを組み立てていきます。なお、連想配列emoji
には適当な絵文字が入っている前提です。
local git_indicator git_indicator=("${emoji[git]} %{%F{blue}%}${git_info[branch]}%{%f%}") (( ${git_info[clean]} )) && git_indicator+=("${emoji[git_clean]}") (( ${git_info[changed]} )) && git_indicator+=("${emoji[git_changed]} %{%F{yellow}%}${git_info[changed]} changed%{%f%}") (( ${git_info[untracked]} )) && git_indicator+=("${emoji[git_untracked]} %{%F{red}%}${git_info[untracked]} untracked%{%f%}")
出来上がったプロンプト
.zshrcからプロンプト設定を抜粋したものを下記に示します。
# .zshrc autoload -Uz add-zsh-hook setopt prompt_subst typeset -A emoji emoji[ok]=$'\U2705' emoji[error]=$'\U274C' emoji[git]=$'\U1F500' emoji[git_changed]=$'\U1F37A' emoji[git_untracked]=$'\U1F363' emoji[git_clean]=$'\U2728' emoji[right_arrow]=$'\U2794' function _vcs_git_indicator () { typeset -A git_info local git_indicator git_status git_status=("${(f)$(git status --porcelain --branch 2> /dev/null)}") (( $? == 0 )) && { git_info[branch]="${${git_status[1]}#\#\# }" shift git_status git_info[changed]=${#git_status:#\?\?*} git_info[untracked]=$(( $#git_status - ${git_info[changed]} )) git_info[clean]=$(( $#git_status == 0 )) git_indicator=("${emoji[git]} %{%F{blue}%}${git_info[branch]}%{%f%}") (( ${git_info[clean]} )) && git_indicator+=("${emoji[git_clean]}") (( ${git_info[changed]} )) && git_indicator+=("${emoji[git_changed]} %{%F{yellow}%}${git_info[changed]} changed%{%f%}") (( ${git_info[untracked]} )) && git_indicator+=("${emoji[git_untracked]} %{%F{red}%}${git_info[untracked]} untracked%{%f%}") } _vcs_git_indicator="${git_indicator}" } add-zsh-hook precmd _vcs_git_indicator function { local dir='%{%F{blue}%B%}%~%{%b%f%}' local now='%{%F{yellow}%}%D{%b %e %a %R %Z}%{%f%}' local rc="%(?,${emoji[ok]} ,${emoji[error]} %{%F{red}%}%?%{%f%})" local user='%{%F{green}%}%n%{%f%}' local host='%{%F{green}%}%m%{%f%}' [ "$SSH_CLIENT" ] && local via="${${=SSH_CLIENT}[1]} %{%B%}${emoji[right_arrow]}%{%b%} " local git='$_vcs_git_indicator' local mark=$'\n%# ' PROMPT="$dir $user($via$host) $rc $git$mark" RPROMPT="$now" }
まとめ
git statusを利用することでプロンプトに多彩な情報を表示できます。また、文字列処理やリスト処理などのzshの強力な機能を利用することで、zshの組み込みコマンドだけでgit statusの結果を処理できます。