Merge branch 'nd/completion-negation'
authorJunio C Hamano <gitster@pobox.com>
Thu, 28 Jun 2018 19:53:32 +0000 (12:53 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 28 Jun 2018 19:53:32 +0000 (12:53 -0700)
Continuing with the idea to programmatically enumerate various
pieces of data required for command line completion, the codebase
has been taught to enumerate options prefixed with "--no-" to
negate them.

* nd/completion-negation:
completion: collapse extra --no-.. options
completion: suppress some -no- options
parse-options: option to let --git-completion-helper show negative form

1  2 
builtin/checkout.c
contrib/completion/git-completion.bash
t/t9902-completion.sh
diff --combined builtin/checkout.c
index 2e1d2376d24043a75adb9bbc69724591bcc2d5c3,c7670dbbfec9172496f8ed73d73412bed326b4b6..4c41d8b4c831937354fca62f454b83e311f79145
@@@ -527,7 -527,6 +527,7 @@@ static int merge_working_tree(const str
                init_tree_desc(&trees[1], tree->buffer, tree->size);
  
                ret = unpack_trees(2, trees, &topts);
 +              clear_unpack_trees_porcelain(&topts);
                if (ret == -1) {
                        /*
                         * Unpack couldn't do a trivial merge; either
@@@ -1120,10 -1119,12 +1120,12 @@@ int cmd_checkout(int argc, const char *
                OPT_SET_INT('t', "track",  &opts.track, N_("set upstream info for new branch"),
                        BRANCH_TRACK_EXPLICIT),
                OPT_STRING(0, "orphan", &opts.new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
-               OPT_SET_INT('2', "ours", &opts.writeout_stage, N_("checkout our version for unmerged files"),
-                           2),
-               OPT_SET_INT('3', "theirs", &opts.writeout_stage, N_("checkout their version for unmerged files"),
-                           3),
+               OPT_SET_INT_F('2', "ours", &opts.writeout_stage,
+                             N_("checkout our version for unmerged files"),
+                             2, PARSE_OPT_NONEG),
+               OPT_SET_INT_F('3', "theirs", &opts.writeout_stage,
+                             N_("checkout their version for unmerged files"),
+                             3, PARSE_OPT_NONEG),
                OPT__FORCE(&opts.force, N_("force checkout (throw away local modifications)"),
                           PARSE_OPT_NOCOMPLETE),
                OPT_BOOL('m', "merge", &opts.merge, N_("perform a 3-way merge with the new branch")),
index bc31e8973b25a8609fd9a6cb5eb4279de3eb332d,425d06256f8da9e59952253ea7f48cd6efddddca..94c95516ebec7f32787cc5a2fb45cae83c7e28e3
@@@ -94,70 -94,6 +94,70 @@@ __git (
                ${__git_dir:+--git-dir="$__git_dir"} "$@" 2>/dev/null
  }
  
 +# Removes backslash escaping, single quotes and double quotes from a word,
 +# stores the result in the variable $dequoted_word.
 +# 1: The word to dequote.
 +__git_dequote ()
 +{
 +      local rest="$1" len ch
 +
 +      dequoted_word=""
 +
 +      while test -n "$rest"; do
 +              len=${#dequoted_word}
 +              dequoted_word="$dequoted_word${rest%%[\\\'\"]*}"
 +              rest="${rest:$((${#dequoted_word}-$len))}"
 +
 +              case "${rest:0:1}" in
 +              \\)
 +                      ch="${rest:1:1}"
 +                      case "$ch" in
 +                      $'\n')
 +                              ;;
 +                      *)
 +                              dequoted_word="$dequoted_word$ch"
 +                              ;;
 +                      esac
 +                      rest="${rest:2}"
 +                      ;;
 +              \')
 +                      rest="${rest:1}"
 +                      len=${#dequoted_word}
 +                      dequoted_word="$dequoted_word${rest%%\'*}"
 +                      rest="${rest:$((${#dequoted_word}-$len+1))}"
 +                      ;;
 +              \")
 +                      rest="${rest:1}"
 +                      while test -n "$rest" ; do
 +                              len=${#dequoted_word}
 +                              dequoted_word="$dequoted_word${rest%%[\\\"]*}"
 +                              rest="${rest:$((${#dequoted_word}-$len))}"
 +                              case "${rest:0:1}" in
 +                              \\)
 +                                      ch="${rest:1:1}"
 +                                      case "$ch" in
 +                                      \"|\\|\$|\`)
 +                                              dequoted_word="$dequoted_word$ch"
 +                                              ;;
 +                                      $'\n')
 +                                              ;;
 +                                      *)
 +                                              dequoted_word="$dequoted_word\\$ch"
 +                                              ;;
 +                                      esac
 +                                      rest="${rest:2}"
 +                                      ;;
 +                              \")
 +                                      rest="${rest:1}"
 +                                      break
 +                                      ;;
 +                              esac
 +                      done
 +                      ;;
 +              esac
 +      done
 +}
 +
  # The following function is based on code from:
  #
  #   bash_completion - programmable completion functions for bash 3.2+
@@@ -330,9 -266,32 +330,32 @@@ __gitcomp (
        case "$cur_" in
        --*=)
                ;;
+       --no-*)
+               local c i=0 IFS=$' \t\n'
+               for c in $1; do
+                       if [[ $c == "--" ]]; then
+                               continue
+                       fi
+                       c="$c${4-}"
+                       if [[ $c == "$cur_"* ]]; then
+                               case $c in
+                               --*=*|*.) ;;
+                               *) c="$c " ;;
+                               esac
+                               COMPREPLY[i++]="${2-}$c"
+                       fi
+               done
+               ;;
        *)
                local c i=0 IFS=$' \t\n'
                for c in $1; do
+                       if [[ $c == "--" ]]; then
+                               c="--no-...${4-}"
+                               if [[ $c == "$cur_"* ]]; then
+                                       COMPREPLY[i++]="${2-}$c "
+                               fi
+                               break
+                       fi
                        c="$c${4-}"
                        if [[ $c == "$cur_"* ]]; then
                                case $c in
@@@ -410,24 -369,6 +433,24 @@@ __gitcomp_nl (
        __gitcomp_nl_append "$@"
  }
  
 +# Fills the COMPREPLY array with prefiltered paths without any additional
 +# processing.
 +# Callers must take care of providing only paths that match the current path
 +# to be completed and adding any prefix path components, if necessary.
 +# 1: List of newline-separated matching paths, complete with all prefix
 +#    path componens.
 +__gitcomp_file_direct ()
 +{
 +      local IFS=$'\n'
 +
 +      COMPREPLY=($1)
 +
 +      # use a hack to enable file mode in bash < 4
 +      compopt -o filenames +o nospace 2>/dev/null ||
 +      compgen -f /non-existing-dir/ >/dev/null ||
 +      true
 +}
 +
  # Generates completion reply with compgen from newline-separated possible
  # completion filenames.
  # It accepts 1 to 3 arguments:
@@@ -447,8 -388,7 +470,8 @@@ __gitcomp_file (
  
        # use a hack to enable file mode in bash < 4
        compopt -o filenames +o nospace 2>/dev/null ||
 -      compgen -f /non-existing-dir/ > /dev/null
 +      compgen -f /non-existing-dir/ >/dev/null ||
 +      true
  }
  
  # Execute 'git ls-files', unless the --committable option is specified, in
  __git_ls_files_helper ()
  {
        if [ "$2" == "--committable" ]; then
 -              __git -C "$1" diff-index --name-only --relative HEAD
 +              __git -C "$1" -c core.quotePath=false diff-index \
 +                      --name-only --relative HEAD -- "${3//\\/\\\\}*"
        else
                # NOTE: $2 is not quoted in order to support multiple options
 -              __git -C "$1" ls-files --exclude-standard $2
 +              __git -C "$1" -c core.quotePath=false ls-files \
 +                      --exclude-standard $2 -- "${3//\\/\\\\}*"
        fi
  }
  
  #    If provided, only files within the specified directory are listed.
  #    Sub directories are never recursed.  Path must have a trailing
  #    slash.
 +# 3: List only paths matching this path component (optional).
  __git_index_files ()
  {
 -      local root="${2-.}" file
 +      local root="$2" match="$3"
  
 -      __git_ls_files_helper "$root" "$1" |
 -      cut -f1 -d/ | sort | uniq
 +      __git_ls_files_helper "$root" "$1" "$match" |
 +      awk -F / -v pfx="${2//\\/\\\\}" '{
 +              paths[$1] = 1
 +      }
 +      END {
 +              for (p in paths) {
 +                      if (substr(p, 1, 1) != "\"") {
 +                              # No special characters, easy!
 +                              print pfx p
 +                              continue
 +                      }
 +
 +                      # The path is quoted.
 +                      p = dequote(p)
 +                      if (p == "")
 +                              continue
 +
 +                      # Even when a directory name itself does not contain
 +                      # any special characters, it will still be quoted if
 +                      # any of its (stripped) trailing path components do.
 +                      # Because of this we may have seen the same direcory
 +                      # both quoted and unquoted.
 +                      if (p in paths)
 +                              # We have seen the same directory unquoted,
 +                              # skip it.
 +                              continue
 +                      else
 +                              print pfx p
 +              }
 +      }
 +      function dequote(p,    bs_idx, out, esc, esc_idx, dec) {
 +              # Skip opening double quote.
 +              p = substr(p, 2)
 +
 +              # Interpret backslash escape sequences.
 +              while ((bs_idx = index(p, "\\")) != 0) {
 +                      out = out substr(p, 1, bs_idx - 1)
 +                      esc = substr(p, bs_idx + 1, 1)
 +                      p = substr(p, bs_idx + 2)
 +
 +                      if ((esc_idx = index("abtvfr\"\\", esc)) != 0) {
 +                              # C-style one-character escape sequence.
 +                              out = out substr("\a\b\t\v\f\r\"\\",
 +                                               esc_idx, 1)
 +                      } else if (esc == "n") {
 +                              # Uh-oh, a newline character.
 +                              # We cant reliably put a pathname
 +                              # containing a newline into COMPREPLY,
 +                              # and the newline would create a mess.
 +                              # Skip this path.
 +                              return ""
 +                      } else {
 +                              # Must be a \nnn octal value, then.
 +                              dec = esc             * 64 + \
 +                                    substr(p, 1, 1) * 8  + \
 +                                    substr(p, 2, 1)
 +                              out = out sprintf("%c", dec)
 +                              p = substr(p, 3)
 +                      }
 +              }
 +              # Drop closing double quote, if there is one.
 +              # (There isnt any if this is a directory, as it was
 +              # already stripped with the trailing path components.)
 +              if (substr(p, length(p), 1) == "\"")
 +                      out = out substr(p, 1, length(p) - 1)
 +              else
 +                      out = out p
 +
 +              return out
 +      }'
 +}
 +
 +# __git_complete_index_file requires 1 argument:
 +# 1: the options to pass to ls-file
 +#
 +# The exception is --committable, which finds the files appropriate commit.
 +__git_complete_index_file ()
 +{
 +      local dequoted_word pfx="" cur_
 +
 +      __git_dequote "$cur"
 +
 +      case "$dequoted_word" in
 +      ?*/*)
 +              pfx="${dequoted_word%/*}/"
 +              cur_="${dequoted_word##*/}"
 +              ;;
 +      *)
 +              cur_="$dequoted_word"
 +      esac
 +
 +      __gitcomp_file_direct "$(__git_index_files "$1" "$pfx" "$cur_")"
  }
  
  # Lists branches from the local repository.
@@@ -889,6 -736,26 +912,6 @@@ __git_complete_revlist_file (
        esac
  }
  
 -
 -# __git_complete_index_file requires 1 argument:
 -# 1: the options to pass to ls-file
 -#
 -# The exception is --committable, which finds the files appropriate commit.
 -__git_complete_index_file ()
 -{
 -      local pfx="" cur_="$cur"
 -
 -      case "$cur_" in
 -      ?*/*)
 -              pfx="${cur_%/*}"
 -              cur_="${cur_##*/}"
 -              pfx="${pfx}/"
 -              ;;
 -      esac
 -
 -      __gitcomp_file "$(__git_index_files "$1" ${pfx:+"$pfx"})" "$pfx" "$cur_"
 -}
 -
  __git_complete_file ()
  {
        __git_complete_revlist_file
@@@ -989,11 -856,127 +1012,11 @@@ __git_complete_strategy (
        return 1
  }
  
 -__git_commands () {
 -      if test -n "${GIT_TESTING_COMMAND_COMPLETION:-}"
 -      then
 -              printf "%s" "${GIT_TESTING_COMMAND_COMPLETION}"
 -      else
 -              git help -a|egrep '^  [a-zA-Z0-9]'
 -      fi
 -}
 -
 -__git_list_all_commands ()
 -{
 -      local i IFS=" "$'\n'
 -      for i in $(__git_commands)
 -      do
 -              case $i in
 -              *--*)             : helper pattern;;
 -              *) echo $i;;
 -              esac
 -      done
 -}
 -
  __git_all_commands=
  __git_compute_all_commands ()
  {
        test -n "$__git_all_commands" ||
 -      __git_all_commands=$(__git_list_all_commands)
 -}
 -
 -__git_list_porcelain_commands ()
 -{
 -      local i IFS=" "$'\n'
 -      __git_compute_all_commands
 -      for i in $__git_all_commands
 -      do
 -              case $i in
 -              *--*)             : helper pattern;;
 -              applymbox)        : ask gittus;;
 -              applypatch)       : ask gittus;;
 -              archimport)       : import;;
 -              cat-file)         : plumbing;;
 -              check-attr)       : plumbing;;
 -              check-ignore)     : plumbing;;
 -              check-mailmap)    : plumbing;;
 -              check-ref-format) : plumbing;;
 -              checkout-index)   : plumbing;;
 -              column)           : internal helper;;
 -              commit-graph)     : plumbing;;
 -              commit-tree)      : plumbing;;
 -              count-objects)    : infrequent;;
 -              credential)       : credentials;;
 -              credential-*)     : credentials helper;;
 -              cvsexportcommit)  : export;;
 -              cvsimport)        : import;;
 -              cvsserver)        : daemon;;
 -              daemon)           : daemon;;
 -              diff-files)       : plumbing;;
 -              diff-index)       : plumbing;;
 -              diff-tree)        : plumbing;;
 -              fast-import)      : import;;
 -              fast-export)      : export;;
 -              fsck-objects)     : plumbing;;
 -              fetch-pack)       : plumbing;;
 -              fmt-merge-msg)    : plumbing;;
 -              for-each-ref)     : plumbing;;
 -              hash-object)      : plumbing;;
 -              http-*)           : transport;;
 -              index-pack)       : plumbing;;
 -              init-db)          : deprecated;;
 -              local-fetch)      : plumbing;;
 -              ls-files)         : plumbing;;
 -              ls-remote)        : plumbing;;
 -              ls-tree)          : plumbing;;
 -              mailinfo)         : plumbing;;
 -              mailsplit)        : plumbing;;
 -              merge-*)          : plumbing;;
 -              mktree)           : plumbing;;
 -              mktag)            : plumbing;;
 -              pack-objects)     : plumbing;;
 -              pack-redundant)   : plumbing;;
 -              pack-refs)        : plumbing;;
 -              parse-remote)     : plumbing;;
 -              patch-id)         : plumbing;;
 -              prune)            : plumbing;;
 -              prune-packed)     : plumbing;;
 -              quiltimport)      : import;;
 -              read-tree)        : plumbing;;
 -              receive-pack)     : plumbing;;
 -              remote-*)         : transport;;
 -              rerere)           : plumbing;;
 -              rev-list)         : plumbing;;
 -              rev-parse)        : plumbing;;
 -              runstatus)        : plumbing;;
 -              sh-setup)         : internal;;
 -              shell)            : daemon;;
 -              show-ref)         : plumbing;;
 -              send-pack)        : plumbing;;
 -              show-index)       : plumbing;;
 -              ssh-*)            : transport;;
 -              stripspace)       : plumbing;;
 -              symbolic-ref)     : plumbing;;
 -              unpack-file)      : plumbing;;
 -              unpack-objects)   : plumbing;;
 -              update-index)     : plumbing;;
 -              update-ref)       : plumbing;;
 -              update-server-info) : daemon;;
 -              upload-archive)   : plumbing;;
 -              upload-pack)      : plumbing;;
 -              write-tree)       : plumbing;;
 -              var)              : infrequent;;
 -              verify-pack)      : infrequent;;
 -              verify-tag)       : plumbing;;
 -              *) echo $i;;
 -              esac
 -      done
 -}
 -
 -__git_porcelain_commands=
 -__git_compute_porcelain_commands ()
 -{
 -      test -n "$__git_porcelain_commands" ||
 -      __git_porcelain_commands=$(__git_list_porcelain_commands)
 +      __git_all_commands=$(git --list-cmds=main,others,alias,nohelpers)
  }
  
  # Lists all set config variables starting with the given section prefix,
@@@ -1011,6 -994,11 +1034,6 @@@ __git_pretty_aliases (
        __git_get_config_variables "pretty"
  }
  
 -__git_aliases ()
 -{
 -      __git_get_config_variables "alias"
 -}
 -
  # __git_aliased_command requires 1 argument
  __git_aliased_command ()
  {
@@@ -1161,7 -1149,7 +1184,7 @@@ _git_am (
                return
                ;;
        --*)
-               __gitcomp_builtin am "--no-utf8" \
+               __gitcomp_builtin am "" \
                        "$__git_am_inprogress_options"
                return
        esac
@@@ -1261,9 -1249,7 +1284,7 @@@ _git_branch (
                __git_complete_refs --cur="${cur##--set-upstream-to=}"
                ;;
        --*)
-               __gitcomp_builtin branch "--no-color --no-abbrev
-                       --no-track --no-column
-                       "
+               __gitcomp_builtin branch
                ;;
        *)
                if [ $only_local_ref = "y" -a $has_r = "n" ]; then
@@@ -1304,7 -1290,7 +1325,7 @@@ _git_checkout (
                __gitcomp "diff3 merge" "" "${cur##--conflict=}"
                ;;
        --*)
-               __gitcomp_builtin checkout "--no-track --no-recurse-submodules"
+               __gitcomp_builtin checkout
                ;;
        *)
                # check if --track, --no-track, or --no-guess was specified
@@@ -1367,7 -1353,7 +1388,7 @@@ _git_clone (
  {
        case "$cur" in
        --*)
-               __gitcomp_builtin clone "--no-single-branch"
+               __gitcomp_builtin clone
                return
                ;;
        esac
@@@ -1400,7 -1386,7 +1421,7 @@@ _git_commit (
                return
                ;;
        --*)
-               __gitcomp_builtin commit "--no-edit --verify"
+               __gitcomp_builtin commit
                return
        esac
  
@@@ -1503,7 -1489,7 +1524,7 @@@ _git_fetch (
                return
                ;;
        --*)
-               __gitcomp_builtin fetch "--no-tags"
+               __gitcomp_builtin fetch
                return
                ;;
        esac
@@@ -1540,7 -1526,7 +1561,7 @@@ _git_fsck (
  {
        case "$cur" in
        --*)
-               __gitcomp_builtin fsck "--no-reflogs"
+               __gitcomp_builtin fsck
                return
                ;;
        esac
@@@ -1618,12 -1604,13 +1639,12 @@@ _git_help (
                return
                ;;
        esac
 -      __git_compute_all_commands
 -      __gitcomp "$__git_all_commands $(__git_aliases)
 -              attributes cli core-tutorial cvs-migration
 -              diffcore everyday gitk glossary hooks ignore modules
 -              namespaces repository-layout revisions tutorial tutorial-2
 -              workflows
 -              "
 +      if test -n "$GIT_TESTING_ALL_COMMAND_LIST"
 +      then
 +              __gitcomp "$GIT_TESTING_ALL_COMMAND_LIST $(git --list-cmds=alias,list-guide) gitk"
 +      else
 +              __gitcomp "$(git --list-cmds=main,nohelpers,alias,list-guide) gitk"
 +      fi
  }
  
  _git_init ()
@@@ -1646,7 -1633,7 +1667,7 @@@ _git_ls_files (
  {
        case "$cur" in
        --*)
-               __gitcomp_builtin ls-files "--no-empty-directory"
+               __gitcomp_builtin ls-files
                return
                ;;
        esac
@@@ -1797,12 -1784,7 +1818,7 @@@ _git_merge (
  
        case "$cur" in
        --*)
-               __gitcomp_builtin merge "--no-rerere-autoupdate
-                               --no-commit --no-edit --no-ff
-                               --no-log --no-progress
-                               --no-squash --no-stat
-                               --no-verify-signatures
-                               "
+               __gitcomp_builtin merge
                return
        esac
        __git_complete_refs
@@@ -1901,10 -1883,7 +1917,7 @@@ _git_pull (
                return
                ;;
        --*)
-               __gitcomp_builtin pull "--no-autostash --no-commit --no-edit
-                                       --no-ff --no-log --no-progress --no-rebase
-                                       --no-squash --no-stat --no-tags
-                                       --no-verify-signatures"
+               __gitcomp_builtin pull
  
                return
                ;;
@@@ -2095,7 -2074,7 +2108,7 @@@ _git_status (
                return
                ;;
        --*)
-               __gitcomp_builtin status "--no-column"
+               __gitcomp_builtin status
                return
                ;;
        esac
@@@ -2142,24 -2121,9 +2155,24 @@@ __git_config_get_set_variables (
        __git config $config_file --name-only --list
  }
  
 +__git_config_vars=
 +__git_compute_config_vars ()
 +{
 +      test -n "$__git_config_vars" ||
 +      __git_config_vars="$(git help --config-for-completion | sort | uniq)"
 +}
 +
  _git_config ()
  {
 -      case "$prev" in
 +      local varname
 +
 +      if [ "${BASH_VERSINFO[0]:-0}" -ge 4 ]; then
 +              varname="${prev,,}"
 +      else
 +              varname="$(echo "$prev" |tr A-Z a-z)"
 +      fi
 +
 +      case "$varname" in
        branch.*.remote|branch.*.pushremote)
                __gitcomp_nl "$(__git_remotes)"
                return
                ;;
        branch.*.*)
                local pfx="${cur%.*}." cur_="${cur##*.}"
 -              __gitcomp "remote pushremote merge mergeoptions rebase" "$pfx" "$cur_"
 +              __gitcomp "remote pushRemote merge mergeOptions rebase" "$pfx" "$cur_"
                return
                ;;
        branch.*)
                local pfx="${cur%.*}." cur_="${cur#*.}"
                __gitcomp_direct "$(__git_heads "$pfx" "$cur_" ".")"
 -              __gitcomp_nl_append $'autosetupmerge\nautosetuprebase\n' "$pfx" "$cur_"
 +              __gitcomp_nl_append $'autoSetupMerge\nautoSetupRebase\n' "$pfx" "$cur_"
                return
                ;;
        guitool.*.*)
                local pfx="${cur%.*}." cur_="${cur##*.}"
                __gitcomp "
 -                      argprompt cmd confirm needsfile noconsole norescan
 -                      prompt revprompt revunmerged title
 +                      argPrompt cmd confirm needsFile noConsole noRescan
 +                      prompt revPrompt revUnmerged title
                        " "$pfx" "$cur_"
                return
                ;;
                local pfx="${cur%.*}." cur_="${cur##*.}"
                __gitcomp "
                        url proxy fetch push mirror skipDefaultUpdate
 -                      receivepack uploadpack tagopt pushurl
 +                      receivepack uploadpack tagOpt pushurl
                        " "$pfx" "$cur_"
                return
                ;;
        remote.*)
                local pfx="${cur%.*}." cur_="${cur#*.}"
                __gitcomp_nl "$(__git_remotes)" "$pfx" "$cur_" "."
 -              __gitcomp_nl_append "pushdefault" "$pfx" "$cur_"
 +              __gitcomp_nl_append "pushDefault" "$pfx" "$cur_"
                return
                ;;
        url.*.*)
                __gitcomp "insteadOf pushInsteadOf" "$pfx" "$cur_"
                return
                ;;
 +      *.*)
 +              __git_compute_config_vars
 +              __gitcomp "$__git_config_vars"
 +              ;;
 +      *)
 +              __git_compute_config_vars
 +              __gitcomp "$(echo "$__git_config_vars" | sed 's/\.[^ ]*/./g')"
        esac
 -      __gitcomp "
 -              add.ignoreErrors
 -              advice.amWorkDir
 -              advice.commitBeforeMerge
 -              advice.detachedHead
 -              advice.implicitIdentity
 -              advice.pushAlreadyExists
 -              advice.pushFetchFirst
 -              advice.pushNeedsForce
 -              advice.pushNonFFCurrent
 -              advice.pushNonFFMatching
 -              advice.pushUpdateRejected
 -              advice.resolveConflict
 -              advice.rmHints
 -              advice.statusHints
 -              advice.statusUoption
 -              advice.ignoredHook
 -              alias.
 -              am.keepcr
 -              am.threeWay
 -              apply.ignorewhitespace
 -              apply.whitespace
 -              branch.autosetupmerge
 -              branch.autosetuprebase
 -              browser.
 -              clean.requireForce
 -              color.branch
 -              color.branch.current
 -              color.branch.local
 -              color.branch.plain
 -              color.branch.remote
 -              color.decorate.HEAD
 -              color.decorate.branch
 -              color.decorate.remoteBranch
 -              color.decorate.stash
 -              color.decorate.tag
 -              color.diff
 -              color.diff.commit
 -              color.diff.frag
 -              color.diff.func
 -              color.diff.meta
 -              color.diff.new
 -              color.diff.old
 -              color.diff.plain
 -              color.diff.whitespace
 -              color.grep
 -              color.grep.context
 -              color.grep.filename
 -              color.grep.function
 -              color.grep.linenumber
 -              color.grep.match
 -              color.grep.selected
 -              color.grep.separator
 -              color.interactive
 -              color.interactive.error
 -              color.interactive.header
 -              color.interactive.help
 -              color.interactive.prompt
 -              color.pager
 -              color.showbranch
 -              color.status
 -              color.status.added
 -              color.status.changed
 -              color.status.header
 -              color.status.localBranch
 -              color.status.nobranch
 -              color.status.remoteBranch
 -              color.status.unmerged
 -              color.status.untracked
 -              color.status.updated
 -              color.ui
 -              commit.cleanup
 -              commit.gpgSign
 -              commit.status
 -              commit.template
 -              commit.verbose
 -              core.abbrev
 -              core.askpass
 -              core.attributesfile
 -              core.autocrlf
 -              core.bare
 -              core.bigFileThreshold
 -              core.checkStat
 -              core.commentChar
 -              core.commitGraph
 -              core.compression
 -              core.createObject
 -              core.deltaBaseCacheLimit
 -              core.editor
 -              core.eol
 -              core.excludesfile
 -              core.fileMode
 -              core.fsyncobjectfiles
 -              core.gitProxy
 -              core.hideDotFiles
 -              core.hooksPath
 -              core.ignoreStat
 -              core.ignorecase
 -              core.logAllRefUpdates
 -              core.loosecompression
 -              core.notesRef
 -              core.packedGitLimit
 -              core.packedGitWindowSize
 -              core.packedRefsTimeout
 -              core.pager
 -              core.precomposeUnicode
 -              core.preferSymlinkRefs
 -              core.preloadindex
 -              core.protectHFS
 -              core.protectNTFS
 -              core.quotepath
 -              core.repositoryFormatVersion
 -              core.safecrlf
 -              core.sharedRepository
 -              core.sparseCheckout
 -              core.splitIndex
 -              core.sshCommand
 -              core.symlinks
 -              core.trustctime
 -              core.untrackedCache
 -              core.warnAmbiguousRefs
 -              core.whitespace
 -              core.worktree
 -              credential.helper
 -              credential.useHttpPath
 -              credential.username
 -              credentialCache.ignoreSIGHUP
 -              diff.autorefreshindex
 -              diff.external
 -              diff.ignoreSubmodules
 -              diff.mnemonicprefix
 -              diff.noprefix
 -              diff.renameLimit
 -              diff.renames
 -              diff.statGraphWidth
 -              diff.submodule
 -              diff.suppressBlankEmpty
 -              diff.tool
 -              diff.wordRegex
 -              diff.algorithm
 -              difftool.
 -              difftool.prompt
 -              fetch.recurseSubmodules
 -              fetch.unpackLimit
 -              format.attach
 -              format.cc
 -              format.coverLetter
 -              format.from
 -              format.headers
 -              format.numbered
 -              format.pretty
 -              format.signature
 -              format.signoff
 -              format.subjectprefix
 -              format.suffix
 -              format.thread
 -              format.to
 -              gc.
 -              gc.aggressiveDepth
 -              gc.aggressiveWindow
 -              gc.auto
 -              gc.autoDetach
 -              gc.autopacklimit
 -              gc.logExpiry
 -              gc.packrefs
 -              gc.pruneexpire
 -              gc.reflogexpire
 -              gc.reflogexpireunreachable
 -              gc.rerereresolved
 -              gc.rerereunresolved
 -              gc.worktreePruneExpire
 -              gitcvs.allbinary
 -              gitcvs.commitmsgannotation
 -              gitcvs.dbTableNamePrefix
 -              gitcvs.dbdriver
 -              gitcvs.dbname
 -              gitcvs.dbpass
 -              gitcvs.dbuser
 -              gitcvs.enabled
 -              gitcvs.logfile
 -              gitcvs.usecrlfattr
 -              guitool.
 -              gui.blamehistoryctx
 -              gui.commitmsgwidth
 -              gui.copyblamethreshold
 -              gui.diffcontext
 -              gui.encoding
 -              gui.fastcopyblame
 -              gui.matchtrackingbranch
 -              gui.newbranchtemplate
 -              gui.pruneduringfetch
 -              gui.spellingdictionary
 -              gui.trustmtime
 -              help.autocorrect
 -              help.browser
 -              help.format
 -              http.lowSpeedLimit
 -              http.lowSpeedTime
 -              http.maxRequests
 -              http.minSessions
 -              http.noEPSV
 -              http.postBuffer
 -              http.proxy
 -              http.sslCipherList
 -              http.sslVersion
 -              http.sslCAInfo
 -              http.sslCAPath
 -              http.sslCert
 -              http.sslCertPasswordProtected
 -              http.sslKey
 -              http.sslVerify
 -              http.useragent
 -              i18n.commitEncoding
 -              i18n.logOutputEncoding
 -              imap.authMethod
 -              imap.folder
 -              imap.host
 -              imap.pass
 -              imap.port
 -              imap.preformattedHTML
 -              imap.sslverify
 -              imap.tunnel
 -              imap.user
 -              init.templatedir
 -              instaweb.browser
 -              instaweb.httpd
 -              instaweb.local
 -              instaweb.modulepath
 -              instaweb.port
 -              interactive.singlekey
 -              log.date
 -              log.decorate
 -              log.showroot
 -              mailmap.file
 -              man.
 -              man.viewer
 -              merge.
 -              merge.conflictstyle
 -              merge.log
 -              merge.renameLimit
 -              merge.renormalize
 -              merge.stat
 -              merge.tool
 -              merge.verbosity
 -              mergetool.
 -              mergetool.keepBackup
 -              mergetool.keepTemporaries
 -              mergetool.prompt
 -              notes.displayRef
 -              notes.rewrite.
 -              notes.rewrite.amend
 -              notes.rewrite.rebase
 -              notes.rewriteMode
 -              notes.rewriteRef
 -              pack.compression
 -              pack.deltaCacheLimit
 -              pack.deltaCacheSize
 -              pack.depth
 -              pack.indexVersion
 -              pack.packSizeLimit
 -              pack.threads
 -              pack.window
 -              pack.windowMemory
 -              pager.
 -              pretty.
 -              pull.octopus
 -              pull.twohead
 -              push.default
 -              push.followTags
 -              rebase.autosquash
 -              rebase.stat
 -              receive.autogc
 -              receive.denyCurrentBranch
 -              receive.denyDeleteCurrent
 -              receive.denyDeletes
 -              receive.denyNonFastForwards
 -              receive.fsckObjects
 -              receive.unpackLimit
 -              receive.updateserverinfo
 -              remote.pushdefault
 -              remotes.
 -              repack.usedeltabaseoffset
 -              rerere.autoupdate
 -              rerere.enabled
 -              sendemail.
 -              sendemail.aliasesfile
 -              sendemail.aliasfiletype
 -              sendemail.bcc
 -              sendemail.cc
 -              sendemail.cccmd
 -              sendemail.chainreplyto
 -              sendemail.confirm
 -              sendemail.envelopesender
 -              sendemail.from
 -              sendemail.identity
 -              sendemail.multiedit
 -              sendemail.signedoffbycc
 -              sendemail.smtpdomain
 -              sendemail.smtpencryption
 -              sendemail.smtppass
 -              sendemail.smtpserver
 -              sendemail.smtpserveroption
 -              sendemail.smtpserverport
 -              sendemail.smtpuser
 -              sendemail.suppresscc
 -              sendemail.suppressfrom
 -              sendemail.thread
 -              sendemail.to
 -              sendemail.tocmd
 -              sendemail.validate
 -              sendemail.smtpbatchsize
 -              sendemail.smtprelogindelay
 -              showbranch.default
 -              status.relativePaths
 -              status.showUntrackedFiles
 -              status.submodulesummary
 -              submodule.
 -              tar.umask
 -              transfer.unpackLimit
 -              url.
 -              user.email
 -              user.name
 -              user.signingkey
 -              web.browser
 -              branch. remote.
 -      "
  }
  
  _git_remote ()
  
        case "$subcommand,$cur" in
        add,--*)
-               __gitcomp_builtin remote_add "--no-tags"
+               __gitcomp_builtin remote_add
                ;;
        add,*)
                ;;
                __gitcomp_builtin remote_update
                ;;
        update,*)
 -              __gitcomp "$(__git_get_config_variables "remotes")"
 +              __gitcomp "$(__git_remotes) $(__git_get_config_variables "remotes")"
                ;;
        set-url,--*)
                __gitcomp_builtin remote_set-url
@@@ -2425,7 -2708,7 +2438,7 @@@ _git_revert (
        fi
        case "$cur" in
        --*)
-               __gitcomp_builtin revert "--no-edit" \
+               __gitcomp_builtin revert "" \
                        "$__git_revert_inprogress_options"
                return
                ;;
@@@ -2495,7 -2778,7 +2508,7 @@@ _git_show_branch (
  {
        case "$cur" in
        --*)
-               __gitcomp_builtin show-branch "--no-color"
+               __gitcomp_builtin show-branch
                return
                ;;
        esac
@@@ -2788,7 -3071,7 +2801,7 @@@ __git_complete_common () 
  __git_cmds_with_parseopt_helper=
  __git_support_parseopt_helper () {
        test -n "$__git_cmds_with_parseopt_helper" ||
 -              __git_cmds_with_parseopt_helper="$(__git --list-parseopt-builtins)"
 +              __git_cmds_with_parseopt_helper="$(__git --list-cmds=parseopt)"
  
        case " $__git_cmds_with_parseopt_helper " in
        *" $1 "*)
@@@ -2874,14 -3157,8 +2887,14 @@@ __git_main (
                        --help
                        "
                        ;;
 -              *)     __git_compute_porcelain_commands
 -                     __gitcomp "$__git_porcelain_commands $(__git_aliases)" ;;
 +              *)
 +                      if test -n "$GIT_TESTING_PORCELAIN_COMMAND_LIST"
 +                      then
 +                              __gitcomp "$GIT_TESTING_PORCELAIN_COMMAND_LIST"
 +                      else
 +                              __gitcomp "$(git --list-cmds=list-mainporcelain,others,nohelpers,alias,list-complete,config)"
 +                      fi
 +                      ;;
                esac
                return
        fi
@@@ -2919,10 -3196,7 +2932,10 @@@ __gitk_main (
        __git_complete_revlist
  }
  
 -if [[ -n ${ZSH_VERSION-} ]]; then
 +if [[ -n ${ZSH_VERSION-} ]] &&
 +   # Don't define these functions when sourced from 'git-completion.zsh',
 +   # it has its own implementations.
 +   [[ -z ${GIT_SOURCING_ZSH_COMPLETION-} ]]; then
        echo "WARNING: this script is deprecated, please see git-completion.zsh" 1>&2
  
        autoload -U +X compinit && compinit
                compadd -Q -S "${4- }" -p "${2-}" -- ${=1} && _ret=0
        }
  
 +      __gitcomp_file_direct ()
 +      {
 +              emulate -L zsh
 +
 +              local IFS=$'\n'
 +              compset -P '*[=:]'
 +              compadd -Q -f -- ${=1} && _ret=0
 +      }
 +
        __gitcomp_file ()
        {
                emulate -L zsh
diff --combined t/t9902-completion.sh
index a28640ce1accd604bd32fdc95fd172119f6c3741,157ee7085d78138525a9115af065ede2000d31f3..3b3a7b66e41024b5a9b939f1c33a9996b8874a12
@@@ -13,7 -13,7 +13,7 @@@ complete (
        return 0
  }
  
 -# Be careful when updating this list:
 +# Be careful when updating these lists:
  #
  # (1) The build tree may have build artifact from different branch, or
  #     the user's $PATH may have a random executable that may begin
@@@ -30,8 -30,7 +30,8 @@@
  #     completion for "git <TAB>", and a plumbing is excluded.  "add",
  #     "filter-branch" and "ls-files" are listed for this.
  
 -GIT_TESTING_COMMAND_COMPLETION='add checkout check-attr filter-branch ls-files'
 +GIT_TESTING_ALL_COMMAND_LIST='add checkout check-attr filter-branch ls-files'
 +GIT_TESTING_PORCELAIN_COMMAND_LIST='add checkout filter-branch'
  
  . "$GIT_BUILD_DIR/contrib/completion/git-completion.bash"
  
@@@ -85,11 -84,10 +85,11 @@@ test_completion (
        then
                printf '%s\n' "$2" >expected
        else
 -              sed -e 's/Z$//' >expected
 +              sed -e 's/Z$//' |sort >expected
        fi &&
        run_completion "$1" &&
 -      test_cmp expected out
 +      sort out >out_sorted &&
 +      test_cmp expected out_sorted
  }
  
  # Test __gitcomp.
@@@ -181,7 -179,7 +181,7 @@@ test_expect_success '__git_find_repo_pa
  test_expect_success '__git_find_repo_path - parent is a .git directory' '
        echo "$ROOT/.git" >expected &&
        (
 -              cd .git/refs/heads &&
 +              cd .git/objects &&
                __git_find_repo_path &&
                echo "$__git_repo_path" >"$actual"
        ) &&
@@@ -402,46 -400,6 +402,46 @@@ test_expect_success '__gitdir - remote 
        test_cmp expected "$actual"
  '
  
 +
 +test_expect_success '__git_dequote - plain unquoted word' '
 +      __git_dequote unquoted-word &&
 +      verbose test unquoted-word = "$dequoted_word"
 +'
 +
 +# input:    b\a\c\k\'\\\"s\l\a\s\h\es
 +# expected: back'\"slashes
 +test_expect_success '__git_dequote - backslash escaped' '
 +      __git_dequote "b\a\c\k\\'\''\\\\\\\"s\l\a\s\h\es" &&
 +      verbose test "back'\''\\\"slashes" = "$dequoted_word"
 +'
 +
 +# input:    sin'gle\' '"quo'ted
 +# expected: single\ "quoted
 +test_expect_success '__git_dequote - single quoted' '
 +      __git_dequote "'"sin'gle\\\\' '\\\"quo'ted"'" &&
 +      verbose test '\''single\ "quoted'\'' = "$dequoted_word"
 +'
 +
 +# input:    dou"ble\\" "\"\quot"ed
 +# expected: double\ "\quoted
 +test_expect_success '__git_dequote - double quoted' '
 +      __git_dequote '\''dou"ble\\" "\"\quot"ed'\'' &&
 +      verbose test '\''double\ "\quoted'\'' = "$dequoted_word"
 +'
 +
 +# input: 'open single quote
 +test_expect_success '__git_dequote - open single quote' '
 +      __git_dequote "'\''open single quote" &&
 +      verbose test "open single quote" = "$dequoted_word"
 +'
 +
 +# input: "open double quote
 +test_expect_success '__git_dequote - open double quote' '
 +      __git_dequote "\"open double quote" &&
 +      verbose test "open double quote" = "$dequoted_word"
 +'
 +
 +
  test_expect_success '__gitcomp_direct - puts everything into COMPREPLY as-is' '
        sed -e "s/Z$//g" >expected <<-EOF &&
        with-trailing-space Z
@@@ -501,6 -459,42 +501,42 @@@ test_expect_success '__gitcomp - suffix
        EOF
  '
  
+ test_expect_success '__gitcomp - ignore optional negative options' '
+       test_gitcomp "--" "--abc --def --no-one -- --no-two" <<-\EOF
+       --abc Z
+       --def Z
+       --no-one Z
+       --no-... Z
+       EOF
+ '
+ test_expect_success '__gitcomp - ignore/narrow optional negative options' '
+       test_gitcomp "--a" "--abc --abcdef --no-one -- --no-two" <<-\EOF
+       --abc Z
+       --abcdef Z
+       EOF
+ '
+ test_expect_success '__gitcomp - ignore/narrow optional negative options' '
+       test_gitcomp "--n" "--abc --def --no-one -- --no-two" <<-\EOF
+       --no-one Z
+       --no-... Z
+       EOF
+ '
+ test_expect_success '__gitcomp - expand all negative options' '
+       test_gitcomp "--no-" "--abc --def --no-one -- --no-two" <<-\EOF
+       --no-one Z
+       --no-two Z
+       EOF
+ '
+ test_expect_success '__gitcomp - expand/narrow all negative options' '
+       test_gitcomp "--no-o" "--abc --def --no-one -- --no-two" <<-\EOF
+       --no-one Z
+       EOF
+ '
  test_expect_success '__gitcomp - doesnt fail because of invalid variable name' '
        __gitcomp "$invalid_variable_name"
  '
@@@ -1210,124 -1204,6 +1246,124 @@@ test_expect_success 'teardown after re
        git remote remove other
  '
  
 +
 +test_path_completion ()
 +{
 +      test $# = 2 || error "bug in the test script: not 2 parameters to test_path_completion"
 +
 +      local cur="$1" expected="$2"
 +      echo "$expected" >expected &&
 +      (
 +              # In the following tests calling this function we only
 +              # care about how __git_complete_index_file() deals with
 +              # unusual characters in path names.  By requesting only
 +              # untracked files we dont have to bother adding any
 +              # paths to the index in those tests.
 +              __git_complete_index_file --others &&
 +              print_comp
 +      ) &&
 +      test_cmp expected out
 +}
 +
 +test_expect_success 'setup for path completion tests' '
 +      mkdir simple-dir \
 +            "spaces in dir" \
 +            árvíztűrő &&
 +      touch simple-dir/simple-file \
 +            "spaces in dir/spaces in file" \
 +            "árvíztűrő/Сайн яваарай" &&
 +      if test_have_prereq !MINGW &&
 +         mkdir BS\\dir \
 +               '$'separators\034in\035dir'' &&
 +         touch BS\\dir/DQ\"file \
 +               '$'separators\034in\035dir/sep\036in\037file''
 +      then
 +              test_set_prereq FUNNYNAMES
 +      else
 +              rm -rf BS\\dir '$'separators\034in\035dir''
 +      fi
 +'
 +
 +test_expect_success '__git_complete_index_file - simple' '
 +      test_path_completion simple simple-dir &&  # Bash is supposed to
 +                                                 # add the trailing /.
 +      test_path_completion simple-dir/simple simple-dir/simple-file
 +'
 +
 +test_expect_success \
 +    '__git_complete_index_file - escaped characters on cmdline' '
 +      test_path_completion spac "spaces in dir" &&  # Bash will turn this
 +                                                    # into "spaces\ in\ dir"
 +      test_path_completion "spaces\\ i" \
 +                           "spaces in dir" &&
 +      test_path_completion "spaces\\ in\\ dir/s" \
 +                           "spaces in dir/spaces in file" &&
 +      test_path_completion "spaces\\ in\\ dir/spaces\\ i" \
 +                           "spaces in dir/spaces in file"
 +'
 +
 +test_expect_success \
 +    '__git_complete_index_file - quoted characters on cmdline' '
 +      # Testing with an opening but without a corresponding closing
 +      # double quote is important.
 +      test_path_completion \"spac "spaces in dir" &&
 +      test_path_completion "\"spaces i" \
 +                           "spaces in dir" &&
 +      test_path_completion "\"spaces in dir/s" \
 +                           "spaces in dir/spaces in file" &&
 +      test_path_completion "\"spaces in dir/spaces i" \
 +                           "spaces in dir/spaces in file"
 +'
 +
 +test_expect_success '__git_complete_index_file - UTF-8 in ls-files output' '
 +      test_path_completion á árvíztűrő &&
 +      test_path_completion árvíztűrő/С "árvíztűrő/Сайн яваарай"
 +'
 +
 +test_expect_success FUNNYNAMES \
 +    '__git_complete_index_file - C-style escapes in ls-files output' '
 +      test_path_completion BS \
 +                           BS\\dir &&
 +      test_path_completion BS\\\\d \
 +                           BS\\dir &&
 +      test_path_completion BS\\\\dir/DQ \
 +                           BS\\dir/DQ\"file &&
 +      test_path_completion BS\\\\dir/DQ\\\"f \
 +                           BS\\dir/DQ\"file
 +'
 +
 +test_expect_success FUNNYNAMES \
 +    '__git_complete_index_file - \nnn-escaped characters in ls-files output' '
 +      test_path_completion sep '$'separators\034in\035dir'' &&
 +      test_path_completion '$'separators\034i'' \
 +                           '$'separators\034in\035dir'' &&
 +      test_path_completion '$'separators\034in\035dir/sep'' \
 +                           '$'separators\034in\035dir/sep\036in\037file'' &&
 +      test_path_completion '$'separators\034in\035dir/sep\036i'' \
 +                           '$'separators\034in\035dir/sep\036in\037file''
 +'
 +
 +test_expect_success FUNNYNAMES \
 +    '__git_complete_index_file - removing repeated quoted path components' '
 +      test_when_finished rm -r repeated-quoted &&
 +      mkdir repeated-quoted &&      # A directory whose name in itself
 +                                    # would not be quoted ...
 +      >repeated-quoted/0-file &&
 +      >repeated-quoted/1\"file &&   # ... but here the file makes the
 +                                    # dirname quoted ...
 +      >repeated-quoted/2-file &&
 +      >repeated-quoted/3\"file &&   # ... and here, too.
 +
 +      # Still, we shold only list the directory name only once.
 +      test_path_completion repeated repeated-quoted
 +'
 +
 +test_expect_success 'teardown after path completion tests' '
 +      rm -rf simple-dir "spaces in dir" árvíztűrő \
 +             BS\\dir '$'separators\034in\035dir''
 +'
 +
 +
  test_expect_success '__git_get_config_variables' '
        cat >expect <<-EOF &&
        name-1
@@@ -1351,6 -1227,17 +1387,6 @@@ test_expect_success '__git_pretty_alias
        test_cmp expect actual
  '
  
 -test_expect_success '__git_aliases' '
 -      cat >expect <<-EOF &&
 -      ci
 -      co
 -      EOF
 -      test_config alias.ci commit &&
 -      test_config alias.co checkout &&
 -      __git_aliases >actual &&
 -      test_cmp expect actual
 -'
 -
  test_expect_success 'basic' '
        run_completion "git " &&
        # built-in
@@@ -1398,8 -1285,8 +1434,8 @@@ test_expect_success 'double dash "git c
        --ignore-other-worktrees Z
        --recurse-submodules Z
        --progress Z
-       --no-track Z
-       --no-recurse-submodules Z
+       --no-quiet Z
+       --no-... Z
        EOF
  '
  
@@@ -1514,7 -1401,6 +1550,7 @@@ test_expect_success 'complete files' 
  
        echo "expected" > .gitignore &&
        echo "out" >> .gitignore &&
 +      echo "out_sorted" >> .gitignore &&
  
        git add .gitignore &&
        test_completion "git commit " ".gitignore" &&
@@@ -1607,6 -1493,7 +1643,7 @@@ test_expect_success 'completion used <c
  test_expect_success 'completion without explicit _git_xxx function' '
        test_completion "git version --" <<-\EOF
        --build-options Z
+       --no-build-options Z
        EOF
  '
  
@@@ -1660,6 -1547,13 +1697,6 @@@ test_expect_success 'sourcing the compl
        verbose test -z "$__git_all_commands"
  '
  
 -test_expect_success 'sourcing the completion script clears cached porcelain commands' '
 -      __git_compute_porcelain_commands &&
 -      verbose test -n "$__git_porcelain_commands" &&
 -      . "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" &&
 -      verbose test -z "$__git_porcelain_commands"
 -'
 -
  test_expect_success !GETTEXT_POISON 'sourcing the completion script clears cached merge strategies' '
        __git_compute_merge_strategies &&
        verbose test -n "$__git_merge_strategies" &&