Git state and shell prompts

By Eddie Lebow

Git has a stateful interface—certain commands behave differently depending on whether the repo is in the midst of an action like rebasing, merging, or bisecting. It can be frustrating to notice through the CLI whether git is in one of these states.

The best solution is to include git’s current “action” in your shell prompt. Shell prompts are a big and personal topic, so let’s limit our discussion to git status. Also, I only use zsh so that’s all I can speak of authoritatively.

zsh has functionality that can populate the special variable vcs_info_msg_0_ with information from several version control systems. The contents of that variable are controlled by setting some zstyle options. See for the full documentation.

There are two styles relevant to this discussion.

style namedescription
formats a string that will be evaluated to determine the contents of vcs_info_msg_0_ when git is not in the midst of an action (ie, when git is in its normal state)
actionformats a string that will be evaluated to determine the contents of vcs_info_msg_0_ when git is in the midst of an action.

Below is a simple example. Note that %b will be replaced by the branch name and %a will be replaced by the current action, if any. See the zsh docs for more variables you can use.

zstyle ':vcs_info:*' formats       "[%b]"
zstyle ':vcs_info:*' actionformats "[%b|%a]" 

precmd () { vcs_info }  # precmd is a hook executed before each time the prompt is rendered. vcs_info populates the variable vcs_info_msg_0_. 

PS1='my great prompt ${vcs_info_msg_0_}'

This is sufficient to overcome the “what state is git in?” problem. But while we’re talking about git and prompts, here are other things I include:

  • Branch name
  • Whether there are staged changes, unstaged changes, or untracked files present – It only takes three characters to convey all of this information, so it’s an easy decision to include.
  • Size of stash – This is important for me, because stash size isn’t visible in git status and I’m not in the habit of checking the stash. The longer things sit in the stash, the more likely there are to have conflicts—I prefer to commit them to a WIP branch quickly.

And here are some things I don’t include, to keep the prompt uncluttered:

  • Divergence from remote – My workflow usually involves a single developer owning a given branch, so it’s rare for origin to get ahead of local unexpectedly. And I just assume that the mainline branch is always ahead of local. I can see more detail in git log.
  • Renamed or deleted files – I think these show up as staged/unstaged changes. I can see more detail in git status.
  • Current SHA – I rarely need to know


All third-party trademarks referenced by Cofense whether in logo form, name form or product form, or otherwise, remain the property of their respective holders, and use of these trademarks in no way indicates any relationship between Cofense and the holders of the trademarks.