94

I run the following codes separately as my prompt unsuccessfully in .zshrc. This suggests me that apparently I do not have a program called __git_ps1. It is not in MacPorts.

#1

PROMPT="$(__git_ps1 " \[\033[1;32m\] (%s)\[\033[0m\]")\$"$

#2

PROMPT="$(__git_ps1 " (%s)")\$"$

#3

# Get the name of the branch we are on
git_prompt_info() {
  branch_prompt=$(__git_ps1)
  if [ -n "$branch_prompt" ]; then
    status_icon=$(git_status)
    echo $branch_prompt $status_icon
  fi
}

# Show character if changes are pending
git_status() {
  if current_git_status=$(git status | grep 'added to commit' 2> /dev/null); then
    echo "☠"
  fi
}
autoload -U colors
colors
setopt prompt_subst
PROMPT='
%~%{$fg_bold[black]%}$(git_prompt_info)
→ %{$reset_color%}'

How can you get a prompt which shows the name of a Git-branch?

Léo Léopold Hertz 준영
  • 134,464
  • 179
  • 445
  • 697

9 Answers9

87

__git_ps1 is from git-completion.bash. In zsh you probably have to provide your own function to determine the current directories git branch. There are quite a few blog posts about a git prompt for zsh.

You just need:

  • a function to provide the branch name
  • enable prompt (command) substitution
  • add the function to your prompt

For example

git_prompt() {
 ref=$(git symbolic-ref HEAD | cut -d'/' -f3)
 echo $ref
}
setopt prompt_subst
PS1=$(git_prompt)%#
autoload -U promptinit
promptinit

Update: use the zsh vcs_info module instead of git_prompt()

setopt prompt_subst
autoload -Uz vcs_info
zstyle ':vcs_info:*' actionformats \
    '%F{5}(%f%s%F{5})%F{3}-%F{5}[%F{2}%b%F{3}|%F{1}%a%F{5}]%f '
zstyle ':vcs_info:*' formats       \
    '%F{5}(%f%s%F{5})%F{3}-%F{5}[%F{2}%b%F{5}]%f '
zstyle ':vcs_info:(sv[nk]|bzr):*' branchformat '%b%F{1}:%F{3}%r'

zstyle ':vcs_info:*' enable git cvs svn

# or use pre_cmd, see man zshcontrib
vcs_info_wrapper() {
  vcs_info
  if [ -n "$vcs_info_msg_0_" ]; then
    echo "%{$fg[grey]%}${vcs_info_msg_0_}%{$reset_color%}$del"
  fi
}
RPROMPT=$'$(vcs_info_wrapper)'
Ronan Boiteau
  • 9,608
  • 6
  • 34
  • 56
ko-dos
  • 1,420
  • 12
  • 11
  • 5
    git_prompt is wrong, if you have a branch with / it does not work. Use `cut -d'/' -f3-` instead. – drizzt Jan 24 '13 at 17:40
  • 1
    Thanks!, this works. Can you please explain what those cryptic zstyle commands do? – balki Feb 25 '13 at 12:06
  • 1
    @balki The `%F{n}` enable [ANSI color](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) `n` for foreground, and `%f` disables foreground color. E.g., the `formats` format `%F{5}(%f%s%F{5})%F{3}-%F{5}[%F{2}%b%F{3}]%f ` becomes `(%s)-[%b]` if you ignore the colors. The `%s` gets replaced by the vc system (e.g. git) and the `%b` gets replaced by the current branch. – ntc2 May 09 '13 at 02:48
  • 1
    @balki The `(sv[nk]|bzr)` subpattern on the `branchformat` restricts it to `svn`, `svk`, and `bzr`. It means that those systems should use `branch:revision` instead of the default `branch`. Because of the `enable` line, `bzr` is not supported, so this `bzr` restriction is "dead code". The first three lines are copied verbatim from the [ZSH docs](http://zsh.sourceforge.net/Doc/Release/User-Contributions.html#Version-Control-Information), but the last line was added by someone else, which probably explains the dead code. – ntc2 May 09 '13 at 02:51
  • 1
    @balki The `actionformats` becomes `(%s)-[%b|%a]` ignoring colors. This format is used during special actions, with `%a` describing the action. This is the most useful feature IMHO: e.g. for git it tells when you are in the middle of a rebase (which I often forget about while trying to resolve merge conflicts). – ntc2 May 09 '13 at 02:58
  • I've been using this for a long time, and it always worked well. Now however, after updating to zsh 5.0.7 and git 1.8.4, I get "fatal: Not a git repository" whenever going into a directory that is not a git repo. Any suggestions? (I see many discussions on detecting whether or not we're in a git repo, but I don't want to exit early if not, I also have svn repos... vcs_info was great at encapsulating all that...) – David Faure Dec 22 '14 at 11:07
  • Ah, found a solution: replacing the call to vcs_info with vcs_info 2>/dev/null – David Faure Dec 22 '14 at 17:23
  • link for `git prompt` seems dead :( – alper Aug 18 '21 at 11:57
  • In the first simple solution, also use `cut -d'/' -f3-`, it will make sure to include the full name of the branch, if the branch name is like `feature/xyz` – SwiftMango Aug 27 '21 at 21:54
68

A lot of these solutions seemed slow for me when mashing the return key, so here's an option that is basic and speedy:

parse_git_branch() {
    git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ (\1)/'
}

setopt PROMPT_SUBST
PROMPT='%9c%{%F{green}%}$(parse_git_branch)%{%F{none}%} $ '

You'll get a prompt that looks like this: ~/dev/project (feature-branch) $

Ronan Boiteau
  • 9,608
  • 6
  • 34
  • 56
Phil
  • 813
  • 7
  • 6
52

Here is an extended git prompt for zsh: zsh-git-prompt.

alt text

Ev0oD
  • 1,395
  • 16
  • 33
Olivier Verdier
  • 46,998
  • 29
  • 98
  • 90
  • 1
    Love it! Perfect. Very nice work, and now a submodule in my own dotfiles repo. :) – Justin Force Jul 02 '12 at 21:23
  • 3
    How does your approach compare to vcs_info? It seems that your script is reinventing the wheel? (Didn't stop me from using it though, I was unaware of vcs_info) – harm Apr 04 '13 at 10:14
  • having trouble during sourcing: " unrecognized modifier `A'". zsh 4.2.6 (x86_64-redhat-linux-gnu). Is it a known issue? – Timofey Jul 23 '15 at 03:01
  • Repository no longer maintained - seems like some guys took over here: https://github.com/zsh-git-prompt/zsh-git-prompt editing answer to link to it – Ev0oD Sep 16 '20 at 09:22
  • Couldn't make it run neither with the repo I mention above. Found out ohmyzsh does it nicely, see https://stackoverflow.com/a/63919265/1920149 – Ev0oD Dec 02 '20 at 12:18
41

ko-dos's answer is great, but I prefer a slightly more git aware prompt than the one he uses. Specifically, I like staged, unstaged, and untracked file notifications in the prompt itself. Using his answer and the zsh vcs_info examples, I came up with the following:

setopt prompt_subst
autoload -Uz vcs_info
zstyle ':vcs_info:*' stagedstr 'M' 
zstyle ':vcs_info:*' unstagedstr 'M' 
zstyle ':vcs_info:*' check-for-changes true
zstyle ':vcs_info:*' actionformats '%F{5}[%F{2}%b%F{3}|%F{1}%a%F{5}]%f '
zstyle ':vcs_info:*' formats \
  '%F{5}[%F{2}%b%F{5}] %F{2}%c%F{3}%u%f'
zstyle ':vcs_info:git*+set-message:*' hooks git-untracked
zstyle ':vcs_info:*' enable git 
+vi-git-untracked() {
  if [[ $(git rev-parse --is-inside-work-tree 2> /dev/null) == 'true' ]] && \
  [[ $(git ls-files --other --directory --exclude-standard | sed q | wc -l | tr -d ' ') == 1 ]] ; then
  hook_com[unstaged]+='%F{1}??%f'
fi
}


precmd () { vcs_info }
PROMPT='%F{5}[%F{2}%n%F{5}] %F{3}%3~ ${vcs_info_msg_0_} %f%# '

This creates a prompt that mimics the colorized output of git status -s (which can be configured in your .gitconfig file). A picture is perhaps most helpful here:

prompt

Compared with git status -s:

enter image description here

If you don't like colorized output, or would prefer some other character or prompt construction, just change the stagedstr, unstagedstr, and hook_com[unstaged] values in the above code.

Christopher
  • 42,720
  • 11
  • 81
  • 99
  • 2
    This is a great git-specific use of `vcs_info`. One thing though: `git status` is *slow*. I used `git ls-files --other --directory --exclude-standard | sed q | wc -l | tr -d ' '` instead, and it's much faster. – Thomas Upton Jun 29 '14 at 18:11
  • 1
    Nice modification. Updated accordingly. – Christopher Jul 01 '14 at 03:25
  • The `--directory` flag might not actually be necessary. I found that it was annoying because git usually ignores empty directories. – Thomas Upton Jul 09 '14 at 03:40
  • 1
    Had the same problem (i.e. empty directories ignored by git caused `??` to appear in the prompt). Fixed by adding `--no-empty-directory` flag to the `git ls-files` command. – szeryf Oct 02 '14 at 08:27
10

I just redid mine since we have long branch names at work. This one will truncate with an ellipsis if it's more than 35 characters.

parse_git_branch() {
    git_status="$(git status 2> /dev/null)"
    pattern="On branch ([^[:space:]]*)"
    if [[ ! ${git_status} =~ "(working (tree|directory) clean)" ]]; then
        state="*"
    fi
    if [[ ${git_status} =~ ${pattern} ]]; then
      branch=${match[1]}
      branch_cut=${branch:0:35}
      if (( ${#branch} > ${#branch_cut} )); then
          echo "(${branch_cut}…${state})"
      else
          echo "(${branch}${state})"
      fi
    fi
}

setopt PROMPT_SUBST
PROMPT='%{%F{blue}%}%9c%{%F{none}%}$(parse_git_branch)$'

(I'm embarrassed at how proud I am of this.)

Dan Rosenstark
  • 68,471
  • 58
  • 283
  • 421
8

This already has a many great answers, and seems like the authoritative Question for readers who don't want to use more than the zsh config and git.

However, I found myself wanting simply

working-directory (git-branch-color-if-uncommitted) %          24h-timestamp
git_branch_test_color() {
  local ref=$(git symbolic-ref --short HEAD 2> /dev/null)
  if [ -n "${ref}" ]; then
    if [ -n "$(git status --porcelain)" ]; then
      local gitstatuscolor='%F{red}'
    else
      local gitstatuscolor='%F{green}'
    fi
    echo "${gitstatuscolor} (${ref})"
  else
    echo ""
  fi
}
setopt PROMPT_SUBST
PROMPT='%9c$(git_branch_test_color)%F{none} %# '

# add 24h time the right side
RPROMPT='%D{%k:%M:%S}'

prompt coloration example

This works by

  • allowing inclusion of a function ($()) by setting PROMPT_SUBST (thanks to Phil's 2017 answer!)
  • declaring a function git_branch_test_color to call git for the branch ref ..and by whether this succeeds (tests for stdout) if the current directory is a git repository
    (thanks to a few comments for symbolic-ref arg!)
    • if not a git repo, only echo an empty string ""
  • if the initial call did succeed (does have collected stdout), the current directory is a git directory and
    git status --porcelain is used to determine if any files have been changed or are new/deleted
    • green coloration prefix for unchanged
    • red coloration prefix for changes (whatever they may be)

Note that all such prompts can be slow on mounted directories (especially via sshfs). If this is experienced, I recommend special-casing those directories if at all possible (perhaps create a new shell function to check if cd is in your collection) or always mount in the same path and ignore it (example with vcs_info)

ti7
  • 16,375
  • 6
  • 40
  • 68
5

If all your prompt looks fine but it does not work, e.g:

function git_branch(){                                                                                                                 
    ref=$(git symbolic-ref --short --quiet HEAD 2>/dev/null)        
    if [ -n "${ref}" ]; then                                                    
        echo "(""$ref"")"                                                       
    fi                                                                          
}                                                                               
                                                                                
setopt PROMPT_SUBST                                                             

PROMPT="%{$fg[red]%}%n%{$reset_color%}@%{$fg[blue]%}%m:%{$fg[yellow]%}%1~%{$reset_color%} %{%F{green}%}$(git_branch)%{%F{none}%}$ "

The PROMPT must set to a string quoted using single quotes instead of double quotes.

PROMPT='%{$fg[red]%}%n%{$reset_color%}@%{$fg[blue]%}%m:%{$fg[yellow]%}%1~%{$reset_color%} %{%F{green}%}$(git_branch)%{%F{none}%}$ '
HVNSweeting
  • 2,859
  • 2
  • 35
  • 30
3

For anyone interested in a maintained ready-made solution, install https://github.com/ohmyzsh/ohmyzsh - it has a git plugin turned on by default. I was looking for sth like this - no-brainer pre-created configuration, works nice.

Ev0oD
  • 1,395
  • 16
  • 33
-1

Thank you for the links!

I made the following prompt based on them

     # get the name of the branch we are on
     git_prompt_info() { 
         git branch | awk '/^\*/ { print $2 }'
     }
     get_git_dirty() { 
       git diff --quiet || echo '*'
     }

     autoload -U colors
     colors     
     setopt prompt_subst

     PROMPT='%{$fg[blue]%}%c %{$fg_bold[red]%}$(git_prompt_info)$(get_git_dirty)%{$fg[blue]%} $ %{$reset_color%}'                                           

     RPROMPT='%{$fg[green]%}%1(j.%j.)'        

Please, suggest any improvements.

Léo Léopold Hertz 준영
  • 134,464
  • 179
  • 445
  • 697