Zsh prompt with asynchronous Git status

Vincent Bernat

Zsh ships vcs_info, a function fetching information about the VCS state for the current directory and populating a variable that can be used in a shell prompt. It supports several VCS, including Git and SVN. Here is an example of configuration:

autoload -Uz vcs_info
zstyle ':vcs_info:*' enable git

() {
    local formats="${PRCH[branch]} %b%c%u"
    local actionformats="${formats}%{${fg[default]}%} ${PRCH[sep]} %{${fg[green]}%}%a"
    zstyle ':vcs_info:*:*' formats           $formats
    zstyle ':vcs_info:*:*' actionformats     $actionformats
    zstyle ':vcs_info:*:*' stagedstr         "%{${fg[green]}%}${PRCH[circle]}"
    zstyle ':vcs_info:*:*' unstagedstr       "%{${fg[yellow]}%}${PRCH[circle]}"
    zstyle ':vcs_info:*:*' check-for-changes true

add-zsh-hook precmd vcs_info

You can use ${vcs_info_msg_0_} in your prompt to display the current branch, the presence of staged and unstaged changes, as well as the ongoing action.1 Have a look at the documentation for more details.

Prompt with Git-related information, including branch name and
presence of tracked and untracked
Example of prompt including information from the vcs_info function.

On large repositories, some information are expensive to fetch. While vcs_info queries Git, interactions with Zsh are stuck. A possible solution is to execute vcs_info asynchronously with zsh-async.

The first step is to define a wrapper around vcs_info. This wrapper will run into a separate process and should communicate its result using the standard output. It expects the current directory as its first argument.

_vbe_vcs_info() {
    cd -q $1
    print ${vcs_info_msg_0_}

The second step is to define a worker, vcs_info, and attach a function to handle the result received from the wrapper. The registered function calls zle reset-prompt to force a refresh of the prompt with the updated information from ${vcs_info_msg_0_}.

source $ZSH/.../async.zsh
async_start_worker vcs_info
async_register_callback vcs_info _vbe_vcs_info_done

_vbe_vcs_info_done() {
    local stdout=$3
    zle reset-prompt

The last step is to schedule the wrapper function in the worker queue before displaying the prompt. This replaces the synchronous invocation of vcs_info:

_vbe_vcs_precmd() {
    async_flush_jobs vcs_info
    async_job vcs_info _vbe_vcs_info $PWD
add-zsh-hook precmd _vbe_vcs_precmd

That’s it!

Without relying on vcs_info, it should be possible to have a better experience by fetching the current branch name before retrieving the more expensive information. However, without much effort, this simple integration can make your prompt snappier! Have a look at the complete code: it contains some small enhancements.

  1. Actions include “merge,” “rebase” and “bisect.” ↩︎