Jun 012011
 

After slowly tweaking my bash prompt over several years I’ve finally arrived at something that I feel is simple, functional, and elegant. I’ve addressed my four primary annoyances with working in a terminal:

  1. Finding commands in the terminal scrollback is difficult.
  2. Long working directories cause awkward wrapping.
  3. I find myself using “git branch” and “git status” far too often.
  4. With so many terminals open, it can be difficult to distinguish between them.

I didn’t want fourteen colors, a clock, the date, and my ascii art gravatar in my prompt, so I’ve tried to keep it simple. To address #1 above, I’ve made my prompt green. This makes finding it in the noisy scrollback much easier. This uses escape pres, which are admittedly a bit nasty, but they get the job done:

\[\e[32;1m\]$PROMPT\[\e[0m\]

For #2, I used to show only the leaf of the path, but for *some* projects even this became problematic with pathnames like:

linux-yocto-2.6.37+git1+79669230fd82a3e7e254cf8b596a2388a4333e62_1+e53debfc6896bb89174ebb74154ed9b3bc72b59c-r18

So I took a page out of Joshua Lock‘s book and just ensure my prompt ends with “\n$ ” so my commands start on a new line and in the same column every time.

PROMPT="\u@\h:\w\n\$ "

For #3, also inspired by Mr. Lock, I did some digging and found a ridiculous number of heavy handed implementations, included some running ruby scripts to determine the current git branch. Ruby? Really? Turns out any modern distribution provides the very handy __git_ps1 bash autocompletion feature, so simply adding:

\$(__git_ps1 ' [%s]')

to my PS1 gets me the current branch in square brackets. To show the current state of the repository, including modified files, staged files, and things like being in the middle of a bisection, I export GIT_PS1_SHOWDIRTYSTATE=1 and add bash.showDirtyState=true to my ~/.gitconfig. This adds a * for modified, + for staged (*+ for both), and things like “|BISECTING” when in the middle of a git bisect command. Very handy!

Update (17-Dec-2012): You may find you need to add the following before you can use __git_ps1, particularly if you are on Fedora:

source /etc/bash_completion.d/git

Finally, for #4, I’ve found that setting the working dir in the titlebar of the terminal does a fair job at distinguishing between them. I don’t use many terminal applications (mutt, mc, etc) so the directory is usually adequate. I set a variable and then include that in PS1:

TITLEBAR="\[\e]2;\w\a\]"

In summary, the setup of my prompt in .bashrc is as follows:

if [ $TERM != "linux" ]; then
    TITLEBAR="\[\e]2;\w\a\]"
fi
GIT_PS1_SHOWDIRTYSTATE=1
PROMPT="\u@\h:\w\$(__git_ps1 ' [%s]')\n\$ "
PS1="$TITLEBAR\[\e[32;1m\]$PROMPT\[\e[0m\]"

This results in a prompt that looks something like this:

dvhart@doubt:~ [master]
$ touch test

dvhart@doubt:~ [master]
$ git add test

dvhart@doubt:~ [master +]
$ echo " " >> ~/.bashrc

dvhart@doubt:~ [master *+]
$ git reset --hard HEAD
HEAD is now at 8def692 bitbake TEMPLATECONF

dvhart@doubt:~ [master]
$ git bisect start HEAD HEAD~2
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[e82b229ca456cb77f1da310f7a6f966a4d5e2c95] use __git_ps1 in bash prompt

dvhart@doubt:~ [(e82b229...)|BISECTING]
$ git checkout master
Previous HEAD position was e82b229... use __git_ps1 in bash prompt
Switched to branch 'master'

dvhart@doubt:~ [master]
$ 

Update (7-Jun-2011): If you happen to use git to track your home directory config files, such as .bashrc and ~/bin you likely want to avoid using __git_ps1 in your prompt as it will show up pretty much all the time, and lose its effectiveness while working in your actual development project repositories. To address this, I’ve updated my prompt to only display __git_ps1 when the git top-level directory is not $HOME:

# Print the __git_ps1 string unless the git directory is $HOME.
source /etc/bash_completion.d/git
function git_ps1() {
    GD="$(git rev-parse --show-toplevel 2>/dev/null)"
    if [ "$GD" != "$HOME" ]; then
        __git_ps1 ' [%s]'
    fi
}

if [ $TERM != "linux" ]; then
    TITLEBAR="\[\e]2;\w\a\]"
fi
GIT_PS1_SHOWDIRTYSTATE=1
PROMPT="\u@\h:\w\$(git_ps1)\n\$ "
PS1="$TITLEBAR\[\e[32;1m\]$PROMPT\[\e[0m\]"