So two things have been bugging me off late, knowing if the previous command exited non-zero, and I’ve just now once too many times committed on the wrong git branch. The former question is easy enough to answer with “echo $?” and the latter with “git branch” before “git commit” – but lets be honest – how regularly do you really double check which branch you’re on?
UPDATE: 2014/09/02 – added \[…\] around escape sequences, this fixes line wrap issues. Not sure why.
Along comes PS1. The default on Gentoo looks something like this:
PS1='\[\033[01;32m\]\u@\h\[\033[01;34m\] \w \$\[\033[00m\] '
For non-root users. This renders a terminal like:
jkroon@greyscale ~ $ _
Essentially what that does is replace most of the \ escape sequences. These labels are well documented, available on many sites, for example the TLDP. The portion that is also documented, but requires one to know a bit of trickery is the \033 ANSI escape sequences. There are a great many number of these as documented (amongst others) on WikiPedia (It’s not the easiest of reads I’ve ever had on WikiPedia – so be VERY sure you really WANT to know.
Then it should be realized that you can actually escape from PS1 using $() or (not tested) “ shell syntax. In bash at least. So one could for example replace the \u above with “$(echo $USER)” – which is really roundabout and stupid but it illustrates the concept. Effectively we can inject arbitrary text into the result of the PS1 variable. We can even call external scripts and functions.
So firstly, if the previous command exited non-zero, let’s add a red “exit $? ” to the front of the prompt.
testing from a terminal we can do:
false; es=$?; [ $es -ne 0 ] && echo -e "\033[01;31mexit $es "
This will output a red “exit 1” to the TTY. To incorporate that into PS1 we can do:
PS1='$(es=$?; [ $es -ne 0 ] && echo "\[\033[01;31m\]exit $es ")\[\033[01;32m\]\u@\h\[\033[01;34m\] \w \$\[\033[00m\] '
If you execute that in your live shell, you should now be able to execute “false” and your prompt should change into:
exit 1 jkroon@greyscale ~ $ _
Execute “true” (or any other command that doesn’t terminate non-zero) to return the prompt to normal.
There is a similar trick to display the current git branch (if any) can be used. I picked the colour to be yellow, and in this case since I want it between the directory and the actual $ indicating the prompt I need to reset the colour back too. After some messing around:
git branch 2>/dev/null | sed -nre 's/^\* (.*)/\\033[01;33m(\1)\\033[01;34m /p'
Will output the required text for going to yellow, and back to blue again if (and only if) we’re currently browsing a git repository.
The ‘ marks here present some issues, and we need to basically replace them with ‘\” (close the existing ones, add a ‘ into the text using \’ and then re-open them).
This results in:
PS1='$(es=$?; [ $es -ne 0 ] && echo "\[\033[01;31m\]exit $es ")\[\033[01;32m\]\u@\h\[\033[01;34m\] \w $(git branch 2>/dev/null | sed -nre '\''s/^\* (.*)/\[\033[01;33m(\1)\033[01;34m\] /p'\'')\$\[\033[00m\] '
When in a git repository, this will result in a prompt similar to:
jkroon@greyscale ~ (master) $ _
That’s super convenient IMHO, and will hopefully result in fewer wrong commits. Happy hacking.