Merge branch 'rr/rebase-checkout-reflog'
[gitweb.git] / contrib / completion / git-prompt.sh
index 7d226251283f374a28e94ef91bfba2227dffb41c..a81ef5a482d48b114a81e242109e2f0dec0839ed 100644 (file)
@@ -3,7 +3,7 @@
 # Copyright (C) 2006,2007 Shawn O. Pearce <spearce@spearce.org>
 # Distributed under the GNU General Public License, version 2.0.
 #
-# This script allows you to see the current branch in your prompt.
+# This script allows you to see repository status in your prompt.
 #
 # To enable:
 #
 #    3a) Change your PS1 to call __git_ps1 as
 #        command-substitution:
 #        Bash: PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
-#        ZSH:  PS1='[%n@%m %c$(__git_ps1 " (%s)")]\$ '
+#        ZSH:  setopt PROMPT_SUBST ; PS1='[%n@%m %c$(__git_ps1 " (%s)")]\$ '
 #        the optional argument will be used as format string.
-#    3b) Alternatively, if you are using bash, __git_ps1 can be
-#        used for PROMPT_COMMAND with two parameters, <pre> and
-#        <post>, which are strings you would put in $PS1 before
-#        and after the status string generated by the git-prompt
-#        machinery.  e.g.
+#    3b) Alternatively, for a slightly faster prompt, __git_ps1 can
+#        be used for PROMPT_COMMAND in Bash or for precmd() in Zsh
+#        with two parameters, <pre> and <post>, which are strings
+#        you would put in $PS1 before and after the status string
+#        generated by the git-prompt machinery.  e.g.
 #        Bash: PROMPT_COMMAND='__git_ps1 "\u@\h:\w" "\\\$ "'
+#          will show username, at-sign, host, colon, cwd, then
+#          various status string, followed by dollar and SP, as
+#          your prompt.
 #        ZSH:  precmd () { __git_ps1 "%n" ":%~$ " "|%s" }
-#        will show username, at-sign, host, colon, cwd, then
-#        various status string, followed by dollar and SP, as
-#        your prompt.
+#          will show username, pipe, then various status string,
+#          followed by colon, cwd, dollar and SP, as your prompt.
 #        Optionally, you can supply a third argument with a printf
 #        format string to finetune the output of the branch status
 #
-# The argument to __git_ps1 will be displayed only if you are currently
-# in a git repository.  The %s token will be the name of the current
-# branch.
+# The repository status will be displayed only if you are currently in a
+# git repository. The %s token is the placeholder for the shown status.
+#
+# The prompt status always includes the current branch name.
 #
 # In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty value,
 # unstaged (*) and staged (+) changes will be shown next to the branch
@@ -78,7 +81,8 @@
 #
 # If you would like a colored hint about the current dirty state, set
 # GIT_PS1_SHOWCOLORHINTS to a nonempty value. The colors are based on
-# the colored output of "git status -sb".
+# the colored output of "git status -sb" and are available only when
+# using __git_ps1 for PROMPT_COMMAND or precmd.
 
 # stores the divergence from upstream in $p
 # used by GIT_PS1_SHOWUPSTREAM
@@ -201,8 +205,8 @@ __git_ps1_show_upstream ()
 }
 
 # Helper function that is meant to be called from __git_ps1.  It
-# builds up a gitstring injecting color codes into the appropriate
-# places.
+# injects color codes into the appropriate gitstring variables used
+# to build a gitstring.
 __git_ps1_colorize_gitstring ()
 {
        if [[ -n ${ZSH_VERSION-} ]]; then
@@ -210,74 +214,40 @@ __git_ps1_colorize_gitstring ()
                local c_green='%F{green}'
                local c_lblue='%F{blue}'
                local c_clear='%f'
-               local bad_color=$c_red
-               local ok_color=$c_green
-               local branch_color="$c_clear"
-               local flags_color="$c_lblue"
-               local branchstring="$c${b##refs/heads/}"
-
-               if [ $detached = no ]; then
-                       branch_color="$ok_color"
-               else
-                       branch_color="$bad_color"
-               fi
-
-               gitstring="$branch_color$branchstring$c_clear"
-
-               if [ -n "$w$i$s$u$r$p" ]; then
-                       gitstring="$gitstring$z"
-               fi
-               if [ "$w" = "*" ]; then
-                       gitstring="$gitstring$bad_color$w"
-               fi
-               if [ -n "$i" ]; then
-                       gitstring="$gitstring$ok_color$i"
-               fi
-               if [ -n "$s" ]; then
-                       gitstring="$gitstring$flags_color$s"
-               fi
-               if [ -n "$u" ]; then
-                       gitstring="$gitstring$bad_color$u"
-               fi
-               gitstring="$gitstring$c_clear$r$p"
-               return
+       else
+               # Using \[ and \] around colors is necessary to prevent
+               # issues with command line editing/browsing/completion!
+               local c_red='\[\e[31m\]'
+               local c_green='\[\e[32m\]'
+               local c_lblue='\[\e[1;34m\]'
+               local c_clear='\[\e[0m\]'
        fi
-       local c_red='\e[31m'
-       local c_green='\e[32m'
-       local c_lblue='\e[1;34m'
-       local c_clear='\e[0m'
        local bad_color=$c_red
        local ok_color=$c_green
-       local branch_color="$c_clear"
        local flags_color="$c_lblue"
-       local branchstring="$c${b##refs/heads/}"
 
+       local branch_color=""
        if [ $detached = no ]; then
                branch_color="$ok_color"
        else
                branch_color="$bad_color"
        fi
+       c="$branch_color$c"
 
-       # Setting gitstring directly with \[ and \] around colors
-       # is necessary to prevent wrapping issues!
-       gitstring="\[$branch_color\]$branchstring\[$c_clear\]"
-
-       if [ -n "$w$i$s$u$r$p" ]; then
-               gitstring="$gitstring$z"
-       fi
+       z="$c_clear$z"
        if [ "$w" = "*" ]; then
-               gitstring="$gitstring\[$bad_color\]$w"
+               w="$bad_color$w"
        fi
        if [ -n "$i" ]; then
-               gitstring="$gitstring\[$ok_color\]$i"
+               i="$ok_color$i"
        fi
        if [ -n "$s" ]; then
-               gitstring="$gitstring\[$flags_color\]$s"
+               s="$flags_color$s"
        fi
        if [ -n "$u" ]; then
-               gitstring="$gitstring\[$bad_color\]$u"
+               u="$bad_color$u"
        fi
-       gitstring="$gitstring\[$c_clear\]$r$p"
+       r="$c_clear$r"
 }
 
 # __git_ps1 accepts 0 or 1 arguments (i.e., format string)
@@ -311,8 +281,12 @@ __git_ps1 ()
                ;;
        esac
 
-       local repo_info="$(git rev-parse --git-dir --is-inside-git-dir \
-               --is-bare-repository --is-inside-work-tree 2>/dev/null)"
+       local repo_info rev_parse_exit_code
+       repo_info="$(git rev-parse --git-dir --is-inside-git-dir \
+               --is-bare-repository --is-inside-work-tree \
+               --short HEAD 2>/dev/null)"
+       rev_parse_exit_code="$?"
+
        if [ -z "$repo_info" ]; then
                if [ $pcmode = yes ]; then
                        #In PC mode PS1 always needs to be set
@@ -321,6 +295,11 @@ __git_ps1 ()
                return
        fi
 
+       local short_sha
+       if [ "$rev_parse_exit_code" = "0" ]; then
+               short_sha="${repo_info##*$'\n'}"
+               repo_info="${repo_info%$'\n'*}"
+       fi
        local inside_worktree="${repo_info##*$'\n'}"
        repo_info="${repo_info%$'\n'*}"
        local bare_repo="${repo_info##*$'\n'}"
@@ -392,8 +371,7 @@ __git_ps1 ()
                                        git describe --tags --exact-match HEAD ;;
                                esac 2>/dev/null)" ||
 
-                               b="$(git rev-parse --short HEAD 2>/dev/null)..." ||
-                               b="unknown"
+                               b="$short_sha..."
                                b="($b)"
                        fi
                fi
@@ -421,19 +399,20 @@ __git_ps1 ()
                   [ "$(git config --bool bash.showDirtyState)" != "false" ]
                then
                        git diff --no-ext-diff --quiet --exit-code || w="*"
-                       if git rev-parse --quiet --verify HEAD >/dev/null; then
+                       if [ -n "$short_sha" ]; then
                                git diff-index --cached --quiet HEAD -- || i="+"
                        else
                                i="#"
                        fi
                fi
-               if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ]; then
-                       git rev-parse --verify refs/stash >/dev/null 2>&1 && s="$"
+               if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ] &&
+                  [ -r "$g/refs/stash" ]; then
+                       s="$"
                fi
 
                if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ] &&
                   [ "$(git config --bool bash.showUntrackedFiles)" != "false" ] &&
-                  [ -n "$(git ls-files --others --exclude-standard)" ]
+                  git ls-files --others --exclude-standard --error-unmatch -- '*' >/dev/null 2>/dev/null
                then
                        u="%${ZSH_VERSION+%}"
                fi
@@ -444,18 +423,23 @@ __git_ps1 ()
        fi
 
        local z="${GIT_PS1_STATESEPARATOR-" "}"
+
+       # NO color option unless in PROMPT_COMMAND mode
+       if [ $pcmode = yes ] && [ -n "${GIT_PS1_SHOWCOLORHINTS-}" ]; then
+               __git_ps1_colorize_gitstring
+       fi
+
        local f="$w$i$s$u"
+       local gitstring="$c${b##refs/heads/}${f:+$z$f}$r$p"
+
        if [ $pcmode = yes ]; then
-               local gitstring=
-               if [ -n "${GIT_PS1_SHOWCOLORHINTS-}" ]; then
-                       __git_ps1_colorize_gitstring
+               if [[ -n ${ZSH_VERSION-} ]]; then
+                       gitstring=$(printf -- "$printf_format" "$gitstring")
                else
-                       gitstring="$c${b##refs/heads/}${f:+$z$f}$r$p"
+                       printf -v gitstring -- "$printf_format" "$gitstring"
                fi
-               gitstring=$(printf -- "$printf_format" "$gitstring")
                PS1="$ps1pc_start$gitstring$ps1pc_end"
        else
-               # NO color option unless in PROMPT_COMMAND mode
-               printf -- "$printf_format" "$c${b##refs/heads/}${f:+$z$f}$r$p"
+               printf -- "$printf_format" "$gitstring"
        fi
 }