Estimated reading time: 4 minutes

Zsh and the VCS

Git prompt screenshot

I’ve recently switched to Zsh as my login shell after 9 years of using bash, and for no particularly good reason either as they’re both great interactive shells. I guess all the Kool Kids are doing it, and I’m just playing catchup. The one side effect of that is I’m now scribbling tips about zsh too…

In a screenshot I posted in our bugtracker — that was considerably less staged than the example above — one of my more visual hunks of zsh configuration was visible, leading to the following question from Dan Wilson:

Okay, ‘fess time. How do you get git branch names in the prompt? How do you make the funky arrows show repo status?

Firstly, I use oh-my-zsh which is an excellent basis for zsh configuration files. My prompt settings work within that framework, although they could be converted to work with plain zsh if you have the inclination.

If you want to see my entire “theme” file you can clone my fork and look at oh-my-zsh/themes/jnrowe.zsh-theme.

Branch names

zsh comes with some neat VCS integration, that is exceptionally documented in the manual. I use that code to enable branch names in my prompt, I use it directly instead of the code in oh-my-zsh that handles git status because it doesn’t do what I want [yet]. I use a format that matches the default(robbyrussell) theme in oh-my-zsh.

autoload -Uz vcs_info

# See the documentation for the format string definition
# This generates a fancy coloured string with $vcs:($branch)
zstyle ':vcs_info:*' formats '%F{2}%s%F{7}:%F{2}(%F{1}%b%F{2})%f '
zstyle ':vcs_info:*' enable git

Once we’ve configured vcs_info we just need to include ${vcs_info_msg_0_} somewhere in our prompt to display the VCS and current branch name.

vcs_info works quite well, and supports many different systems(both common and uncommon). As the code snippet shows I enable support for git exclusively. I’ve used it with mercurial too, and it works well. darcs also appears to work well, but it isn’t a system I use often enough to have tested it thoroughly.

I tested bzr support while writing this but it is totally unusable because of just how painfully slow bzr is. On my system it adds close to one and half seconds to every prompt display, although that could be improved if I wasn’t using conservative CPU scaling to save power. As a comparison the git info takes less than a tenth of a second to calculate on the same system, and mercurial around three times that which is most definitely still usable.

Note

There is a use-simple setting for the bzr support that may make the vcs_info functionality faster for you, albeit not noticeably on my system. It is also the only VCS that has such a hack, which is quite telling in itself.

Repository state

The “funky arrows” Dan asks about are dependent on the state of the current working directory as can be seen in the screenshot at the top of this page.

Identifier

Description

white →

Not a git repository

green ▶

Clean git repository

red ▶

Staged changes in git repository

yellow ▶

Unstaged changes in git repository

Using these visual markers it is always obvious what state a directory is in, I’ve toyed with adding more but suspect the lack of complexity is what makes them so useful.

To enable them we need to add a precmd hook to calculate the repository status:

autoload -U add-zsh-hook
add-zsh-hook precmd prompt_jnrowe_precmd

prompt_jnrowe_precmd () {
    vcs_info

    if [ -z "${vcs_info_msg_0_}" ] {
        dir_status="%F{2}→%f"
    } elif [[ -n "$(git diff --cached --name-status 2>/dev/null )" ]] {
        dir_status="%F{1}▶%f"
    } elif [[ -n "$(git diff --name-status 2>/dev/null )" ]] {
        dir_status="%F{3}▶%f"
    } else {
        dir_status="%F{2}▶%f"
    }
}

With this added the we just need to include $dir_status in our prompt and the status identifiers will be used.

If you are using a font which doesn’t display the characters correctly, either change the characters in the dir_status values or switch to a better font that can display them.


Authenticate this page by pasting this signature into Keybase.

Have a suggestion or see a typo? Edit this page