One of the things that made me much better at Git was making my current branch (and whether or not I have any changed files) always visible. By default zsh includes everything you need to do this, you just need to configure it.
Quick Solution
The short answer is to add this block of code to the bottom of your ~/.zshrc
file. This will use functionality already built into zsh to display your Version Control System information (which includes Git) in your prompt.
After adding these lines, you’ll need to start a new terminal session – you can do this by closing your terminal and opening it again.
# Autoload zsh add-zsh-hook and vcs_info functions (-U autoload w/o substition, -z use zsh style)
autoload -Uz add-zsh-hook vcs_info
# Enable substitution in the prompt.
setopt prompt_subst
# Run vcs_info just before a prompt is displayed (precmd)
add-zsh-hook precmd vcs_info
# add ${vcs_info_msg_0} to the prompt
# e.g. here we add the Git information in red
PROMPT='%1~ %F{red}${vcs_info_msg_0_}%f %# '
# Enable checking for (un)staged changes, enabling use of %u and %c
zstyle ':vcs_info:*' check-for-changes true
# Set custom strings for an unstaged vcs repo changes (*) and staged changes (+)
zstyle ':vcs_info:*' unstagedstr ' *'
zstyle ':vcs_info:*' stagedstr ' +'
# Set the format of the Git information for vcs_info
zstyle ':vcs_info:git:*' formats '(%b%u%c)'
zstyle ':vcs_info:git:*' actionformats '(%b|%a%u%c)'
The rest of this article will go into these settings and how they work.
Default Prompt
By setting the environment variable PROMPT
you can define the information zsh should display on the command line prompt. My default prompt looked something like this
You can view your settings for PROMPT by running the following from the command line.
echo $PROMPT
If you are using the default zsh prompt this will output
%n@%m %1~ %#
The %n
, %m
, and %#
are all zsh prompt sequences which get expanded into “username”, “hostname”, and %
(or #
when logged in as a super user account like root).
zsh add-zsh-hook
add-zsh-hook is a built in function that comes with zsh. You can use this function to run other functions at certain pre-determined times.
Autoloading add-zsh-hook
By default, zsh does not load add-zsh-hook. If we want it to be available, we need to add the following to ~/.zshrc
autoload -Uz add-zsh-hook
-U
mark the function for autoloading and suppress alias expansion-z
use zsh style for function
See zsh function information for more information on functions and autoloading.
zsh vcs_info
vcs_info is a built in function that comes with zsh. You can use this in your zsh prompt by adding the following to your ~/.zshrc
file. (If you want to see the code inside the vcs_info function, you can run the following at the command line declare -f vcs_info
).
Autoloading vcs_info
By default, zsh does not load vcs_info. If we want it to be available, we can add it to our autoload
statement we are already using for add-zsh-hook
in ~/.zshrc
autoload -Uz add-zsh-hook vcs_info
Enable Substitution in the PROMPT
zsh will allow you to use an environment variable in your prompt. Using the format ${variable_name}
but this functionality is off by default. We need to enable it with
setopt prompt_subst
We do this because our Git information is going to be stored in an environment variable (vcs_info_msg_0_
) and we’re going to add this variable to our prompt.
Run vcs_info before displaying the prompt
Here we use the zsh hook function precmd
to run the vcs_info
function right before we display the prompt. When this function runs, it gathers information about Git for the current directory and puts this information in the environment variable vcs_info_msg_0_
.
add-zsh-hook precmd vcs_info
Modify PROMPT
Finally after all this configuration, the result of vcs_info
is stored in vcs_info_msg_0
and we add this to our prompt with ${vcs_info_msg_0_}
PROMPT='%1~ %F{red}${vcs_info_msg_0_}%f %# '
In this case we’ve also wrapped the string in %F{red}
and %f
to set ${vcs_info_msg_0_}
to red.
Have vcs_info check for Git changes
By default, vcs_info does not check for changes (staged or unstaged) because we want to include this information in our prompt we set the vcs_info check-for-changes
to true
.
zstyle ':vcs_info:*' check-for-changes true
This is a vcs_info configuration option.
Modify the Strings Indicating Changes
By default U
and S
are used respectively for unstaged and staged changes when the %u
and %c
placeholders are used. We change these symbols to *
and +
respectively by setting these vcs_info configuration options.
zstyle ':vcs_info:*' unstagedstr ' *'
zstyle ':vcs_info:*' stagedstr ' +'
Set the Git Format of vcs_info
In the same way we can define the format for a prompt (using %
sequences) we can set the format for vcs_info
. The vcs_info
function supports multiple types of Version Control System (VCS), we are only interested in Git at the moment so we target vcs_info:git
.
zstyle ':vcs_info:git:*' formats '(%b%u%c)'
zstyle ':vcs_info:git:*' actionformats '(%b|%a%u%c)'
There are two formats to set:
formats
used for the default “vcs_info” stringactionformats
used when the prompt is displayed and Git is in the middle of an action (likerebase
,merge
, orcherry-pick
)
The sequences used here are:
%b
the current branch name%u
are there any unstaged changes%c
are there any staged changes%a
the current Git action being performed (this only makes sense in actionformats)
Love that you did and how you explained what the code you were adding to the .zshrc does. Probably a subconscious action from the Math Teacher in you.
I learned stuff, thank you!
Thanks for the example.
I have a question though. I change the prompt to this:
PROMPT=’%1~ %F{red}${vcs_info_msg_0_}%f ‘
But what I don’t get is that when I’m not in a git context I see this:
somedir [user@compname]$
I can’t figure out where this ‘[user@compname]$’ comes from.
Any idea?
Cheers,
Danny
Mm.. nevermind.. after a new terminal session, this no longer seems to be the case
Thanks for pointing this out, Danny. I’ve added a note about starting a new terminal session to this blog post. Thanks.
Or you can run the command
source ~/.zshrc
How to know that there are untracked files (after changes or after creating new files)
Thank you for this article. The best explanation I’ve found about this subject.
For developers using bash, you can use something like the following in your
.bashrc
file in order to see the highlighted name of your current git branch in the terminal:parse_git_branch() {
git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/(\1)/'
}
export PS1="\u@\h \[\e[32m\]\w \[\e[91m\]\$(parse_git_branch)\[\e[00m\]$ "
You’ll need to exit any open terminal windows after making the change in order for it to be seen.