Zsh Prompt Magic
Ever tried to configure Zsh? Then you probably found yourself lost and confused after looking at all those man pages, examples and google results. So let me confuse you a little bit more. That is if you want to have SCM/VCS info embedded in your prompt and colors and configurability. If you are using Git, Mercurial, Bazaar or the like or even Subversion or, god beware, CVS you most likely want to know where you are now in your repository. Good old pwd might not be enough for that and the old prompt [foo@bar:~/src/project]% does not help either. What you need is a prompt that displays everything your old prompt did as well as the current branch you’re working on, the action that is currently done to your repository (e.g. rebase) and optionally if there are staged or unstaged changes in your working tree. Let me tell you how I did all this.

Introduction
Let’s start with a little history: First I found Bart’s post show more git info on zsh prompt and used it, rewrote it a bit, then found MadCoder’s git prompt used some of that too and rewrote a bit more and integrated some ideas of Aaron Toponce’s My ZSH Prompt Long Overdue Then I found out that there is a vcs_info function already in Zsh that can do all this and a lot more more (here’s the documentation).
What we are going to do is setup a prompt consisting of a left and a right prompt (this nice guy disappears automatically when you touch it with your text cursor) that does not use more than one line and use the vcs_info function to put some SCM info in them. So let’s first explore this vcs_info function a little. This function collects data from your SCM and sets n variables $vcs_info_msg_N_. You can try it out by first loading the vcs_info function with autoload -Uz vcs_info and then calling vcs_info; vcs_info_lastmsg. vcs_info_lastmsg prints out all $vcs_info_msg_N_ variables. Be sure to check it out in a Git or whatever repository (you can see what repositories are supported with the vcs_info_printsys function). What you see is probably something like this:
[foo@bar:~]% vcs_info; vcs_info_lastmsg $vcs_info_msg_0_: " (git)-[master]-" $vcs_info_msg_1_: ""
Blagh. What the hell? Isn’t that configurable? Yes it is! zstyle is your friend. Let’s try it out:
[foo@bar:~]% zstyle ':vcs_info:*' actionformats "BUUH\!" [foo@bar:~]% zstyle ':vcs_info:*' formats "BUUH\!" [foo@bar:~]% vcs_info; vcs_info_lastmsg $vcs_info_msg_0_: " BUUH!" $vcs_info_msg_1_: ""
Seems useless? Yes, but that is only because I am to lazy to come up with a good example. For now, please look into the documentation if you want to know how to configure it better.
The Prompt
Let’s start with the real stuff. First some warm up:
#!/bin/zsh setopt prompt_subst autoload colors colors autoload -Uz vcs_info
This loads some colors and the vcs_info function, but using those colors for the prompt is a pain. You end up writing something like %{${fg_bold[yellow]}%}%~%{${reset_color}%} all over your code. So let’s create some variables doing that:
# set some colors
for COLOR in RED GREEN YELLOW WHITE BLACK CYAN; do
eval PR_$COLOR='%{$fg[${(L)COLOR}]%}'
eval PR_BRIGHT_$COLOR='%{$fg_bold[${(L)COLOR}]%}'
done
PR_RESET="%{${reset_color}%}";
Now we can access some colors using $PR_RED, $PR_GREEN, ... and reset to default with $PR_RESET. With that in place we can start formating our prompt and the vcs_info output.
# set formats
# %b - branchname
# %u - unstagedstr (see below)
# %c - stangedstr (see below)
# %a - action (e.g. rebase-i)
# %R - repository path
# %S - path in the repository
FMT_BRANCH="${PR_GREEN}%b%u%c${PR_RESET}" # e.g. master¹²
FMT_ACTION="(${PR_CYAN}%a${PR_RESET}%)" # e.g. (rebase-i)
FMT_PATH="%R${PR_YELLOW}/%S" # e.g. ~/repo/subdir
# check-for-changes can be really slow.
# you should disable it, if you work with large repositories
zstyle ':vcs_info:*:prompt:*' check-for-changes true
zstyle ':vcs_info:*:prompt:*' unstagedstr '¹' # display ¹ if there are unstaged changes
zstyle ':vcs_info:*:prompt:*' stagedstr '²' # display ² if there are staged changes
zstyle ':vcs_info:*:prompt:*' actionformats "${FMT_BRANCH}${FMT_ACTION}//" "${FMT_PATH}"
zstyle ':vcs_info:*:prompt:*' formats "${FMT_BRANCH}//" "${FMT_PATH}"
zstyle ':vcs_info:*:prompt:*' nvcsformats "" "%~"
As you can see, the zstyle commands do a lot of magic here. First of all is the second parameter ':vcs_info:*:prompt:*'. Normally you would call vcs_info and look at the variables, but with the second parameter in that shape the format will only be used when vcs_info is called with the parameter 'prompt'. This of course is not strictly necessary, but it’s good style. Second is that there are two last parameters. The first one is the format for $vcs_info_msg_0_ and the second is the format for $vcs_info_msg_1_. We will be using those for the left and the right prompt respectively. Third is the second parameter; actionformats is used when there is an action going on, formats when there is no action going on, and nvcsformats is used when we are not inside a repository. The last parameter %~ to nvcsformats will not be interpreted by vcs_info instead passed through and then subjected to prompt expansion, resulting in the current working directory.
Okay, now call the vcs_info function before the prompt is drawn
function precmd {
vcs_info 'prompt'
}
And finally set up the prompt
function lprompt {
local brackets=$1
local color1=$2
local color2=$3
local bracket_open="${color1}${brackets[1]}${PR_RESET}"
local bracket_close="${color1}${brackets[2]}"
local git='$vcs_info_msg_0_'
local cwd="${color2}%B%1~%b"
PROMPT="${PR_RESET}${bracket_open}${git}${cwd}${bracket_close}%# ${PR_RESET}"
}
function rprompt {
local brackets=$1
local color1=$2
local color2=$3
local bracket_open="${color1}${brackets[1]}${PR_RESET}"
local bracket_close="${color1}${brackets[2]}${PR_RESET}"
local colon="${color1}:"
local at="${color1}@${PR_RESET}"
local user_host="${color2}%n${at}${color2}%m"
local vcs_cwd='${${vcs_info_msg_1_%%.}/$HOME/~}'
local cwd="${color2}%B%20<..<${vcs_cwd}%<<%b"
local inner="${user_host}${colon}${cwd}"
RPROMPT="${PR_RESET}${bracket_open}${inner}${bracket_close}${PR_RESET}"
}
lprompt '[]' $BR_BRIGHT_BLACK $PR_WHITE
rprompt '()' $BR_BRIGHT_BLACK $PR_WHITE
Most of the code above is just for show. You could write all of that in in two lines. However, it’s much more readable like this, I think. For more information (especially concerning all those percent signs) see the prompt expansion documentation.
Glitches
I observed that the vcs_info function does not work correctly in two cases. When you are in a newly initialized Git repository you get an error on each invocation of vcs_info, i.e. before every prompt. The same happens when you are in the .git directory.
Comments
ft (not verified) (Sat, 06/20/2009 - 20:06)
After discussing the glitches with Florian via mail, the zsh cvs repository
now contains fixes that should take care of of both problems.
ft (not verified) (Thu, 06/18/2009 - 01:55)
I cannot reproduce either of the glitches you mentioned.
Could you send me a mail describing the problem(s) in more detail?
(You got the address, since I needed to enter an address to be able to
comment.) - I’d like to get that fixed, too.
Apart from that, nice post about vcs_info. :)
Regards, Frank