VCS shell prompts

Some recipes to make cool shell prompts that work with your git repository

Do you want your shell prompt to help you with knowing what is going on in your git repository? You can do that pretty easily. There are a number of people who have done some really wild things, from simply showing what branch you are in, to showing the name of the repository, and if you are the middle of an operation (such as a rebase, merge, etc.).

zsh

Check out this fancy prompt for zsh:

You can do that by grabbing the 60_prompt and putting it into your .zshenv, but I prefer to separate the different pieces of my shell environment, so I do the following:

  1. mkdir .zsh
  2. cd .zsh
  3. wget the 60_prompt
  4. add the following to your .zshrc
    for zshrc_snipplet in ~/.zsh/[0-9][0-9][^.]#; do
        source $zshrc_snipplet
    done

Here is another way to do this in zsh, without all the fancy colors, and less functionality.

And yet another one that is able to detect what revision control system you are using and make sure you are aware of that.

bash

Make sure your colors are defined, by also adding the following into your .bashrc:

# \e[XX;Ym
# 3X for foreground, 4X for background
# 0:black,1:red,2:green,3:yellow,4:blue,5:magenta,6:cyan,7:white
# ;0/;1 for regular/bold
# \[...\] needed to tell bash to ignore in calculating length
prompt_c_grey='\[\e[30;1m\]'
prompt_c_green='\[\e[32;1m\]'
prompt_c_yellow='\[\e[33;1m\]'
prompt_c_blue='\[\e[34;1m\]'
prompt_c_magenta='\[\e[35;1m\]'
prompt_c_cyan='\[\e[36;1m\]'
prompt_c_reset='\[\e[0m\]'

Then get this function defined in your .bashrc:

function prompt_string {
    # previous command return code
    local rc=${1:-$?}
    printf '%s' $prompt_c_green
    # add username to prompt if it doesn't match ME
    if [ "$USER" != "$ME" ] ; then
        printf '\u@'
    fi
    # add hostname
    printf "\h"
    # separator
    printf '%s:' $prompt_c_grey
    # add path
    printf '%s\w' $prompt_c_blue
    # add git info
    if true ; then
        printf ' %s[' $prompt_c_grey
        git status 2>/dev/null | \
          while read -r line; do
            local c=$((c+1))
            if ((c == 1)); then
                if [[ "$line" =~ ^'# Not currently on any branch.' ]] ; then
                    local branch='(none)'
                else
                    local branch=$(echo $line | awk '{ print $4 }')
                fi
                printf '%s%s' $prompt_c_yellow $branch
            elif [[ "$line" =~ ^'# Unmerged paths:' ]] ; then
                printf '%s!' $prompt_c_cyan
            elif [[ "$line" =~ ^'# Changes to be committed:' ]] ; then
                printf '%s~' $prompt_c_cyan
            elif [[ "$line" =~ ^'# Changed but not updated:' ]] ; then
                printf '%s*' $prompt_c_cyan
            fi
        done
        printf '%s]' $prompt_c_grey
    fi
    # add return code and close
    printf ' %s%s\$%s ' $prompt_c_grey $rc $prompt_c_reset
}

Then you can set your prompt by doing the following:

PROMPT_COMMAND=prompt_command
prompt_command() { PS1=$(prompt_string $?); }

Here is another mechanism to do all of this in bash.

 

i like the colors, but i don’t know if i can handle the gaudiness of that prompt.

Here is one that is a little more tame:

PROMPT="$pcc[1][$pcc[2]%{%(?..%S)%}%m%s$pcc[1]] $rst" 
RPROMPT="$pcc[1](%B%~$pcc[1]%(1v.:$pcc[3]%B%U%2v%u%(3v.$pcc[4] <%3v>.).)$pcc[1])$rst"

 
 

yeah, the first one is a little much for me too… I cannot handle a 2-line prompt, and agreed its way too gaudy. You should note that the above prompt elijah is suggesting requires that you are using the stuff from the 60_prompt attached to this page, specifically the $pcc variables and the $rst. So if you want that, you need to get that file and then change the PROMPT and RPROMPT variables that are in there and then re-run zsh

 
 

I couldn’t help myself… so I reworked the prompt to do what I want. The 60_prompt file now does the below.

First, a note… these screenshots were taken on a small window, normally it would be much wider and the right-hand side would not be so crowded looking. In any case, the right-hand side is smart because it will hide itself when it is in the way.

This is what it looks like regularly:

Unobtrusively it tells you what time it is, where you are, and on the right-hand side, if you look closely, there is a tilde which demonstrates that I am in the home directory, without drawing my eye there because of bold weird colors.

Here is what it looks like when I change to another directory:

The right-hand side directory goes away automatically if I were to type enough.

I find it very handy to know if the previous command I typed had a non-zero exit code, but I’m not interested in seeing the zero exit code everytime. This makes it obvious to me that the previous command failed, regardless of the command:

When I enter a repository, it is clear that I’ve done so, and it says what branch of the repository I am in, again this is done with small tweaks to keep it from being in my face (no bold or extra characters for my brain to parse):

Finally, this is what it looks like when I become root, its made very obvious that I’ve done so and exit codes are also displayed:

 
   

It looks like zsh-beta is incorporating this functionality into the vcs_info subsystem developed by Frank Terbeck. It has backends for bzr, cdv, cvs, darcs, git, hg, mtn, p4, svk, svn, and tla.

You need to be running zsh-beta, version 4.3.6-dev-0+20080929-1 or later (4.3.6-dev-0+20081001-1 is preferred as it has a fix for tab completion) in debian. Add this to your .zshenv:

autoload -Uz vcs_info

precmd() {
  psvar=()

  vcs_info
  [[ -n $vcs_info_msg_0_ ]] && psvar[1]="$vcs_info_msg_0_"
}

PS1="%m%(1v.%F{red}%1v%f.)%# "