Merge branch 'sg/completion-zsh-workaround'
authorJunio C Hamano <gitster@pobox.com>
Wed, 13 Jun 2018 19:50:45 +0000 (12:50 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 13 Jun 2018 19:50:45 +0000 (12:50 -0700)
Work around zsh segfaulting when loading git-completion.zsh

* sg/completion-zsh-workaround:
completion: correct zsh detection when run from git-completion.zsh

1  2 
contrib/completion/git-completion.bash
contrib/completion/git-completion.zsh
index 12814e9bbf6be5ff0d1608c71554c2b4bb14c87d,1ed6b97f8a8bd68f6705aecfc9a5c932b8832c2b..dd3e925843acf53c439e504101d46d8606ea5d69
@@@ -29,8 -29,6 +29,8 @@@
  # tell the completion to use commit completion.  This also works with aliases
  # of form "!sh -c '...'".  For example, "!sh -c ': git commit ; ... '".
  #
 +# Compatible with bash 3.2.57.
 +#
  # You can set the following environment variables to influence the behavior of
  # the completion routines:
  #
@@@ -94,70 -92,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+
@@@ -410,24 -344,6 +410,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 -363,7 +447,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" |
 -      while read -r file; do
 -              case "$file" in
 -              ?*/*) echo "${file%%/*}" ;;
 -              *) echo "$file" ;;
 -              esac
 -      done | 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 -716,26 +889,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 -836,126 +989,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-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 -973,11 +1011,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 ()
  {
@@@ -1321,12 -1288,6 +1321,12 @@@ _git_checkout (
  
  _git_cherry ()
  {
 +      case "$cur" in
 +      --*)
 +              __gitcomp_builtin cherry
 +              return
 +      esac
 +
        __git_complete_refs
  }
  
@@@ -1546,6 -1507,16 +1546,6 @@@ _git_fsck (
        esac
  }
  
 -_git_gc ()
 -{
 -      case "$cur" in
 -      --*)
 -              __gitcomp_builtin gc
 -              return
 -              ;;
 -      esac
 -}
 -
  _git_gitk ()
  {
        _gitk
@@@ -1618,12 -1589,13 +1618,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 ()
@@@ -1669,13 -1641,6 +1669,13 @@@ _git_ls_remote (
  
  _git_ls_tree ()
  {
 +      case "$cur" in
 +      --*)
 +              __gitcomp_builtin ls-tree
 +              return
 +              ;;
 +      esac
 +
        __git_complete_file
  }
  
@@@ -1851,6 -1816,11 +1851,6 @@@ _git_mv (
        fi
  }
  
 -_git_name_rev ()
 -{
 -      __gitcomp_builtin name-rev
 -}
 -
  _git_notes ()
  {
        local subcommands='add append copy edit get-ref list merge prune remove show'
@@@ -1983,7 -1953,7 +1983,7 @@@ _git_rebase (
        --*)
                __gitcomp "
                        --onto --merge --strategy --interactive
 -                      --preserve-merges --stat --no-stat
 +                      --rebase-merges --preserve-merges --stat --no-stat
                        --committer-date-is-author-date --ignore-date
                        --ignore-whitespace --whitespace=
                        --autosquash --no-autosquash
@@@ -2154,7 -2124,7 +2154,7 @@@ _git_config (
                return
                ;;
        branch.*.rebase)
 -              __gitcomp "false true preserve interactive"
 +              __gitcomp "false true merges preserve interactive"
                return
                ;;
        remote.pushdefault)
                __gitcomp "$__git_log_date_formats"
                return
                ;;
 -      sendemail.aliasesfiletype)
 +      sendemail.aliasfiletype)
                __gitcomp "mutt mailrc pine elm gnus"
                return
                ;;
                core.bigFileThreshold
                core.checkStat
                core.commentChar
 +              core.commitGraph
                core.compression
                core.createObject
                core.deltaBaseCacheLimit
@@@ -2809,21 -2778,13 +2809,21 @@@ _git_show_branch (
  _git_stash ()
  {
        local save_opts='--all --keep-index --no-keep-index --quiet --patch --include-untracked'
 -      local subcommands='push save list show apply clear drop pop create branch'
 -      local subcommand="$(__git_find_on_cmdline "$subcommands")"
 +      local subcommands='push list show apply clear drop pop create branch'
 +      local subcommand="$(__git_find_on_cmdline "$subcommands save")"
 +      if [ -n "$(__git_find_on_cmdline "-p")" ]; then
 +              subcommand="push"
 +      fi
        if [ -z "$subcommand" ]; then
                case "$cur" in
                --*)
                        __gitcomp "$save_opts"
                        ;;
 +              sa*)
 +                      if [ -z "$(__git_find_on_cmdline "$save_opts")" ]; then
 +                              __gitcomp "save"
 +                      fi
 +                      ;;
                *)
                        if [ -z "$(__git_find_on_cmdline "$save_opts")" ]; then
                                __gitcomp "$subcommands"
@@@ -3079,52 -3040,6 +3079,52 @@@ _git_worktree (
        fi
  }
  
 +__git_complete_common () {
 +      local command="$1"
 +
 +      case "$cur" in
 +      --*)
 +              __gitcomp_builtin "$command"
 +              ;;
 +      esac
 +}
 +
 +__git_cmds_with_parseopt_helper=
 +__git_support_parseopt_helper () {
 +      test -n "$__git_cmds_with_parseopt_helper" ||
 +              __git_cmds_with_parseopt_helper="$(__git --list-cmds=parseopt)"
 +
 +      case " $__git_cmds_with_parseopt_helper " in
 +      *" $1 "*)
 +              return 0
 +              ;;
 +      *)
 +              return 1
 +              ;;
 +      esac
 +}
 +
 +__git_complete_command () {
 +      local command="$1"
 +      local completion_func="_git_${command//-/_}"
 +      if ! declare -f $completion_func >/dev/null 2>/dev/null &&
 +              declare -f _completion_loader >/dev/null 2>/dev/null
 +      then
 +              _completion_loader "git-$command"
 +      fi
 +      if declare -f $completion_func >/dev/null 2>/dev/null
 +      then
 +              $completion_func
 +              return 0
 +      elif __git_support_parseopt_helper "$command"
 +      then
 +              __git_complete_common "$command"
 +              return 0
 +      else
 +              return 1
 +      fi
 +}
 +
  __git_main ()
  {
        local i c=1 command __git_dir __git_repo_path
                        --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
  
 -      local completion_func="_git_${command//-/_}"
 -      declare -f $completion_func >/dev/null 2>/dev/null && $completion_func && return
 +      __git_complete_command "$command" && return
  
        local expansion=$(__git_aliased_command "$command")
        if [ -n "$expansion" ]; then
                words[1]=$expansion
 -              completion_func="_git_${expansion//-/_}"
 -              declare -f $completion_func >/dev/null 2>/dev/null && $completion_func
 +              __git_complete_command "$expansion"
        fi
  }
  
@@@ -3223,7 -3134,10 +3223,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
index 53cb0f934f8ba0b468145fe06480f9c99f860996,78808b1c5e98a0b8f0970a6b0f8a2bddf9519d48..049d6b80f650717f20e68568251c21098f598d80
@@@ -39,7 -39,7 +39,7 @@@ if [ -z "$script" ]; the
                test -f $e && script="$e" && break
        done
  fi
ZSH_VERSION='' . "$script"
GIT_SOURCING_ZSH_COMPLETION=y . "$script"
  
  __gitcomp ()
  {
@@@ -93,15 -93,6 +93,15 @@@ __gitcomp_nl_append (
        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