よんちゅBlog

― このブログは自分用のメモや日々の問題などを共有するためのものです ―

20121005185841 お知らせ:  2013/07/17 ブログデザインをリニューアルしました。

zshのプロンプトにgitの状態表示 - 未add/未commit/未push/untracked(未追跡)に対応

zshのプロンプトにgitの状態(add/commit/push)を表示する方法に関しては、ここここを参考にしています。

今回はそこにプラスしてUntracked(未追跡)なファイルが存在しているかどうかも分かるようにzshのプロンプトに表示させます。
さらに状態を表す記号は全てブランチ名の”前”に付けるようにしました。
その他、git init直後などでHEADがまだ存在しない場合にも正常に表示されるようにするための対応、リモートリポジトリが存在しない場合に未pushと認識しないように対応したました。

っでこんな感じになりました。



.zshrcに設定する内容はこんな感じ

### zshプロンプト設定
# カラーの設定を$fg[red]のように人がわかるような書き方ができる
autoload -Uz colors
colors

#
# Color定義(あとで変更しやすいように)
#
DEFAULT=$'%{\e[0;0m%}'
RESET="%{${reset_color}%}"
GREEN="%{${fg[green]}%}"
BOLD_GREEN="%{${fg_bold[green]}%}"
BLUE="%{${fg[blue]}%}"
BOLD_BLUE="%{${fg_bold[blue]}%}"
RED="%{${fg[red]}%}"
BOLD_RED="%{${fg_bold[red]}%}"
CYAN="%{${fg[cyan]}%}"
BOLD_CYAN="%{${fg_bold[cyan]}%}"
YELLOW="%{${fg[yellow]}%}"
BOLD_YELLOW="%{${fg_bold[yellow]}%}"
MAGENTA="%{${fg[magenta]}%}"
BOLD_MAGENTA="%{${fg_bold[magenta]}%}"
WHITE="%{${fg[white]}%}"


setopt prompt_subst
autoload -Uz add-zsh-hook

#
# Gitの状態表示
#
# 記号について
#   - : WorkingTreeに変更がある場合(Indexにaddしていない変更がある場合)
#   + : Indexに変更がある場合(commitしていない変更がIndexにある場合)
#   ? : Untrackedなファイルがある場合
#   * : remoteにpushしていない場合
#
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
    # zshが4.3.10以上の場合
    zstyle ':vcs_info:git:*' check-for-changes true
    zstyle ':vcs_info:git:*' stagedstr "+"
    zstyle ':vcs_info:git:*' unstagedstr "-"
    zstyle ':vcs_info:git:*' formats '%s,%u%c,%b'
    zstyle ':vcs_info:git:*' actionformats '%s,%u%c,%b|%a'
fi

function _update_vcs_info_msg() {
    psvar=()
    LANG=en_US.UTF-8 vcs_info
    local _vcs_name _status  _branch_action
    if [ -n "$vcs_info_msg_0_" ]; then
        _vcs_name=$(echo "$vcs_info_msg_0_" | cut -d , -f 1)
        _status=$(_git_untracked_or_not_pushed $(echo "$vcs_info_msg_0_" | cut -d , -f 2))
        _branch_action=$(echo "$vcs_info_msg_0_" | cut -d , -f 3)
        psvar[1]="(${_vcs_name})-[${_status}${_branch_action}]"
    fi
    # 右側プロンプト
    RPROMPT="${RESET}%1(v|${RED}%1v|)${RESET}${BOLD_YELLOW}${VIRTUAL_ENV:+($(basename "$VIRTUAL_ENV"))}${RESET}[${MAGENTA}%D{%Y/%m/%d %H:%M:%S}${RESET}]${RESET}"
}
add-zsh-hook precmd _update_vcs_info_msg

#
# Untrackedなファイルが存在するか未PUSHなら記号を出力
#   Untracked: ?
#   未PUSH: *
#
function _git_untracked_or_not_pushed() {
    local git_status head remotes stagedstr
    local  staged_unstaged=$1 not_pushed="*" untracked="?"
    # カレントがgitレポジトリ下かどうか判定
    if [ "$(git rev-parse --is-inside-work-tree 2> /dev/null)" = "true" ]; then
        # statusをシンプル表示で取得
        git_status=$(git status -s 2> /dev/null)
        # git status -s の先頭が??で始まる行がない場合、Untrackedなファイルは存在しない
        if ! echo "$git_status" | grep "^??" > /dev/null 2>&1; then
            untracked=""
        fi

        # stagedstrを取得
        zstyle -s ":vcs_info:git:*" stagedstr stagedstr
        # git status -s の先頭がAで始まる行があればstagedと判断
        if [ -n "$stagedstr" ] && ! printf "$staged_unstaged" | grep "$stagedstr" > /dev/null 2>&1 \
            && echo "$git_status" | grep "^A" > /dev/null 2>&1; then
            staged_unstaged=${staged_unstaged}${stagedstr}
        fi

        # HEADのハッシュ値を取得
        #  --verify 有効なオブジェクト名として使用できるかを検証
        #  --quiet  --verifyと共に使用し、無効なオブジェクトが指定された場合でもエラーメッセージを出さない
        #           そのかわり終了ステータスを0以外にする
        head=$(git rev-parse --verify -q HEAD 2> /dev/null)
        if [ $? -eq 0 ]; then
            # HEADのハッシュ値取得に成功
            # リモートのハッシュ値を配列で取得
            remotes=($(git rev-parse --remotes 2> /dev/null))
            if [ "$remotes[*]" ]; then
                # リモートのハッシュ値取得に成功(リモートが存在する)
                for x in ${remotes[@]}; do
                    # リモートとHEADのハッシュ値が一致するか判定
                    if [ "$head" = "$x" ]; then
                        # 一致した場合はPUSH済み
                        not_pushed=""
                        break
                    fi
                done
            else
                # リモートが存在しない場合
                not_pushed=""
            fi
        else
            # HEADが存在しない場合(init直後など)
            not_pushed=""
        fi

        # Untrackedなファイルが存在するか未PUSHなら記号を出力
        if [ -n "$staged_unstaged" -o -n "$untracked" -o -n "$not_pushed" ]; then
            printf "${staged_unstaged}${untracked}${not_pushed}"
        fi
    fi
    return 0
}


状態を表す記号は以下のようになっています。

- : WorkingTree変更がある場合(Indexにaddしていない変更がある場合)
+ : Indexに変更がある場合(commitしていない変更がIndexにある場合)
? : Untrackedなファイルがある場合
* : remoteにpushしていない場合(リモートが設定されている場合のみ)


記号を変更したい方は

zstyle ':vcs_info:git:*' stagedstr "+"
zstyle ':vcs_info:git:*' unstagedstr "-"

local  staged_unstaged=$1 not_pushed="*" untracked="?"

の行の記号を変更して下さい。

記号の出力位置とかが気に食わないよって方は

psvar[1]="(${_vcs_name})-[${_status}${_branch_action}]"

の部分を修正して下さい。

上記の設定は私のgithub(yonchu/dotfiles)にて公開しています。
良かったら見てみたください。