Customizing the Bash Prompt

(incl. Git Status Support)

bam098
9 min readMay 6, 2018

--

Sometimes when I use Git (currently on Ubuntu 16.04), it happens, that I am not always aware of the current status of my project. Have I still uncommitted changes? Did I stash anything? Then I usually have to do git status to find out, but sometimes I also even totally forget about it. Well, this is not really dramatic, because usually at some point I will notice it. However, it still would be nice to immediately see the status, so that I can not even forget about it in the first place. But how can we do something like that? Well, the answer is: With customizing the bash prompt! Therefor we need to add some configurations to our ~/.bashrc file (resp. ~/.bash_profile if you prefer that one). Okay, but before I go on with how to set everything up, let us first clarify what we actually want to have in detail. Which information would be nice to see at all times? Well, for me it would be the following:

  • Do I still have uncommitted changes?
  • Have I committed, but not pushed yet?
  • Did I stash anything?
  • Is my current branch ahead of or behind the remote branch?
  • What is my current branch actually?

Okay, let us see now how we can modify the bash prompt to get all this information.

Add Git Status Support

Let us start with displaying the current branch. We can alter the bash prompt by changing the PS1 environment variable. So, let us write a function, that gives us the current branch and then let us add it to PS1 . The function could look like the following:

Basically, what happens here is, that we execute git status and then look through the output regarding the term On branch <branch name> . Then we just simply grab the name of the branch, let the function return it and finally add it to PS1 by calling the function.

However, we are not done yet. So far we only display the branch name. But what does it actually mean to be on a branch? Well, it means, that we are at the HEAD commit of that branch (i.e. the last commit of the branch). This is what you probably do most of the time. However, sometimes we might also want to checkout an arbitrary commit.

(source: git-tower)

Of course, it would be also nice to display that commit over the bash prompt. To do that, we just need to adjust our git_branch function from above a little, so that we also look for the term HEAD detached at <commit name> in the git status output and grab the commit name. The updated function could look like the following:

Just write this at the end of your ~/.bashrc file and open a new shell. Now the prompt should look like the following:

Okay, since we display the current branch now, let us think about visualizing the current status of the project. Maybe it is a good idea to do that by coloring the branch name. We could have the following color-coding:

  • When we have uncommitted changes, let us use RED.
  • When we committed already, but we have not pushed yet (i.e. our current local branch is ahead of the remote branch), let us use YELLOW.
  • When our current branch is behind the remote branch (i.e. the remote branch has additional commits), let us use YELLOW as well.
  • When our current branch and the remote branch have diverged (i.e. the local and remote branch have different commits), let us also use YELLOW.
  • When there is nothing to commit or push and our local and the remote branch have the same status, let us use GREEN.

Okay, let us define the colors first. Bash requires a certain format for this, which you can check out here. For our colors the definitions should be:

COLOR_RED="\033[0;31m"
COLOR_YELLOW="\033[0;33m"
COLOR_GREEN="\033[0;32m"

Okay, but how do we change colors within the bash prompt? Well, it turns out, that we can do it by adding \[<color>\] to PS1 . Everything after it will be written in the specified color then. Let us maybe write a function git_color, that sets the appropriate color for each git status and then call that function for PS1 .

Maybe you noticed, that I added with OCHRE and WHITE two additional colors. OCHRE is just used as a default color within the function git_color and WHITE is used to reset the color after the branch name (otherwise also our commands would be in RED , GREEN or YELLOW ). Furthermore, I also needed to add git fetch 2> /dev/null at the beginning of git_color . This is not really good honestly, but to automatically set a YELLOW status in case of a local branch, that is behind the remote one, I have not been able to come up with a better solution yet. If anyone has a good idea, please let me know! :)

Anyway. The bash prompt should now look like this:

However, we can still add more details regarding the status of the project. For instance, when we have uncommitted changes, we still can distinguish between changes, that we already added with git add and changes, that have not even been added yet. Moreover, when we have the YELLOW status, we know, that our current local branch and the remote branch differ, but we still do not know whether our local branch is ahead, behind or whether both branches have diverged. Let us add this information as well with the following coding:

  • Changes, that have not even been added yet, should be tagged with ⚡.
  • Changes, that have already been added, should be tagged with +.
  • When our current local branch is ahead, let us use the tag ↑.
  • When our current local branch is behind, let us use the tag ↓.
  • When both branches have diverged, let us use the tag ↕.

It might be good to use an own function git_info for this as well, which we can then call within the function git_branch to add the tag behind the branch name. To implement the function we can just execute git status and then look for each appropriate term again in the same way as we have done it for git_color and git_branch .

Fortunately, we do not need to execute a git fetch again, since we have already done it in git_color . Okay, the bash prompt should look like the following now:

We are actually almost done. There are only two things left, that I would like to include in the prompt:

  • When I try to merge one branch into another, I would like to see immediately whether or not there are any merge conflicts.
  • When there are any stashed changes, I want to easily see it. Let us add the tag ▴ next to the branch name for this.

To add this functionality, we can simply extend the function git_info in the same way as for the other tags. However, we have one problem now. Unfortunately, git status does not track stashed changes by default, so that we can not grab this information from there. Thus, we need to add it to git status first. We can do this by creating the file ~/.bash_alias in our home directory and add the following lines to it:

git () {
command git "$@" || return # preserve $?
[[ $1 = status ]] && command git stash list
}

After that we can finally extend git info .

You can find the complete ~/.bashrc file later at the end of this blog post. But now let us first check our current bash prompt (stashed changes in git status marked in red):

Visual Customization

So, now we are actually done. We have added all the information about the status of a Git project, that we initially wanted to have. Nevertheless, I wondered whether there are still other settings of the bash prompt, which could be useful. And are there any other information, that would be interesting to add next to the status of a Git project? Well, I came up with the following ideas:

  • I would like to see my current location as a path from / (root directory) or ~ (home directory), so that I know where I am currently at. Well, this is actually already a standard stetting for my shell, but I still wanted to mention it.
  • However, the current location can be quite long, if we have deep directory structures. Thus, we can easily have line breaks within the shell, because the prompt just gets too long. But what can we do about it? Well, we could put information like e.g. the current location or the Git status and the command prompt in separate lines.
  • Since lately I often work with remote machines (to e.g. have CUDA support for some Machine Learning projects), it would be nice to also display the name of the current user and the machine I am at.

How can we do all that? Well, it turns out, that it is actually not too hard to set this up. We just need to know a few bash standard variables like the following ones:

\u     Name of the user (that started the shell)
\h Host name on which the shell runs (up to the first ".")
\w Current working directory
\n New line

You can find an extended list of variables here. Okay, let us add them to our bash prompt. Moreover, I would like to use different colors. Thus, let us maybe have more variety and add BLUE as well (definition is not shown here; see appendix at the end).

I also added an additional \n at the very beginning, since I thought it might make it more clear, when I have an empty line between two prompts. Okay, let us see now how this looks like:

Of course, this is my personal preference regarding the visual appearance of the bash prompt and other people might prefer other designs. However, with the bash standard variables and the PS1 environment variable you can easily set it up to what ever you like.

Anaconda Environment Support

Okay, one more thing! But don’t worry. This will be the last one. Anaconda environments! Well, just in case you never heard of it: Anaconda is a Python distribution, which lets you create so-called environments. This is a good thing, since installing Python libraries directly on your system can actually totally mess it up. Installing libraries within Anaconda environments instead, is much safer. It is actually quite frequently used, since Python is really popular in the Machine Learning community (but of course there are also other alternatives to Anaconda). You can find a short tutorial about Anaconda here.

Okay, but what does this have to do with the bash prompt? Well, when you activate such an Anaconda environment, it is probably also useful to see in which environment you currently are. But we actually do not need to set this up by ourselves. Anaconda usually already does it for us and as a result, it should appear at the beginning of our bash prompt. However, with our changes from the last section we kind of messed it up and now it looks like this:

This is caused by our \n at the beginning, which we added to have an empty line between two prompts. However, what we actually want is, that the \n appears even before the Anaconda environment name. Fortunately, there is an easy fix for that. Just add the following line right before you export the variable PS1 in your ~/.bashrc file and remove the first \n , that we set above.

PROMPT_COMMAND="printf '\n'"

Now the problem should be fixed. Well, actually Anaconda has an own configuration file for the prompt, which we also could have modified. However, I think it is better to just stick to ~/.bashrc , because then we have our complete bash prompt customization at one place. Okay, now it should look better again:

In the following appendix you can find a full version of the ~/.bashrc file.

Appendix

--

--

bam098
bam098

Responses (2)