Zsh prompt with asynchronous Git status

Vincent Bernat

Zsh ships vcs_​info, a func­tion fetch­ing in­for­ma­tion about the VCS state for the cur­rent di­rec­tory and pop­u­lat­ing a vari­able that can be used in a shell prompt. It sup­ports sev­eral VCS, in­clud­ing Git and SVN. Here is an ex­am­ple of con­fig­uration:

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 dis­play the cur­rent branch, the pres­ence of staged and un­staged changes, as well as the on­go­ing ac­tion.1 Have a look at the doc­u­men­ta­tion for more details.

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

On large repos­i­to­ries, some in­for­ma­tion is ex­pen­sive to fetch. While vcs_​info queries Git, in­ter­ac­tions with Zsh are stuck. A pos­si­ble so­lu­tion is to ex­e­cute vcs_​info asyn­chronously with zsh-async.

The first step is to de­fine a wrap­per around vcs_​info. This wrap­per will run into a sep­a­rate process and should com­mu­ni­cate its re­sult us­ing the stan­dard out­put. It ex­pects the cur­rent di­rec­tory as its first argument.

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

The sec­ond step is to de­fine a worker, vcs_​info, and at­tach a func­tion to han­dle the re­sult re­ceived from the wrap­per. The reg­is­tered func­tion calls zle reset-prompt to force a re­fresh of the prompt with the up­dated in­for­ma­tion from ${vcs_​info_​msg_0_}.

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

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

The last step is to sched­ule the wrap­per func­tion in the worker queue be­fore dis­play­ing the prompt. This re­places the syn­chro­nous in­vo­ca­tion 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!

With­out re­ly­ing on vcs_​info, it should be pos­si­ble to have a bet­ter ex­pe­ri­ence by fetch­ing the cur­rent branch name be­fore re­triev­ing the more ex­pen­sive in­for­ma­tion. How­ever, with­out much ef­fort, this sim­ple in­te­gra­tion can make your prompt snap­pier! Have a look at the com­plete code: it con­tains some small en­hancements.

Up­date (2026-10)

This post was briefly dis­cussed on Lobsters.