souping up your bash prompt

    souping up your bash prompt
                Summary for the impatient/expert
                Details
                Download bash script
                Screenshot
                Supplementary code


This document describes how to get command completion as well as a souped up bash prompt.

Git comes with a bash script in the 'contrib/completion' directory which takes care of command completion as well as displaying the current branch name in your PS1 (primary bash prompt). We use that as a basis, and -- without digging inside of it (meaning you can upgrade that without worry) -- we add more functionality.

Summary for the impatient/expert

You might be wondering about that "hit enter twice in quick succession" piece :-) The idea is that it should be as convenient and accessible as the current implementation, but it shouldn't really happen on every prompt because it's expensive to compute and could delay the prompt.

The implementation is either a neat and elegant hack or kludge city, depending on whom you ask :)

Details

For a while, I've wanted a quick summary of git status -- how many files are staged (git added), modified, untracked, unmerged, etc. I've also wanted a quick indication of whether the current HEAD is a detached head, etc. And I wanted it to be as compact as possible.

But... I didn't want 'git status' to run on every prompt -- that would be too expensive for some of the msysgit users I support. I wanted this extra piece to kick in only when

That last part was tough, but I'm pleased that my solution not only works, it even works on msysgit so my Windows users are happy too!

Download bash script

The part that computes the status and summarises it with colored numbers is a hacked up version of git://github.com/lvv/git-prompt.git), and you can download it here. And here's a syntax colored version to quickly view on a browser. You're expected to source it into your .bashrc; it'll take care of sourcing the git-supplied one, which it looks for in $HOME.

After that, you need to include the snippet of bash code given in this section. I used to have all this in one file, but the code to detect the quick-double-enter has uses outside git also, so I separated that logic from the 'git status colorisation' stuff.

Screenshot

[Update 2009-03-19: the screenshot is not quite current but will suffice to give you the general idea]

Someone asked for a screenshot so here is one; it should give you enough of an idea. You can see the extra information (namely, the output of __git_ps1 being colored, and a few numbers after that in different colors at certain places:

The color choices retain the colors that git status shows (with git config --global color.ui true) for staged and modified files -- this is green and red, respectively. git status uses red for untracked files also, but we use blue. Unmerged files are magenta.

I didn't show changing to another directory and coming back -- that would also trigger the extra info. I also didn't show all 4 colors (staged, modified, untracked, unmerged) at the same time but I'm sure you get the idea.

Supplementary code

The following code contains the hack to do something different if you hit enter twice in quick succession. It need to be run via bash's PROMPT_COMMAND mechanism -- do not call it directly from PS1 using the $(bash_command) syntax. That syntax runs things in a sub-shell, which won't let you maintain state.

You'll notice there's git-specific stuff in there. The idea is that if you need some other information (like, perhaps, percentage disk free, or maybe system load, if you don't have conky or gkrellm), then that also can use the same mechanism; you'd just add bits of code in here, piggy-backing on the logic already present to make things run only sometimes instead of on every prompt.

Of course you can change the PS1=[...] lines however you like.

export PROMPT_COMMAND=__ps1_plus
__ps1_plus()
{
    # elegant hack or extreme kludgery?  You decide...

    # I wanted a way to see a long prompt very conveniently, but
    # not on *every* prompt -- hitting enter twice in quick succession at the
    # bash prompt was about as much as I was willing to do :-)

    # The hack works by remembering the $SECONDS value at the end of each
    # unsuccesful invocation.  When the next invocation still has the same
    # $SECONDS, you know the user hit enter twice, so you do the 'git status'
    # stuff.

    # In addition, of course, if the PWD or the output of __git_ps1 itself
    # changed, that also triggers the extra stuff.

    local gitps1
    declare -F __git_ps1 >/dev/null && gitps1=$(__git_ps1)
    if [[ $__ps1_state != $PWD/$gitps1 || $__ps1_plus_last -eq $SECONDS ]]
    then
        __ps1_state=$PWD/$gitps1

        # if enhanced PS1 builder exists call it to set __git_ps1_text;
        # otherwise default is the git-distributed one
        __git_ps1_text=$gitps1
        declare -F __git_ps1_plus >/dev/null && __git_ps1_plus

        echo -n -e '\e]0;'${HOSTNAME%%.*}:
        [[ $PWD == $HOME ]] && echo -n '~' || echo -n $(basename $PWD)
        echo -n -e '\a'
        PS1='\[\e[4m\e[32m\]\t \h:\W$__git_ps1_text\[\e[m\] \$ '; [[ $EUID == 0 ]] &&
        PS1='\[\e[4m\e[31m\]\t \h:\W$__git_ps1_text\[\e[m\] \$ '
    else
        PS1='\[\e[4m\e[32m\]\t\[\e[m\] \$ '; [[ $EUID == 0 ]] &&
        PS1='\[\e[4m\e[32m\]\t\[\e[m\] \$ '
    fi
    __ps1_plus_last=$SECONDS
}

and this is a copy of the v1.6.1 version of git-completion.bash from the git source tree; you probably have it already