zshのvcs_infoの挙動のお話

zshvcs_infoについての備忘録。

vcs_infoというのはzshのプロンプトに今いるリポジトリの情報を表示してくれるものです。

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

これすごく便利なんですが、異なるVCSリポジトリがネストした状況下で想定した動作をしません。
例えば $HOME (~/) 以下をバージョン管理している状況でその下に個別のバージョン管理しているプロジェクトを置いている場合などです。
vcs_infoの設定を以下のようにしている場合

zstyle ':vcs_info:*' enable git hg
zstyle ':vcs_info:*' formats '(%s:%b)'
PROMPT="%1v%# "

gitリポジトリ以下にmercurialリポジトリを作成してcdした場合にプロンプトの表示がgitのままになります。

% git init top-repo && cd top-repo
(git:master)% hg init nested-repo && cd nested-repo
(git:master)%

これはvcs_infoが zstyle ':vcs_info:*' enable git hg の先頭から順に調べて行って、そのVCSリポジトリが見つかった時点で確定するためです。
gitやmercurialなどはカレントディレクトリにリポジトリが見つからない場合、上の階層に向かって再帰的に探すようになっています。そのため、前述のような動作になるわけです。

実際のコードは /usr/share/zsh/functions/VCS_Info/vcs_info の138行目辺りにある以下のコードです。 *1

...

for vcs in ${enabled} ; do
    [[ -n ${(M)disabled:#${vcs}} ]] && continue
    if (( ${+functions[VCS_INFO_detect_${vcs}]} == 0 )) ; then
        printf 'vcs_info: configured unknown backend: '\''%s'\''\n' ${vcs}
        printf 'vcs_info: use '\''vcs_info_printsys'\'' to find supported systems.\n'
        continue
    fi
    vcs_comm=()
    VCS_INFO_get_cmd
    VCS_INFO_detect_${vcs} && (( found = 1 )) && break
done

...

これの解決策としては、ネストしてるリポジトリの一番上のVCSvcs_infoのenableの一番後にもってくれば大丈夫です。
本家ML(http://www.zsh.org/mla/users/2011/msg00676.html)にもありますね。

*1:zsh 4.3.12の場合