Merge branch 'sg/completion-refs-speedup'
authorJunio C Hamano <gitster@pobox.com>
Thu, 30 Mar 2017 21:07:14 +0000 (14:07 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 30 Mar 2017 21:07:14 +0000 (14:07 -0700)
The refs completion for large number of refs has been sped up,
partly by giving up disambiguating ambiguous refs and partly by
eliminating most of the shell processing between 'git for-each-ref'
and 'ls-remote' and Bash's completion facility.

* sg/completion-refs-speedup:
completion: speed up branch and tag completion
completion: fill COMPREPLY directly when completing fetch refspecs
completion: fill COMPREPLY directly when completing refs
completion: let 'for-each-ref' sort remote branches for 'checkout' DWIMery
completion: let 'for-each-ref' filter remote branches for 'checkout' DWIMery
completion: let 'for-each-ref' strip the remote name from remote branches
completion: let 'for-each-ref' and 'ls-remote' filter matching refs
completion: don't disambiguate short refs
completion: don't disambiguate tags and branches
completion: support excluding full refs
completion: support completing fully qualified non-fast-forward refspecs
completion: support completing full refs after '--option=refs/<TAB>'
completion: wrap __git_refs() for better option parsing
completion: remove redundant __gitcomp_nl() options from _git_commit()

1  2 
contrib/completion/git-completion.bash
index fc32286a43cdece9ea468c4a07c2a8e3d585f1b5,bd07d9a7461021be6ab58b5083c34169e455f965..380f755b9fd0a049d5edf5f54210aac39ab48d3e
@@@ -213,6 -213,20 +213,20 @@@ _get_comp_words_by_ref (
  }
  fi
  
+ # Fills the COMPREPLY array with prefiltered words without any additional
+ # processing.
+ # Callers must take care of providing only words that match the current word
+ # to be completed and adding any prefix and/or suffix (trailing space!), if
+ # necessary.
+ # 1: List of newline-separated matching completion words, complete with
+ #    prefix and suffix.
+ __gitcomp_direct ()
+ {
+       local IFS=$'\n'
+       COMPREPLY=($1)
+ }
  __gitcompappend ()
  {
        local x i=${#COMPREPLY[@]}
@@@ -338,14 -352,27 +352,27 @@@ __git_index_files (
        done | sort | uniq
  }
  
+ # Lists branches from the local repository.
+ # 1: A prefix to be added to each listed branch (optional).
+ # 2: List only branches matching this word (optional; list all branches if
+ #    unset or empty).
+ # 3: A suffix to be appended to each listed branch (optional).
  __git_heads ()
  {
-       __git for-each-ref --format='%(refname:short)' refs/heads
+       local pfx="${1-}" cur_="${2-}" sfx="${3-}"
+       __git for-each-ref --format="${pfx//\%/%%}%(refname:strip=2)$sfx" \
+                       "refs/heads/$cur_*" "refs/heads/$cur_*/**"
  }
  
+ # Lists tags from the local repository.
+ # Accepts the same positional parameters as __git_heads() above.
  __git_tags ()
  {
-       __git for-each-ref --format='%(refname:short)' refs/tags
+       local pfx="${1-}" cur_="${2-}" sfx="${3-}"
+       __git for-each-ref --format="${pfx//\%/%%}%(refname:strip=2)$sfx" \
+                       "refs/tags/$cur_*" "refs/tags/$cur_*/**"
  }
  
  # Lists refs from the local (by default) or from a remote repository.
  #    Can be the name of a configured remote, a path, or a URL.
  # 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: A prefix to be added to each listed ref (optional).
+ # 4: List only refs matching this word (optional; list all refs if unset or
+ #    empty).
+ # 5: A suffix to be appended to each listed ref (optional; ignored, if set
+ #    but empty).
+ #
+ # Use __git_complete_refs() instead.
  __git_refs ()
  {
        local i hash dir track="${2-}"
        local list_refs_from=path remote="${1-}"
-       local format refs pfx
+       local format refs
+       local pfx="${3-}" cur_="${4-$cur}" sfx="${5-}"
+       local match="${4-}"
+       local fer_pfx="${pfx//\%/%%}" # "escape" for-each-ref format specifiers
  
        __git_find_repo_path
        dir="$__git_repo_path"
        fi
  
        if [ "$list_refs_from" = path ]; then
-               case "$cur" in
+               if [[ "$cur_" == ^* ]]; then
+                       pfx="$pfx^"
+                       fer_pfx="$fer_pfx^"
+                       cur_=${cur_#^}
+                       match=${match#^}
+               fi
+               case "$cur_" in
                refs|refs/*)
                        format="refname"
-                       refs="${cur%/*}"
+                       refs=("$match*" "$match*/**")
                        track=""
                        ;;
                *)
-                       [[ "$cur" == ^* ]] && pfx="^"
                        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$sfx"
+                                       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
+               __git_dir="$dir" __git for-each-ref --format="$fer_pfx%($format)$sfx" \
+                       "${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="$fer_pfx%(refname:strip=3)$sfx" \
+                               --sort="refname:strip=3" \
+                               "refs/remotes/*/$match*" "refs/remotes/*/$match*/**" | \
+                       uniq -u
                fi
                return
        fi
-       case "$cur" in
+       case "$cur_" in
        refs|refs/*)
-               __git ls-remote "$remote" "$cur*" | \
+               __git ls-remote "$remote" "$match*" | \
                while read -r hash i; do
                        case "$i" in
                        *^{}) ;;
-                       *) echo "$i" ;;
+                       *) echo "$pfx$i$sfx" ;;
                        esac
                done
                ;;
        *)
                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 "${pfx}HEAD$sfx" ;;
+                       esac
+                       __git for-each-ref --format="$fer_pfx%(refname:strip=3)$sfx" \
+                               "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
                                *^{})   ;;
-                               refs/*) echo "${i#refs/*/}" ;;
-                               *)      echo "$i" ;;  # symbolic refs
+                               refs/*) echo "$pfx${i#refs/*/}$sfx" ;;
+                               *)      echo "$pfx$i$sfx" ;;  # symbolic refs
                                esac
                        done
                fi
        esac
  }
  
+ # Completes refs, short and long, local and remote, symbolic and pseudo.
+ #
+ # Usage: __git_complete_refs [<option>]...
+ # --remote=<remote>: The remote to list refs from, can be the name of a
+ #                    configured remote, a path, or a URL.
+ # --track: List unique remote branches for 'git checkout's tracking DWIMery.
+ # --pfx=<prefix>: A prefix to be added to each ref.
+ # --cur=<word>: The current ref to be completed.  Defaults to the current
+ #               word to be completed.
+ # --sfx=<suffix>: A suffix to be appended to each ref instead of the default
+ #                 space.
+ __git_complete_refs ()
+ {
+       local remote track pfx cur_="$cur" sfx=" "
+       while test $# != 0; do
+               case "$1" in
+               --remote=*)     remote="${1##--remote=}" ;;
+               --track)        track="yes" ;;
+               --pfx=*)        pfx="${1##--pfx=}" ;;
+               --cur=*)        cur_="${1##--cur=}" ;;
+               --sfx=*)        sfx="${1##--sfx=}" ;;
+               *)              return 1 ;;
+               esac
+               shift
+       done
+       __gitcomp_direct "$(__git_refs "$remote" "$track" "$pfx" "$cur_" "$sfx")"
+ }
  # __git_refs2 requires 1 argument (to pass to __git_refs)
+ # Deprecated: use __git_complete_fetch_refspecs() instead.
  __git_refs2 ()
  {
        local i
        done
  }
  
+ # Completes refspecs for fetching from a remote repository.
+ # 1: The remote repository.
+ # 2: A prefix to be added to each listed refspec (optional).
+ # 3: The ref to be completed as a refspec instead of the current word to be
+ #    completed (optional)
+ # 4: A suffix to be appended to each listed refspec instead of the default
+ #    space (optional).
+ __git_complete_fetch_refspecs ()
+ {
+       local i remote="$1" pfx="${2-}" cur_="${3-$cur}" sfx="${4- }"
+       __gitcomp_direct "$(
+               for i in $(__git_refs "$remote" "" "" "$cur_") ; do
+                       echo "$pfx$i:$i$sfx"
+               done
+               )"
+ }
  # __git_refs_remotes requires 1 argument (to pass to ls-remote)
  __git_refs_remotes ()
  {
@@@ -554,15 -655,15 +655,15 @@@ __git_complete_revlist_file (
        *...*)
                pfx="${cur_%...*}..."
                cur_="${cur_#*...}"
-               __gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
+               __git_complete_refs --pfx="$pfx" --cur="$cur_"
                ;;
        *..*)
                pfx="${cur_%..*}.."
                cur_="${cur_#*..}"
-               __gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
+               __git_complete_refs --pfx="$pfx" --cur="$cur_"
                ;;
        *)
-               __gitcomp_nl "$(__git_refs)"
+               __git_complete_refs
                ;;
        esac
  }
@@@ -647,23 -748,23 +748,23 @@@ __git_complete_remote_or_refspec (
        case "$cmd" in
        fetch)
                if [ $lhs = 1 ]; then
-                       __gitcomp_nl "$(__git_refs2 "$remote")" "$pfx" "$cur_"
+                       __git_complete_fetch_refspecs "$remote" "$pfx" "$cur_"
                else
-                       __gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
+                       __git_complete_refs --pfx="$pfx" --cur="$cur_"
                fi
                ;;
        pull|remote)
                if [ $lhs = 1 ]; then
-                       __gitcomp_nl "$(__git_refs "$remote")" "$pfx" "$cur_"
+                       __git_complete_refs --remote="$remote" --pfx="$pfx" --cur="$cur_"
                else
-                       __gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
+                       __git_complete_refs --pfx="$pfx" --cur="$cur_"
                fi
                ;;
        push)
                if [ $lhs = 1 ]; then
-                       __gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
+                       __git_complete_refs --pfx="$pfx" --cur="$cur_"
                else
-                       __gitcomp_nl "$(__git_refs "$remote")" "$pfx" "$cur_"
+                       __git_complete_refs --remote="$remote" --pfx="$pfx" --cur="$cur_"
                fi
                ;;
        esac
@@@ -1001,7 -1102,6 +1102,7 @@@ _git_apply (
                        --apply --no-add --exclude=
                        --ignore-whitespace --ignore-space-change
                        --whitespace= --inaccurate-eof --verbose
 +                      --recount --directory=
                        "
                return
        esac
@@@ -1013,17 -1113,13 +1114,17 @@@ _git_add (
        --*)
                __gitcomp "
                        --interactive --refresh --patch --update --dry-run
 -                      --ignore-errors --intent-to-add
 +                      --ignore-errors --intent-to-add --force --edit --chmod=
                        "
                return
        esac
  
 -      # XXX should we check for --update and --all options ?
 -      __git_complete_index_file "--others --modified --directory --no-empty-directory"
 +      local complete_opt="--others --modified --directory --no-empty-directory"
 +      if test -n "$(__git_find_on_cmdline "-u --update")"
 +      then
 +              complete_opt="--modified"
 +      fi
 +      __git_complete_index_file "$complete_opt"
  }
  
  _git_archive ()
        --*)
                __gitcomp "
                        --format= --list --verbose
 -                      --prefix= --remote= --exec=
 +                      --prefix= --remote= --exec= --output
                        "
                return
                ;;
@@@ -1066,7 -1162,7 +1167,7 @@@ _git_bisect (
  
        case "$subcommand" in
        bad|good|reset|skip|start)
-               __gitcomp_nl "$(__git_refs)"
+               __git_complete_refs
                ;;
        *)
                ;;
@@@ -1088,7 -1184,7 +1189,7 @@@ _git_branch (
  
        case "$cur" in
        --set-upstream-to=*)
-               __gitcomp_nl "$(__git_refs)" "" "${cur##--set-upstream-to=}"
+               __git_complete_refs --cur="${cur##--set-upstream-to=}"
                ;;
        --*)
                __gitcomp "
                        --track --no-track --contains --merged --no-merged
                        --set-upstream-to= --edit-description --list
                        --unset-upstream --delete --move --remotes
 +                      --column --no-column --sort= --points-at
                        "
                ;;
        *)
                if [ $only_local_ref = "y" -a $has_r = "n" ]; then
-                       __gitcomp_nl "$(__git_heads)"
+                       __gitcomp_direct "$(__git_heads "" "$cur" " ")"
                else
-                       __gitcomp_nl "$(__git_refs)"
+                       __git_complete_refs
                fi
                ;;
        esac
@@@ -1146,18 -1241,18 +1247,18 @@@ _git_checkout (
        *)
                # check if --track, --no-track, or --no-guess was specified
                # if so, disable DWIM mode
-               local flags="--track --no-track --no-guess" track=1
+               local flags="--track --no-track --no-guess" track_opt="--track"
                if [ -n "$(__git_find_on_cmdline "$flags")" ]; then
-                       track=''
+                       track_opt=''
                fi
-               __gitcomp_nl "$(__git_refs '' $track)"
+               __git_complete_refs $track_opt
                ;;
        esac
  }
  
  _git_cherry ()
  {
-       __gitcomp_nl "$(__git_refs)"
+       __git_complete_refs
  }
  
  _git_cherry_pick ()
                __gitcomp "--edit --no-commit --signoff --strategy= --mainline"
                ;;
        *)
-               __gitcomp_nl "$(__git_refs)"
+               __git_complete_refs
                ;;
        esac
  }
@@@ -1210,8 -1305,6 +1311,8 @@@ _git_clone (
                        --single-branch
                        --branch
                        --recurse-submodules
 +                      --no-single-branch
 +                      --shallow-submodules
                        "
                return
                ;;
@@@ -1224,7 -1317,7 +1325,7 @@@ _git_commit (
  {
        case "$prev" in
        -c|-C)
-               __gitcomp_nl "$(__git_refs)" "" "${cur}"
+               __git_complete_refs
                return
                ;;
        esac
                ;;
        --reuse-message=*|--reedit-message=*|\
        --fixup=*|--squash=*)
-               __gitcomp_nl "$(__git_refs)" "" "${cur#*=}"
+               __git_complete_refs --cur="${cur#*=}"
                return
                ;;
        --untracked-files=*)
                        --reset-author --file= --message= --template=
                        --cleanup= --untracked-files --untracked-files=
                        --verbose --quiet --fixup= --squash=
 +                      --patch --short --date --allow-empty
                        "
                return
        esac
@@@ -1272,12 -1364,11 +1373,12 @@@ _git_describe (
        --*)
                __gitcomp "
                        --all --tags --contains --abbrev= --candidates=
 -                      --exact-match --debug --long --match --always
 +                      --exact-match --debug --long --match --always --first-parent
 +                      --exclude
                        "
                return
        esac
-       __gitcomp_nl "$(__git_refs)"
+       __git_complete_refs
  }
  
  __git_diff_algorithms="myers minimal patience histogram"
@@@ -1356,7 -1447,6 +1457,7 @@@ __git_fetch_recurse_submodules="yes on-
  __git_fetch_options="
        --quiet --verbose --append --upload-pack --force --keep --depth=
        --tags --no-tags --all --prune --dry-run --recurse-submodules=
 +      --unshallow --update-shallow
  "
  
  _git_fetch ()
@@@ -1406,7 -1496,7 +1507,7 @@@ _git_fsck (
        --*)
                __gitcomp "
                        --tags --root --unreachable --cache --no-reflogs --full
 -                      --strict --verbose --lost-found
 +                      --strict --verbose --lost-found --name-objects
                        "
                return
                ;;
@@@ -1450,8 -1540,6 +1551,8 @@@ _git_grep (
                        --max-depth
                        --count
                        --and --or --not --all-match
 +                      --break --heading --show-function --function-context
 +                      --untracked --no-index
                        "
                return
                ;;
                ;;
        esac
  
-       __gitcomp_nl "$(__git_refs)"
+       __git_complete_refs
  }
  
  _git_help ()
@@@ -1524,12 -1612,6 +1625,12 @@@ _git_ls_files (
  
  _git_ls_remote ()
  {
 +      case "$cur" in
 +      --*)
 +              __gitcomp "--heads --tags --refs --get-url --symref"
 +              return
 +              ;;
 +      esac
        __gitcomp_nl "$(__git_remotes)"
  }
  
@@@ -1640,7 -1722,7 +1741,7 @@@ _git_merge (
                        --rerere-autoupdate --no-rerere-autoupdate --abort --continue"
                return
        esac
-       __gitcomp_nl "$(__git_refs)"
+       __git_complete_refs
  }
  
  _git_mergetool ()
                return
                ;;
        --*)
 -              __gitcomp "--tool="
 +              __gitcomp "--tool= --prompt --no-prompt"
                return
                ;;
        esac
@@@ -1665,7 -1747,7 +1766,7 @@@ _git_merge_base (
                return
                ;;
        esac
-       __gitcomp_nl "$(__git_refs)"
+       __git_complete_refs
  }
  
  _git_mv ()
@@@ -1703,7 -1785,7 +1804,7 @@@ _git_notes (
        ,*)
                case "$prev" in
                --ref)
-                       __gitcomp_nl "$(__git_refs)"
+                       __git_complete_refs
                        ;;
                *)
                        __gitcomp "$subcommands --ref"
                ;;
        add,--reuse-message=*|append,--reuse-message=*|\
        add,--reedit-message=*|append,--reedit-message=*)
-               __gitcomp_nl "$(__git_refs)" "" "${cur#*=}"
+               __git_complete_refs --cur="${cur#*=}"
                ;;
        add,--*|append,--*)
                __gitcomp '--file= --message= --reedit-message=
                -m|-F)
                        ;;
                *)
-                       __gitcomp_nl "$(__git_refs)"
+                       __git_complete_refs
                        ;;
                esac
                ;;
@@@ -1759,7 -1841,7 +1860,7 @@@ _git_pull (
        __git_complete_remote_or_refspec
  }
  
 -__git_push_recurse_submodules="check on-demand"
 +__git_push_recurse_submodules="check on-demand only"
  
  __git_complete_force_with_lease ()
  {
        --*=)
                ;;
        *:*)
-               __gitcomp_nl "$(__git_refs)" "" "${cur_#*:}"
+               __git_complete_refs --cur="${cur_#*:}"
                ;;
        *)
-               __gitcomp_nl "$(__git_refs)" "" "$cur_"
+               __git_complete_refs --cur="$cur_"
                ;;
        esac
  }
@@@ -1848,7 -1930,7 +1949,7 @@@ _git_rebase (
  
                return
        esac
-       __gitcomp_nl "$(__git_refs)"
+       __git_complete_refs
  }
  
  _git_reflog ()
        if [ -z "$subcommand" ]; then
                __gitcomp "$subcommands"
        else
-               __gitcomp_nl "$(__git_refs)"
+               __git_complete_refs
        fi
  }
  
@@@ -2005,7 -2087,7 +2106,7 @@@ _git_config (
                return
                ;;
        branch.*.merge)
-               __gitcomp_nl "$(__git_refs)"
+               __git_complete_refs
                return
                ;;
        branch.*.rebase)
                ;;
        branch.*)
                local pfx="${cur%.*}." cur_="${cur#*.}"
-               __gitcomp_nl "$(__git_heads)" "$pfx" "$cur_" "."
+               __gitcomp_direct "$(__git_heads "$pfx" "$cur_" ".")"
                __gitcomp_nl_append $'autosetupmerge\nautosetuprebase\n' "$pfx" "$cur_"
                return
                ;;
  
  _git_remote ()
  {
 -      local subcommands="add rename remove set-head set-branches set-url show prune update"
 +      local subcommands="
 +              add rename remove set-head set-branches
 +              get-url set-url show prune update
 +              "
        local subcommand="$(__git_find_on_cmdline "$subcommands")"
        if [ -z "$subcommand" ]; then
 -              __gitcomp "$subcommands"
 +              case "$cur" in
 +              --*)
 +                      __gitcomp "--verbose"
 +                      ;;
 +              *)
 +                      __gitcomp "$subcommands"
 +                      ;;
 +              esac
                return
        fi
  
 -      case "$subcommand" in
 -      rename|remove|set-url|show|prune)
 -              __gitcomp_nl "$(__git_remotes)"
 +      case "$subcommand,$cur" in
 +      add,--*)
 +              __gitcomp "--track --master --fetch --tags --no-tags --mirror="
 +              ;;
 +      add,*)
 +              ;;
 +      set-head,--*)
 +              __gitcomp "--auto --delete"
 +              ;;
 +      set-branches,--*)
 +              __gitcomp "--add"
                ;;
 -      set-head|set-branches)
 +      set-head,*|set-branches,*)
                __git_complete_remote_or_refspec
                ;;
 -      update)
 +      update,--*)
 +              __gitcomp "--prune"
 +              ;;
 +      update,*)
                __gitcomp "$(__git_get_config_variables "remotes")"
                ;;
 +      set-url,--*)
 +              __gitcomp "--push --add --delete"
 +              ;;
 +      get-url,--*)
 +              __gitcomp "--push --all"
 +              ;;
 +      prune,--*)
 +              __gitcomp "--dry-run"
 +              ;;
        *)
 +              __gitcomp_nl "$(__git_remotes)"
                ;;
        esac
  }
  
  _git_replace ()
  {
-       __gitcomp_nl "$(__git_refs)"
 +      case "$cur" in
 +      --*)
 +              __gitcomp "--edit --graft --format= --list --delete"
 +              return
 +              ;;
 +      esac
+       __git_complete_refs
  }
  
 +_git_rerere ()
 +{
 +      local subcommands="clear forget diff remaining status gc"
 +      local subcommand="$(__git_find_on_cmdline "$subcommands")"
 +      if test -z "$subcommand"
 +      then
 +              __gitcomp "$subcommands"
 +              return
 +      fi
 +}
 +
  _git_reset ()
  {
        __git_has_doubledash && return
  
        case "$cur" in
        --*)
 -              __gitcomp "--merge --mixed --hard --soft --patch"
 +              __gitcomp "--merge --mixed --hard --soft --patch --keep"
                return
                ;;
        esac
-       __gitcomp_nl "$(__git_refs)"
+       __git_complete_refs
  }
  
  _git_revert ()
        fi
        case "$cur" in
        --*)
 -              __gitcomp "--edit --mainline --no-edit --no-commit --signoff"
 +              __gitcomp "
 +                      --edit --mainline --no-edit --no-commit --signoff
 +                      --strategy= --strategy-option=
 +                      "
                return
                ;;
        esac
-       __gitcomp_nl "$(__git_refs)"
+       __git_complete_refs
  }
  
  _git_rm ()
@@@ -2583,7 -2614,7 +2684,7 @@@ _git_shortlog (
                __gitcomp "
                        $__git_log_common_options
                        $__git_log_shortlog_options
 -                      --numbered --summary
 +                      --numbered --summary --email
                        "
                return
                ;;
@@@ -2667,7 -2698,7 +2768,7 @@@ _git_stash (
                        ;;
                branch,*)
                        if [ $cword -eq 3 ]; then
-                               __gitcomp_nl "$(__git_refs)";
+                               __git_complete_refs
                        else
                                __gitcomp_nl "$(__git stash list \
                                                | sed -n -e 's/:.*//p')"
@@@ -2688,11 -2719,10 +2789,11 @@@ _git_submodule (
        __git_has_doubledash && return
  
        local subcommands="add status init deinit update summary foreach sync"
 -      if [ -z "$(__git_find_on_cmdline "$subcommands")" ]; then
 +      local subcommand="$(__git_find_on_cmdline "$subcommands")"
 +      if [ -z "$subcommand" ]; then
                case "$cur" in
                --*)
 -                      __gitcomp "--quiet --cached"
 +                      __gitcomp "--quiet"
                        ;;
                *)
                        __gitcomp "$subcommands"
                esac
                return
        fi
 +
 +      case "$subcommand,$cur" in
 +      add,--*)
 +              __gitcomp "--branch --force --name --reference --depth"
 +              ;;
 +      status,--*)
 +              __gitcomp "--cached --recursive"
 +              ;;
 +      deinit,--*)
 +              __gitcomp "--force --all"
 +              ;;
 +      update,--*)
 +              __gitcomp "
 +                      --init --remote --no-fetch
 +                      --recommend-shallow --no-recommend-shallow
 +                      --force --rebase --merge --reference --depth --recursive --jobs
 +              "
 +              ;;
 +      summary,--*)
 +              __gitcomp "--cached --files --summary-limit"
 +              ;;
 +      foreach,--*|sync,--*)
 +              __gitcomp "--recursive"
 +              ;;
 +      *)
 +              ;;
 +      esac
  }
  
  _git_svn ()
                        --no-metadata --use-svm-props --use-svnsync-props
                        --log-window-size= --no-checkout --quiet
                        --repack-flags --use-log-author --localtime
 +                      --add-author-from
                        --ignore-paths= --include-paths= $remote_opts
                        "
                local init_opts="
                        --template= --shared= --trunk= --tags=
                        --branches= --stdlayout --minimize-url
                        --no-metadata --use-svm-props --use-svnsync-props
 -                      --rewrite-root= --prefix= --use-log-author
 -                      --add-author-from $remote_opts
 +                      --rewrite-root= --prefix= $remote_opts
                        "
                local cmt_opts="
                        --edit --rmdir --find-copies-harder --copy-similarity=
@@@ -2834,7 -2837,7 +2935,7 @@@ _git_tag (
                i="${words[c]}"
                case "$i" in
                -d|-v)
-                       __gitcomp_nl "$(__git_tags)"
+                       __gitcomp_direct "$(__git_tags "" "$cur" " ")"
                        return
                        ;;
                -f)
                ;;
        -*|tag)
                if [ $f = 1 ]; then
-                       __gitcomp_nl "$(__git_tags)"
+                       __gitcomp_direct "$(__git_tags "" "$cur" " ")"
                fi
                ;;
        *)
-               __gitcomp_nl "$(__git_refs)"
+               __git_complete_refs
                ;;
        esac
  
        --*)
                __gitcomp "
                        --list --delete --verify --annotate --message --file
 -                      --sign --cleanup --local-user --force --column --sort
 -                      --contains --points-at
 +                      --sign --cleanup --local-user --force --column --sort=
 +                      --contains --points-at --merged --no-merged --create-reflog
                        "
                ;;
        esac
@@@ -3024,6 -3027,15 +3125,15 @@@ if [[ -n ${ZSH_VERSION-} ]]; the
                esac
        }
  
+       __gitcomp_direct ()
+       {
+               emulate -L zsh
+               local IFS=$'\n'
+               compset -P '*[=:]'
+               compadd -Q -- ${=1} && _ret=0
+       }
        __gitcomp_nl ()
        {
                emulate -L zsh