Learn some bash scripting by adding current git branch name to bash prompt
Maybe you saw something like this:
04:13 PM:~/www/project (dev) $
The terminal is telling what is the current git branch name of repository in current directory. And maybe you are wondering how it works. Let’s implement this functionality!
PS1 environment variable
The text that is displayed at the beginning of your command line input is called “primary prompt string”
and it is controlled by PS1
environment variable.
Just type echo $PS1
in your terminal and it will output something like \h:\W \u\$
. You may say WTF?
Take it easy! Manual explains all this stuff (man bash
PROMPTING section).
Character | Meaning |
---|---|
\d | Date in “Weekday Month Date” format |
\h | The hostname up to the first point |
\H | Host name |
\n | New line |
\s | Name of the shell |
\t | Current time in 24-hour HH:MM:SS format |
\u | Username of the current user |
\w | Current working directory |
…
Append text to PS1 environment variable
So we should change the PS1
environment variable to also display current git branch name.
Let’s create a function that will output current git branch name and append the function output to PS1
variable.
For that we should know that:
(command)
construct is used to execute the command inside the brackets.$(command)
construct is used to execute the command inside the brackets AND return the output of that command.$VAR
is used to refer toVAR
variable value inside the string.
Open ~/.bashrc
file (this file is loaded when you start new non-login shell) and add next code to it:
function current_git_branch() {
echo "(dev)"
return
}
PS1="$PS1 \$(current_git_branch) "
Now we have a stub current_git_branch
function and result of this function is appended to
PS1
environment variable.
Get the real current git branch name
For that we should know that:
|
is a pipe operator and it’s used to direct the output of command placed before pipe operator to input of the command placed after pipe operator. The small example of|
operator:ls -l
command outputs filenames in current directory, each filename on new linegrep "^d"
filters each line of input by regular expression, in our case"^d"
means that line should start with a ‘d’ symbol which means that file should be a directory. And by doing
ls -l | grep ^d
we will filter the output ofls
command and get the list of subdirectories in current directory.
colrm from to
is a command for removing characters from input (e.gcolrm 2 4
will outputHo world
forHello world
input)
Let’s continue. We all know git branch
command. But the output is not acceptable for us.
04:13 PM:~/www/project $ git branch
CVW-3
DEFAULT-CONTENT
UVW-3
* dev
We should transform this output to just (dev)
.
The first step:
04:13 PM:~/www/project $ git branch | grep '^*'
* dev
Also we should remove the star and the space after star (2 characters).
04:13 PM:~/www/project $ git branch | grep '^*' | colrm 1 2
dev
And now we can assign the output of these piped commands to a variable
04:13 PM:~/www/project $ CURRENT_GIT_BRANCH=$(git branch | grep '^*' | colrm 1 2)
Nitpick! What if we execute this command from directory that is not a part of git repository?
04:13 PM:~/www $ CURRENT_GIT_BRANCH=$(git branch | grep '^*' | colrm 1 2)
fatal: Not a git repository (or any of the parent directories): .git
We do not need this error in output. There is an easy way to fix that. We should redirect the
error output to another place. Error output redirection can be done by appending 2>file_for_errors_output.txt
to the command. And there is a special file named /dev/null
. This file discards all data written to it,
you can think about it like about a trash can.
04:13 PM:~/www/project $ CURRENT_GIT_BRANCH=$(git branch 2>/dev/null | grep '^*' | colrm 1 2)
Now we can change our .bash_profile
file.
function current_git_branch() {
CURRENT_GIT_BRANCH=$(git branch 2>/dev/null | grep '^*' | colrm 1 2)
echo $CURRENT_GIT_BRANCH
return
}
PS1="$PS1 \$(current_git_branch) "
IF statement
But we also want to display round braces around current git branch name and only when our branch output is not empty. We need some conditional statement for this functionality. Here is the syntax:
if [ condition ]
then
commands
fi
[ condition ]
is a shorthand for test
command (crazy, yeah?) Documentation
There are a lot of keys for testing values. We need the -n
key which checks that “the length of STRING is nonzero”.
if [ -n "$CURRENT_GIT_BRANCH" ]
then echo "($CURRENT_GIT_BRANCH)";
fi
Colors!
We can also make the name of git branch yellow or green or whatever color we want (almost any).
To output text in color use the following syntax:
echo -e "\e[x;ym YOUR_TEXT \e[m"
where
-e
is a flag for echo command that turns on escape sequences output (colors are escape sequences)\e[
is used to begin the colors modificationsx;y
is a color code to use\e[m
is used to reset the color to default one.
Color | Code |
---|---|
Black | 0;30 |
Blue | 0;34 |
Green | 0;32 |
Cyan | 0;36 |
Red | 0;31 |
Purple | 0;35 |
Brown | 0;33 |
Blue | 0;34 |
Green | 0;32 |
Cyan | 0;36 |
Red | 0;31 |
Purple | 0;35 |
Brown | 0;33 |
Yellow | 1;33 |
Cyan | 1;36 |
White | 1;37 |
So we can rewrite the function like this:
function current_git_branch() {
YELLOW_COLOR='\033[1;33m'
NO_COLOR='\033[0m'
CURRENT_GIT_BRANCH=$(git branch 2>/dev/null | grep '^*' | colrm 1 2)
if [ -n "$CURRENT_GIT_BRANCH" ]
then echo -e "${YELLOW_COLOR}($CURRENT_GIT_BRANCH)${NO_COLOR}"
fi
return ;
}
Put functionality in separate file.
We can put all our custom code in separate file and then include it inside ~/.bashrc
file
with help of source file
command (this command executes the content of passed file).
Here is the final code:
~/.bashrc
:
...
source '~/.bash_git_branch'
~/.bash_git_branch
:
function current_git_branch() {
YELLOW_COLOR='\033[1;33m'
NO_COLOR='\033[0m'
CURRENT_GIT_BRANCH=$(git branch 2>/dev/null | grep '^*' | colrm 1 2)
if [ -n "$CURRENT_GIT_BRANCH" ]
then echo -e "${YELLOW_COLOR}($CURRENT_GIT_BRANCH)${NO_COLOR}"
fi
return
}
PS1="$PS1 \$(current_git_branch) "
export PS1
P.S
It’s great that we can customize prompt string by ourselves but there are already existing solutions with much more functionality. I use this one and very happy with it.