Prompt Show Git Branch In Prompt
Gabriel Manricks
Chief Architect, ClearX
Published: 5/20/2024
The short answer
The appearance of the terminal prompt is controlled by an environment variable named PS1, which the shell evaluates each time the prompt is displayed.
To change your prompt to include the current Git branch, you can start by opening your shell's configuration file using your favorite text editor; for Bash, it is ~/.bashrc, and for Zsh, it is ~/.zshrc:
$ vim ~/.zshrc
Run in Warp
Define the following getBranch function into it:
getBranch() {
git rev-parse --abbrev-ref HEAD 2> /dev/null
}
Run in Warp
Where:
- The git rev-parse --abbrev-ref HEAD command fetches the name of the current branch.
- The 2> /dev/null redirection is used to discard any error messages, for example, if the current directory is not a Git repository.
Integrate this function into the PS1 environment variable:
export PS1="$PS1 \$(getBranch) "
Run in Warp
Where:
- The export command sets an environment variable.
- The $PS1 variable contains the current prompt settings.
- The $(getBranch) expression will be replaced by the getBranch command output.
- The \ ensures that the $(getBranch) expression evaluates dynamically each time the prompt is displayed rather than being pre-evaluated and stored as a string on terminal' start up.
Note that if you are using Zsh as your terminal, you will need to enable command substitution within the prompt using the setopt command as follows:
setopt PROMPT_SUBST;
Run in Warp
After editing your shell config file, you will need to reload your shell’s configuration by opening a new terminal or by running the following source command:
$ source path/to/shell_config
Run in Warp
Easily change the prompt using the Warp Context Chips feature
If you’re using Warp as your terminal, you can easily change your terminal's prompt using the Warp Context Chips feature:
The Warp Custom Prompt is a GUI-based feature that allows you to customize your shell using a list of pre-configured prompts that you can easily combine through a system of drag and drop, including timestamp, remote login, git branch, and more.
Just right-click your prompt and click on “Edit Prompt” to bring up the visual custom prompt editor.
These prompt chips are context-aware, so for example, if you add the “uncommitted files” chip, then only when inside a git directory you will have a section on the number of uncommitted files, or if you add the Kubernetes context it will only appear when running a kubectl command.
Extending your Git prompt
The goal of a good Git prompt is to provide as much helpful context about the repository you are currently in without requiring you to run any commands manually, like how a prompt displays the current working directory so that you don’t need to check where you are manually.
In the context of Git, some relevant contextual information is:
- The branch name - to make sure you are working on the correct branch
- The status of the branch - to allow you to know if you need to commit something
- The difference in commits between the local and remote repositories - to see if you need to pull or push any changes
Commits ahead or behind upstream
To get the number of commits you are ahead or behind the remote branch, you can use:
$ git rev-list --count --left-right @{upstream}...HEAD
12 6
Run in Warp
The command will output two columns: the first will be the number of commits the upstream (origin) is ahead of your local branch, and the second is the number of commits you are ahead of the remote branch.
Using this, you can create a function in your shell’s config file (~/.zshrc):
gitUpstreamPosition() {
local commitCount=$(git rev-list --count --left-right @{upstream}...HEAD 2> /dev/null)
if [ -n "$commitCount" ]; then
local behindCount=$(echo -n "$commitCount" | cut -f1)
local aheadCount=$(echo -n "$commitCount" | cut -f2)
if [ "$behindCount" != "0" ]; then
echo -n " ${behindCount}↓"
fi
if [ "$aheadCount" != "0" ]; then
echo -n " ${aheadCount}↑"
fi
fi
}
Run in Warp
Where:
- local commitCount=$(...) runs the command and stores the results in a local variable called commitCount
- if [ -n "$commitCount" ]; checks if the value is not an empty string
- echo -n $commitCount | cut -f1 will cut the first column, which is the number of commits behind the remote branch. The value is stored in a variable called behindCount
- local aheadCount=$(...) same is done for the second column, which is the number of commits ahead of the remote branch.
- if [ "$behindCount" != "0" ]; check only to display something if the value is different than zero.
- echo -n " ${behindCount}↓" print out the number plus a down arrow as a symbol for commits that need to be pulled.
Working Tree Status
Another common use case is to display the status of your work tree to see if there are changes to commit or if you are in a clean state. The command to get the status of the working tree is:
$ git status --porcelain
A file.txt
MM demo.bin
D README.md
Run in Warp
Many git commands have a --porcelain flag, which outputs the data in an easier-to-parse format. For git status, the porcelain flag will cause the output only to include changed files without any extra text, and for each line, you will have two letters describing the change followed by a space and the file name.
The first letter is the staged change status, and the second is the unstaged change status. The exception to this is new files that have never been tracked (added) to Git; they will have a question mark in each column.
So, to see if there are any staged changes, you can simply check to see if, in any of the rows, there is a line with a letter (other than a question mark) in the first character space:
$ git status --porcelain | grep '^[^? ]'
Run in Warp
Where the grep regular expression is:
- ^ - means the match must start at the beginning of a line
- [^? ] - brackets specify a group of characters to match, and the ^ reverses the selection, so any character not matching a question mark or space.
A prompt function to display symbols for these different kinds of changes would look something like the following:
gitStatusSymbols() {
local gitstatus=$(git status --porcelain 2> /dev/null)
if [ -n "$gitstatus" ]; then
local statusString="";
if [ -n "$(echo "$gitstatus" | grep '^[^? ]')" ]; then
statusString="$statusString$GREEN+$BLANK"
fi
if [ -n "$(echo "$gitstatus" | grep '^.[^? ]')" ]; then
statusString="$statusString$RED*$BLANK"
fi
if [ -n "$(echo "$gitstatus" | grep '^[?][?]')" ]; then
statusString="$statusString?"
fi
echo -e " $statusString"
fi
}
RED='%F{red}'
GREEN='%F{green}'
BLANK='%f'
Run in Warp
Where:
- The first line gets the status output, handling errors
- if [ -n "$gitstatus" ]; checks if the value is not empty
- local statusString=""; prepares a variable to hold the statuses
- The function then checks for three kinds of changes using grep and a regular expression to test for staged changes, unstaged changes, and new files
- For each of these cases, the function appends the desired symbol to the output variable
- echo -e " $statusString" outputs the final results, prepending the value with a space to fit well with the rest of the prompt.
- The last three lines hold variables with the escape sequence for adding color to the terminal output.
For Bash instead of Zsh, the variables for color should be:
GREEN='\033[32m'
RED='\033[31m'
BLANK='\033[0m'
Run in Warp
Tying it all together
To wrap the above two functions into a single string along with the branch name you can create another function like the following:
gitPrompt() {
local branchName=$(git rev-parse --abbrev-ref HEAD 2> /dev/null)
if [ -n "$branchName" ]; then
echo -e " [🌱 ${branchName}$(gitStatusSymbols)$(gitUpstreamPosition)]"
fi
}
Run in Warp
This function gets the current branch name, and if the value isn’t blank (i.e., we are in a Git directory), it will output the branch name between square brackets along with the output of the two other functions. It is worth noting the ${} with curly braces prints out a variable, whereas $() with parentheses runs a command/function and outputs the results.
This function can then be used by adding to your shell’s config file:
export PS1="\u@\h \w\$(gitPrompt)> "
Run in Warp
Where:
- \u prints the current user
- \h prints the hostname
- \w prints the current working directory
- $(gitPrompt) prints the output of the gitPrompt[.inline-code] function
For Zsh, you will also need to enable prompt substitution as described above:
setopt PROMPT_SUBST
Run in Warp
Git Prompt Script
Git has released an official utility that can be used to add Git info easily into your terminal prompt. To get started, you need to download the script itself:
$ curl https://raw.githubusercontent.com/git/git/master/contrib/completion/git-prompt.sh -o ~/git-prompt.sh
Run in Warp
And then edit your shells configuration (~/.zshrc) with the following:
source ~/.git-prompt.sh
Run in Warp
That loads in a shell function called __git_ps1, which prints out git information primarily to be used in prompts. To add it to your prompt, you can include it in your PS1 variable:
export PS1="$PS1\$(__git_ps1 \" (%s)\")> "
Run in Warp
The __git_ps1 function accepts an optional format string parameter used to style the output. The example above sends ” (%s)” where we will get a string that starts with a space and then parenthesis, and within the parenthesis, the %s will be replaced by the __git_ps1 results. The advantage to using a format string instead of just hard coding the space and parenthesis is that if you are not in a Git repository, it will be omitted completely.
For Zsh, you will also need to enable prompt substitutions:
setopt PROMPT_SUBST;
Run in Warp
By default, this script will only display the git branch, but you can enable other features by setting specific environment variables.
GIT_PS1_SHOWDIRTYSTATE will cause the prompt to include the working tree statusGIT_PS1_SHOWUNTRACKEDFILES will show if there are new untracked filesGIT_PS1_SHOWUPSTREAM can be used to show commits ahead and behind your upstream
For example, add to your ~/.zshrc
export GIT_PS1_SHOWDIRTYSTATE=true
Run in Warp
These are just a few of the options. You can view more info in the script comments here.
Advanced Prompt Toolkits
Designing a truly dynamic and interactive prompt that reacts to different tools like Docker, Git, Python, Node.js, etc., is a complex task. Each tool has nuances and design decisions (not to mention supporting multiple terminals like bash/zsh). Luckily, there are very popular projects that work on just this.
Starship
One such project is called Starship, which supports over 80 different tools, smart defaults out of the box, and the ability to customize many different settings in each.
To install Starship, you can run:
$ curl -sS https://starship.rs/install.sh | sh
Run in Warp
Then, to get it running in Bash, add the following to your ~/.bashrc:
eval "$(starship init bash)"
Run in Warp
And for Zsh, you would add to your ~/.zshrc:
eval "$(starship init bash)"
Run in Warp
It is also recommended to install a NerdFont from here, which are regular fonts with the symbols replaced with popular development icons from icon frameworks like Devicons, Font Awesome, Material Design, and more.
Customising Starship
To customize Starship prompt, create a file ~/.config/starship.toml with your configuration. Starship has both high-level settings for designing the prompt's format and granular per-tool options.
For example, if you would like to add the current Kubernetes context to your prompt, you can add the following to your ~/.config/starship.toml file:
[kubernetes]
disabled = false
format = '[${symbol}k8s $context]($style) in '
Run in Warp
Each tool in Starship has its own settings and variables. For Kubernetes, you have variables like context, namespace, user, and cluster; in the example above, we display the current Kubernetes context.
There are also configuration variables per tool, like symbol and style, which we used here; symbol defaults to a Kubernetes icon, and the style defaults to “cyan bold”. In Starship, to customize colors of text, you use the format [<text to color>](<style>); everything between the square brackets will receive the style defined in the parentheses.
You can find more info about starship configuration for Kubernetes and other tools here.
There are also presets that are like themes; these are complete starship.toml configurations you can just copy and use. You can find some presets on the official Starship site here.
Powerlevel10K (P10K)
If you are using ZSH, another popular off-the-shelf prompt is PowerLevel10K; like Starship, it has extensive support for many different tools and contexts.
To install P10K, you can run:
$ git clone --depth=1 https://github.com/romkatv/powerlevel10k.git ~/powerlevel10k
$ echo 'source ~/powerlevel10k/powerlevel10k.zsh-theme' >>~/.zshrc
Run in Warp
The first line clones the repository into your HOME directory, and the second command adds a line to your ~/.zshrc file to load P10K.
Unlike other prompt kits like Starship, where you start off with the defaults and then customize it later, with P10K, you are greeted with a setup wizard the first time P10K is loaded. This setup wizard visually guides you through the different customization options, allowing you to get it set up just how you like it very quickly.
You can get back to this setup wizard at any time by running p10k configure
Written by
Gabriel Manricks
Chief Architect, ClearX
Filed Under
Related Articles
Undo A Git Pull
How to effectively remove the commits introduced by a pull in Git using git-reset and preserve your local changes using git-stash. Also, how to cancel an unmerged pull request on GitHub.
Undo a Git Merge
How to rollback the changes introduced by a merge in Git by adding new opposite commits using git-revert and effectively removing commits using git-reset.
How To Remove Secrets From The Git History Remove Secrets From The Git History
Learn how to remove secrets from the Git history using the BFG and git-filter-repo command-line tools.
Adding a Submodule in Git
This post will show you how to simply add a submodule to a local repository, clone a repository with a submodule, and work within a repository that has a submodule.
Undo a git push
This post will show you had to simply undo a git push three different ways.
Undo Git Add
Learn how to effectively use 'git add' to stage files in Git for committing, and discover two powerful methods to undo accidental stagings.
Undo a Git Rebase
This post will show you how to undo a rebase using git reset, git rebase and git revert
Git Push Origin
A breakdown of git push origin
Create Folder In GitHub Repository
Learn how to create and push one or more empty directories in a Git repository using `.placeholder` and `README.md` files using both the CLI and the GitHub interface.
Git Push Tags
This post will show you how to push a single tag, multiple tags, all tags, and tags with commits.
Undoing Git Commits
Explore ways to undo a commit, including git reset, git checkout, and git revert with git while preserving commit history.
Delete Local Git Branch
Learn how to delete local branches from your git repository, including ones with unmerged changes, as well as local remote-tracking branches.