completion: let 'for-each-ref' sort remote branches for 'checkout' DWIMery
[gitweb.git] / contrib / completion / git-completion.bash
index 067dff823da9744980ee47c232cfd89ff05b2139..d26312899db45180524a394be0d0064c2ab38675 100644 (file)
@@ -340,12 +340,12 @@ __git_index_files ()
 
 __git_heads ()
 {
-       __git for-each-ref --format='%(refname:short)' refs/heads
+       __git for-each-ref --format='%(refname:strip=2)' refs/heads
 }
 
 __git_tags ()
 {
-       __git for-each-ref --format='%(refname:short)' refs/tags
+       __git for-each-ref --format='%(refname:strip=2)' refs/tags
 }
 
 # Lists refs from the local (by default) or from a remote repository.
@@ -355,7 +355,8 @@ __git_tags ()
 # 2: In addition to local refs, list unique branches from refs/remotes/ for
 #    'git checkout's tracking DWIMery (optional; ignored, if set but empty).
 # 3: Currently ignored.
-# 4: The current ref to be completed (optional).
+# 4: List only refs matching this word (optional; list all refs if unset or
+#    empty).
 #
 # Use __git_complete_refs() instead.
 __git_refs ()
@@ -364,6 +365,7 @@ __git_refs ()
        local list_refs_from=path remote="${1-}"
        local format refs pfx
        local cur_="${4-$cur}"
+       local match="${4-}"
 
        __git_find_repo_path
        dir="$__git_repo_path"
@@ -387,46 +389,49 @@ __git_refs ()
        fi
 
        if [ "$list_refs_from" = path ]; then
+               if [[ "$cur_" == ^* ]]; then
+                       pfx="^"
+                       cur_=${cur_#^}
+                       match=${match#^}
+               fi
                case "$cur_" in
                refs|refs/*)
                        format="refname"
-                       refs="${cur_%/*}"
+                       refs=("$match*" "$match*/**")
                        track=""
                        ;;
                *)
-                       if [[ "$cur_" == ^* ]]; then
-                               pfx="^"
-                               cur_=${cur_#^}
-                       fi
                        for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD; do
-                               if [ -e "$dir/$i" ]; then echo $pfx$i; fi
+                               case "$i" in
+                               $match*)
+                                       if [ -e "$dir/$i" ]; then
+                                               echo $pfx$i
+                                       fi
+                                       ;;
+                               esac
                        done
-                       format="refname:short"
-                       refs="refs/tags refs/heads refs/remotes"
+                       format="refname:strip=2"
+                       refs=("refs/tags/$match*" "refs/tags/$match*/**"
+                               "refs/heads/$match*" "refs/heads/$match*/**"
+                               "refs/remotes/$match*" "refs/remotes/$match*/**")
                        ;;
                esac
                __git_dir="$dir" __git for-each-ref --format="$pfx%($format)" \
-                       $refs
+                       "${refs[@]}"
                if [ -n "$track" ]; then
                        # employ the heuristic used by git checkout
                        # Try to find a remote branch that matches the completion word
                        # but only output if the branch name is unique
-                       local ref entry
-                       __git for-each-ref --shell --format="ref=%(refname:short)" \
-                               "refs/remotes/" | \
-                       while read -r entry; do
-                               eval "$entry"
-                               ref="${ref#*/}"
-                               if [[ "$ref" == "$cur_"* ]]; then
-                                       echo "$ref"
-                               fi
-                       done | sort | uniq -u
+                       __git for-each-ref --format="%(refname:strip=3)" \
+                               --sort="refname:strip=3" \
+                               "refs/remotes/*/$match*" "refs/remotes/*/$match*/**" | \
+                       uniq -u
                fi
                return
        fi
        case "$cur_" in
        refs|refs/*)
-               __git ls-remote "$remote" "$cur_*" | \
+               __git ls-remote "$remote" "$match*" | \
                while read -r hash i; do
                        case "$i" in
                        *^{}) ;;
@@ -436,12 +441,20 @@ __git_refs ()
                ;;
        *)
                if [ "$list_refs_from" = remote ]; then
-                       echo "HEAD"
-                       __git for-each-ref --format="%(refname:short)" \
-                               "refs/remotes/$remote/" | sed -e "s#^$remote/##"
+                       case "HEAD" in
+                       $match*)        echo "HEAD" ;;
+                       esac
+                       __git for-each-ref --format="%(refname:strip=3)" \
+                               "refs/remotes/$remote/$match*" \
+                               "refs/remotes/$remote/$match*/**"
                else
-                       __git ls-remote "$remote" HEAD \
-                               "refs/tags/*" "refs/heads/*" "refs/remotes/*" |
+                       local query_symref
+                       case "HEAD" in
+                       $match*)        query_symref="HEAD" ;;
+                       esac
+                       __git ls-remote "$remote" $query_symref \
+                               "refs/tags/$match*" "refs/heads/$match*" \
+                               "refs/remotes/$match*" |
                        while read -r hash i; do
                                case "$i" in
                                *^{})   ;;