Categories: git, bash, zsh, shell, PS1, PROMPT_COMMAND, yak-shaving
Many people have written or adapted complex scripts to get information from
git, munge that data and then put it into their shell’s prompt. (I’ve done
it myself.) However, git-prompt.sh – which comes with git –
provides a very high-quality way to do that with nearly no effort. We
should all stop writing our own and leverage what git already provides.
Much of what follows only applies to git 1.8.1. Earlier versions of
git-prompt.sh have __git_ps1, but they don’t have the extra methods to
hook into PROMPT_COMMAND. So you may need to adjust what follows
depending on your version of git and its accompanying scripts.
That raises another good point. Whenever you upgrade git, make sure
to reread and update the shell-completion and prompt scripts that come with
it (git-completion.bash, git-completion.zsh, git-completion.tcsh and
git-prompt.sh). They’re frequently updated and improved. I upgraded last
night, and I was excited to see some changes to git-prompt.sh. So I’m
sharing what I found there. Nothing life-changing, but definitely worth
looking at. (Note that I use bash, so most of what follows applies to bash
specifically, but you can easily adjust it for other shells.)
Final disclaimer: Although these scripts come with git, they may not be
automatically installed or sourced, depending on your operating system and
package tools. Check for them, for example, in /usr/local/etc. No matter
what you can find them in the source directory for git at
contrib/completions. Wherever they are, you probably need to source them
manually in your shell’s startup files.
The script git comes with is excellent, but a little complicated. You can now add Git information to your prompt in three ways. I’ll go through each way and give small examples.
For the following, I’m going to assume that I have two helper functions
defined already in my .bashrc:
get_dir() {
printf "%s" $(pwd | sed "s:$HOME:~:")
}
get_sha() {
git rev-parse --short HEAD 2>/dev/null
}
The first way to use git-prompt.sh is the simplest. Just place the magic
function __git_ps1 into your PS1. For example,
PS1='\u \W$(__git_ps1)\$ '
Most of that is a normal PS1. The call to $(__git_ps1) adds git
information to the prompt automatically if you are in a Git repository.
The %s expands to name-of-branch in parentheses and adds a space before
the addition. The result looks something like this:
achilles ithaca (master)$
If you want more control of the string that git prints, you can add a double-quoted string:
PS1='\u \W$(__git_ps1 " [%s $(get_sha)] ")\$ '
Here I’ve switched to square brackets, added a space before and after the
Git information and used the get_sha function to add the SHA1 number of
my last commit to the prompt as well. The result might look something like
this:
achilles ithaca [master 8348cc8] $.
The second and third ways use PROMPT_COMMAND instead of PS1
directly. In each case, you add __git_ps1 to PROMPT_COMMAND. The
difference between the second and third method is how many strings you
provide after __git_ps1. You can add either two or three.
If you provide two strings only, the first string will automatically appear before the Git information in the resulting prompt and the second after the Git information. For example:
PROMPT_COMMAND='__git_ps1 "\u@\h:\w" "\\\$ "'
This gives you a fairly traditional listing of your username, your
machine’s hostname, your current directory, git information and then $ or
#, depending on whether you’re running as a regular user or root. Here’s
what it might look like:
achilles@ilium:~/code/ithaca (master)$
You can also add a third string to PROMPT_COMMAND. In this case, the
third string formats output in the style of printf. Here’s an example
with the third string:
PROMPT_COMMAND='__git_ps1 "\u \W" "\\\$ " "{%s $(get_sha)}"'
Now I’m adding curly brackets and using get_sha. This time, I’ve also
removed all spaces to make the prompt more compact. The result looks like
this:
achilles ithaca{master 8348cc8}$
In all the cases where you add a string for formatting, the %s will be
the branch-name, everything else is up to you. I’m using my little custom
function to get the last SHA1 number, but you can do whatever you like
there.
Finally, there are a number of variables that you can set in your shell
that __git_ps1 will pick up on.
GIT_PS1_SHOWDIRTYSTATE=1
GIT_PS1_SHOWSTASHSTATE=1
GIT_PS1_SHOWUNTRACKEDFILES=1
# Explicitly unset color (default anyhow). Use 1 to set it.
GIT_PS1_SHOWCOLORHINTS=
GIT_PS1_DESCRIBE_STYLE="branch"
GIT_PS1_SHOWUPSTREAM="auto git"
The variables set to 1 can be set to any non-empty value. The others
provide choices. (For more details about what each of these does, read
through git-prompt.sh.) If these variables are set, __git_ps1 will
automatically display information about the state of your repository by
adding various symbols to your prompt. SHOWCOLORHINTS also adds color to
your prompt, as the name suggests. (This only works if you use one of the
two PROMPT_COMMAND methods.)
Let’s assume I’ve set the values as above. The result might look something like this:
achilles dotfiles [master *+= 1a9c53b] $
The * tells me that I have unstaged changes in the repo. The + tells me
that I have staged changes in the repo. The = tells me that I’m neither
ahead of nor behind the remote branch. (A < means I’m behind, a >
means I’m ahead and <> shows that the branches have diverged.)
Using these environment variables in combination with __git_ps1
effectively removes the need for the 50-100 line custom scripts that many
people have been using for years to munge Git information into their
prompts. (You obviously have slightly less control over what characters
show up as hints, but if you really want to control that, you can edit
git-prompt.sh itself.) The bottom line is that I trust the maintainers of
this script to keep up with changes in how git provides information much
more than myself. It also means not everyone on the planet has to create
their own munging functions.
Finally, PROMPT_COMMAND can include more than one command. If you want to
use it both for your PS1 and to set your terminal’s title-bar, you can do
that too:
PROMPT_COMMAND='__git_ps1 "\u \W" "\\\$ " " [%s $(get_sha)] "; set_titlebar "$USER@${HOSTNAME%%.*} $(get_dir)"'
By the way, PROMPT_COMMAND appears to be the new hot thing. In the past
I only remember seeing it to set a terminal’s title bar. Now in the last
month, I’ve seen it in three places. Git is obviously using it here. In
addition, both chruby and rvm recently began to use
PROMPT_COMMAND rather than overriding cd to provide automatic switching
of Ruby interpreters when you enter specific directories. I was initially
worried about adding too many commands to PROMPT_COMMAND, but so far I’m
not seeing any trouble with it. (Note: I use it only for __git_ps1 and
set_titlebar. I don’t need auto-switching of Ruby interpreters.)