A git-aware prompt

UPDATE: Gitigit in the comment talked about another env var that would show unstaged changes. This article thus contains wrong informations, see the next one to do things properly.

Thanks for pointing me my mistakes Gitigit and Blah! :-)


UPDATE: Blah pointed out in the comments that something already existed, but as I stated, it doesn't quite match my workflow with Git. However, this lead me to think a little and I saw that I didn't understand anything about the difference between staged and unstaged changes :)

After looking at it a bit more, I finally got it, and as such, I updated the below function so that it takes unstaged changes into account.

So once again, thank you blah :-)


I read Peter's post today about how happy he was to have switched to zsh.

Now, this isn't one of those « you're right man, zsh is so much more awesomer » or even « dude, bash can do it too, why did you go to the other side ? » posts.

However, Peter had an incredible idea: display the current git branch in your prompt, as well as whether there are uncommited changes or not.

So I don't really care about what shell I'm using, and bash has at least one huge benefit: it's the default on my distro of choice (and I'm too lazy to change it and learn another one :). But this git thingy, I love it!

Here's what I added to my ~/.bashrc file:

# set the prompt, appending the current git branch (if any)
set_prompt() {
if [ -d ./.git ]; then
GITBRANCH="(\[\033[0;32m\]$(awk -F '/' '{ print $NF }' .git/HEAD)\[\033[0m\]"

if [ $(git diff --exit-code > /dev/null 2>&1; echo $?) -eq 1 ] || [ $(git diff-index --cached --quiet --ignore-submodules HEAD --; echo $?) -eq 1 ]; then
GITBRANCH="$GITBRANCH\[\033[1;31m\]*\[\033[0m\]"
else
GITBRANCH="$GITBRANCH\[\033[0;34m\]-\[\033[0m\]"
fi

if [ "x$(git status | grep Untracked)" != "x" ]; then
GITBRANCH="$GITBRANCH\[\033[1;31m\]+\[\033[0m\])"
else
GITBRANCH="$GITBRANCH\[\033[0;34m\]-\[\033[0m\])"
fi
fi

echo -ne "\[\033[0;34m\][\u@\h \W]\[\033[0m\]${GITBRANCH}\[\033[0;34m\]\\$\[\033[0m\] "
}

export PROMPT_COMMAND='PS1="$(set_prompt)"'

This gives me a very nice (and useful!) prompt:

[mathieu@localhost ~]$

Yeah, not so great... But now let's say I enter a folder which turns out to be a git repository. Name of the current branch appears:

[mathieu@localhost rpmbuild](master--)$

If there are uncommited changes to the tree, then the first green dash after the branch name becomes a red star. Just the same, if there are new untracked files in the tree, the second green dash becomes a red plus.

[mathieu@localhost rpmbuild](master*-)$

[mathieu@localhost rpmbuild](master-+)$

[mathieu@localhost rpmbuild](master*+)$

Hope that can be useful to someone. It certainly is for me :).

Comments

1. On Saturday, August 8 2009, 19:30 by blah

So, under which rock have you been sleeping?

git has been shipping /etc/bash_completion.d/git since quite some time which includes a bash macro __git_ps1 which does exactly this. So really, why reinvent the wheel here?

2. On Saturday, August 8 2009, 20:11 by bochecha

@blah: Why reinvent the wheel ? Because I had no idea it already existed :D

Actually, I use the git bash completion all the time and I love it. However, you just taught me the existence of this macro. And how would I have discovered it if I hadn't tried to reinvent it and thus blog about it ? :)

I'll look into it, thanks a lot!

3. On Saturday, August 8 2009, 20:30 by bochecha

@blah: Actually, I don't really like how this __git_ps1 work, unless I missed something.

What I did with my prompt, is to have it report when there are some new untracked files (i.e. before running git add [file] ).

The one you pointed will only show it (with a « + ») once it has been "git add"-ed. But once it's added, I wanted to show it just like any other tracked but uncommited change (with the « * »).

So yes, that looks great, but it's a tiny little bit away from the way I use Git. Maybe I'm doing it wrong...

Thanks again anyway :)

4. On Saturday, August 8 2009, 21:56 by Michael

Thanks for sharing this code. Very clean.
--Michael

5. On Sunday, August 9 2009, 12:57 by bochecha

@Michael: you're welcome.

But have you tried the solution pointed by blah ? As it is an upstream project that is actually maintained, it is always better to use than a gross hack in your bashrc. However, if you're like me and it doesn't quite match your workflow, I'm glad I could help :)

6. On Tuesday, August 11 2009, 12:19 by Gitigit

I'm sorry, but by setting GIT_PS1_SHOWDIRTYSTATE, GIT_PS1_SHOWUNTRACKEDFILES, GIT_PS1_SHOWSTASHSTATE you can fine tune which information the default prompt displays. Really no need for reinventions. You see, your blog entry even found its way into the Fedora weekly news. People looking for info on a git aware shell prompt should really be directed to the solution shipped with git.

7. On Saturday, August 15 2009, 02:42 by bochecha

@Gitigit:

I'm sorry, but by setting GIT_PS1_SHOWDIRTYSTATE, GIT_PS1_SHOWUNTRACKEDFILES, GIT_PS1_SHOWSTASHSTATE you can fine tune which information the default prompt displays.

I can't find out how :-/

The /etc/bash_competion.d/git file I have here doesn't speak about those variables, only GIT_PS1_SHOWDIRTYSTATE... Any pointer on how to use those?

People looking for info on a git aware shell prompt should really be directed to the solution shipped with git.

I totally agree. That's why I asked Michael if he had tried the git shipped solution, see my comment above. :)

As I answered to blah first, I didn't know about the solution shipped with Git. Sure, this article might seem stupid to someone who knows, but for me, that was a way of knowing as it lead some people to actually tell me about what already existed :)

However, for now, I can't make the solution shipped with Git as I'd like, so this little script in my bashrc does the trick. But if I manage to do what I want without writing some custom functions but rather using upstream tools, it would be even better!

I'll try to see how to use those 2 more variables. Thanks for the hint.

8. On Saturday, August 15 2009, 10:53 by bochecha

I searched what Gitigit proposed, see the next article for a much better way of doing things!