bash prompt: use bash builtins to check stash state
[gitweb.git] / contrib / completion / git-prompt.sh
index a915b04c1d495dbb1adb6f92db791b36af0a07b2..afa867030de12916358d24e63c4335b4f43c8a91 100644 (file)
 # GIT_PS1_SHOWCOLORHINTS to a nonempty value. The colors are based on
 # the colored output of "git status -sb".
 
-# __gitdir accepts 0 or 1 arguments (i.e., location)
-# returns location of .git repo
-__gitdir ()
-{
-       # Note: this function is duplicated in git-completion.bash
-       # When updating it, make sure you update the other one to match.
-       if [ -z "${1-}" ]; then
-               if [ -n "${__git_dir-}" ]; then
-                       echo "$__git_dir"
-               elif [ -n "${GIT_DIR-}" ]; then
-                       test -d "${GIT_DIR-}" || return 1
-                       echo "$GIT_DIR"
-               elif [ -d .git ]; then
-                       echo .git
-               else
-                       git rev-parse --git-dir 2>/dev/null
-               fi
-       elif [ -d "$1/.git" ]; then
-               echo "$1/.git"
-       else
-               echo "$1"
-       fi
-}
-
 # stores the divergence from upstream in $p
 # used by GIT_PS1_SHOWUPSTREAM
 __git_ps1_show_upstream ()
@@ -335,8 +311,13 @@ __git_ps1 ()
                ;;
        esac
 
-       local g="$(__gitdir)"
-       if [ -z "$g" ]; then
+       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
                        PS1="$ps1pc_start$ps1pc_end"
@@ -344,14 +325,26 @@ __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'}"
+       repo_info="${repo_info%$'\n'*}"
+       local inside_gitdir="${repo_info##*$'\n'}"
+       local g="${repo_info%$'\n'*}"
+
        local r=""
        local b=""
        local step=""
        local total=""
        if [ -d "$g/rebase-merge" ]; then
-               b="$(cat "$g/rebase-merge/head-name" 2>/dev/null)"
-               step=$(cat "$g/rebase-merge/msgnum" 2>/dev/null)
-               total=$(cat "$g/rebase-merge/end" 2>/dev/null)
+               read b 2>/dev/null <"$g/rebase-merge/head-name"
+               read step 2>/dev/null <"$g/rebase-merge/msgnum"
+               read total 2>/dev/null <"$g/rebase-merge/end"
                if [ -f "$g/rebase-merge/interactive" ]; then
                        r="|REBASE-i"
                else
@@ -359,10 +352,10 @@ __git_ps1 ()
                fi
        else
                if [ -d "$g/rebase-apply" ]; then
-                       step=$(cat "$g/rebase-apply/next" 2>/dev/null)
-                       total=$(cat "$g/rebase-apply/last" 2>/dev/null)
+                       read step 2>/dev/null <"$g/rebase-apply/next"
+                       read total 2>/dev/null <"$g/rebase-apply/last"
                        if [ -f "$g/rebase-apply/rebasing" ]; then
-                               b="$(cat "$g/rebase-apply/head-name" 2>/dev/null)"
+                               read b 2>/dev/null <"$g/rebase-apply/head-name"
                                r="|REBASE"
                        elif [ -f "$g/rebase-apply/applying" ]; then
                                r="|AM"
@@ -379,25 +372,39 @@ __git_ps1 ()
                        r="|BISECTING"
                fi
 
-               test -n "$b" ||
-               b="$(git symbolic-ref HEAD 2>/dev/null)" || {
-                       detached=yes
-                       b="$(
-                       case "${GIT_PS1_DESCRIBE_STYLE-}" in
-                       (contains)
-                               git describe --contains HEAD ;;
-                       (branch)
-                               git describe --contains --all HEAD ;;
-                       (describe)
-                               git describe HEAD ;;
-                       (* | default)
-                               git describe --tags --exact-match HEAD ;;
-                       esac 2>/dev/null)" ||
+               if [ -n "$b" ]; then
+                       :
+               elif [ -h "$g/HEAD" ]; then
+                       # symlink symbolic ref
+                       b="$(git symbolic-ref HEAD 2>/dev/null)"
+               else
+                       local head=""
+                       if ! read head 2>/dev/null <"$g/HEAD"; then
+                               if [ $pcmode = yes ]; then
+                                       PS1="$ps1pc_start$ps1pc_end"
+                               fi
+                               return
+                       fi
+                       # is it a symbolic ref?
+                       b="${head#ref: }"
+                       if [ "$head" = "$b" ]; then
+                               detached=yes
+                               b="$(
+                               case "${GIT_PS1_DESCRIBE_STYLE-}" in
+                               (contains)
+                                       git describe --contains HEAD ;;
+                               (branch)
+                                       git describe --contains --all HEAD ;;
+                               (describe)
+                                       git describe HEAD ;;
+                               (* | default)
+                                       git describe --tags --exact-match HEAD ;;
+                               esac 2>/dev/null)" ||
 
-                       b="$(git rev-parse --short HEAD 2>/dev/null)..." ||
-                       b="unknown"
-                       b="($b)"
-               }
+                               b="$short_sha..."
+                               b="($b)"
+                       fi
+               fi
        fi
 
        if [ -n "$step" ] && [ -n "$total" ]; then
@@ -411,25 +418,26 @@ __git_ps1 ()
        local c=""
        local p=""
 
-       if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then
-               if [ "true" = "$(git rev-parse --is-bare-repository 2>/dev/null)" ]; then
+       if [ "true" = "$inside_gitdir" ]; then
+               if [ "true" = "$bare_repo" ]; then
                        c="BARE:"
                else
                        b="GIT_DIR!"
                fi
-       elif [ "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]; then
+       elif [ "true" = "$inside_worktree" ]; then
                if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ] &&
                   [ "$(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-}" ] &&