completion: add hack to enable file mode in bash < 4
[gitweb.git] / contrib / completion / git-completion.bash
index f67b0f008b7c7e82612d7d2330cbd6dbf80adf6b..f9e8e7dae52068f82edeced360198279c09a86a6 100644 (file)
@@ -53,19 +53,6 @@ __gitdir ()
        fi
 }
 
-__gitcomp_1 ()
-{
-       local c IFS=$' \t\n'
-       for c in $1; do
-               c="$c$2"
-               case $c in
-               --*=*|*.) ;;
-               *) c="$c " ;;
-               esac
-               printf '%s\n' "$c"
-       done
-}
-
 # The following function is based on code from:
 #
 #   bash_completion - programmable completion functions for bash 3.2+
@@ -195,8 +182,18 @@ _get_comp_words_by_ref ()
 }
 fi
 
-# Generates completion reply with compgen, appending a space to possible
-# completion words, if necessary.
+__gitcompadd ()
+{
+       local i=0
+       for x in $1; do
+               if [[ "$x" == "$3"* ]]; then
+                       COMPREPLY[i++]="$2$x$4"
+               fi
+       done
+}
+
+# Generates completion reply, appending a space to possible completion words,
+# if necessary.
 # It accepts 1 to 4 arguments:
 # 1: List of possible completion words.
 # 2: A prefix to be added to each possible completion word (optional).
@@ -208,19 +205,25 @@ __gitcomp ()
 
        case "$cur_" in
        --*=)
-               COMPREPLY=()
                ;;
        *)
-               local IFS=$'\n'
-               COMPREPLY=($(compgen -P "${2-}" \
-                       -W "$(__gitcomp_1 "${1-}" "${4-}")" \
-                       -- "$cur_"))
+               local c i=0 IFS=$' \t\n'
+               for c in $1; do
+                       c="$c${4-}"
+                       if [[ $c == "$cur_"* ]]; then
+                               case $c in
+                               --*=*|*.) ;;
+                               *) c="$c " ;;
+                               esac
+                               COMPREPLY[i++]="${2-}$c"
+                       fi
+               done
                ;;
        esac
 }
 
-# Generates completion reply with compgen from newline-separated possible
-# completion words by appending a space to all of them.
+# Generates completion reply from newline-separated possible completion words
+# by appending a space to all of them.
 # It accepts 1 to 4 arguments:
 # 1: List of possible completion words, separated by a single newline.
 # 2: A prefix to be added to each possible completion word (optional).
@@ -231,7 +234,7 @@ __gitcomp ()
 __gitcomp_nl ()
 {
        local IFS=$'\n'
-       COMPREPLY=($(compgen -P "${2-}" -S "${4- }" -W "$1" -- "${3-$cur}"))
+       __gitcompadd "$1" "${2-}" "${3-$cur}" "${4- }"
 }
 
 # Generates completion reply with compgen from newline-separated possible
@@ -249,35 +252,23 @@ __gitcomp_file ()
        # since tilde expansion is not applied.
        # This means that COMPREPLY will be empty and Bash default
        # completion will be used.
-       COMPREPLY=($(compgen -P "${2-}" -W "$1" -- "${3-$cur}"))
-
-       # Tell Bash that compspec generates filenames.
-       compopt -o filenames 2>/dev/null
-}
-
-__git_index_file_list_filter_compat ()
-{
-       local path
+       __gitcompadd "$1" "${2-}" "${3-$cur}" ""
 
-       while read -r path; do
-               case "$path" in
-               ?*/*) echo "${path%%/*}/" ;;
-               *) echo "$path" ;;
-               esac
-       done
+       # use a hack to enable file mode in bash < 4
+       compopt -o filenames 2>/dev/null ||
+       compgen -f /non-existing-dir/ > /dev/null
 }
 
-__git_index_file_list_filter_bash ()
+# Process path list returned by "ls-files" and "diff-index --name-only"
+# commands, in order to list only file names relative to a specified
+# directory, and append a slash to directory names.
+__git_index_file_list_filter ()
 {
        local path
 
        while read -r path; do
                case "$path" in
                ?*/*)
-                       # XXX if we append a slash to directory names when using
-                       # `compopt -o filenames`, Bash will append another slash.
-                       # This is pretty stupid, and this the reason why we have to
-                       # define a compatible version for this function.
                        echo "${path%%/*}" ;;
                *)
                        echo "$path" ;;
@@ -285,43 +276,27 @@ __git_index_file_list_filter_bash ()
        done
 }
 
-# Process path list returned by "ls-files" and "diff-index --name-only"
-# commands, in order to list only file names relative to a specified
-# directory, and append a slash to directory names.
-__git_index_file_list_filter ()
-{
-       # Default to Bash >= 4.x
-       __git_index_file_list_filter_bash
-}
-
-# Execute git ls-files, returning paths relative to the directory
-# specified in the first argument, and using the options specified in
-# the second argument.
+# Execute 'git ls-files', unless the --committable option is specified, in
+# which case it runs 'git diff-index' to find out the files that can be
+# committed.  It return paths relative to the directory specified in the first
+# argument, and using the options specified in the second argument.
 __git_ls_files_helper ()
 {
        (
                test -n "${CDPATH+set}" && unset CDPATH
-               # NOTE: $2 is not quoted in order to support multiple options
-               cd "$1" && git ls-files --exclude-standard $2
+               cd "$1"
+               if [ "$2" == "--committable" ]; then
+                       git diff-index --name-only --relative HEAD
+               else
+                       # NOTE: $2 is not quoted in order to support multiple options
+                       git ls-files --exclude-standard $2
+               fi
        ) 2>/dev/null
 }
 
 
-# Execute git diff-index, returning paths relative to the directory
-# specified in the first argument, and using the tree object id
-# specified in the second argument.
-__git_diff_index_helper ()
-{
-       (
-               test -n "${CDPATH+set}" && unset CDPATH
-               cd "$1" && git diff-index --name-only --relative "$2"
-       ) 2>/dev/null
-}
-
 # __git_index_files accepts 1 or 2 arguments:
 # 1: Options to pass to ls-files (required).
-#    Supported options are --cached, --modified, --deleted, --others,
-#    and --directory.
 # 2: A directory path (optional).
 #    If provided, only files within the specified directory are listed.
 #    Sub directories are never recursed.  Path must have a trailing
@@ -336,22 +311,6 @@ __git_index_files ()
        fi
 }
 
-# __git_diff_index_files accepts 1 or 2 arguments:
-# 1) The id of a tree object.
-# 2) A directory path (optional).
-#    If provided, only files within the specified directory are listed.
-#    Sub directories are never recursed.  Path must have a trailing
-#    slash.
-__git_diff_index_files ()
-{
-       local dir="$(__gitdir)" root="${2-.}"
-
-       if [ -d "$dir" ]; then
-               __git_diff_index_helper "$root" "$1" | __git_index_file_list_filter |
-                       sort | uniq
-       fi
-}
-
 __git_heads ()
 {
        local dir="$(__gitdir)"
@@ -549,44 +508,23 @@ __git_complete_revlist_file ()
 }
 
 
-# __git_complete_index_file requires 1 argument: the options to pass to
-# ls-file
+# __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"
+       local pfx="" cur_="$cur"
 
        case "$cur_" in
        ?*/*)
                pfx="${cur_%/*}"
                cur_="${cur_##*/}"
                pfx="${pfx}/"
-
-               __gitcomp_file "$(__git_index_files "$1" "$pfx")" "$pfx" "$cur_"
-               ;;
-       *)
-               __gitcomp_file "$(__git_index_files "$1")" "" "$cur_"
                ;;
        esac
-}
-
-# __git_complete_diff_index_file requires 1 argument: the id of a tree
-# object
-__git_complete_diff_index_file ()
-{
-       local pfx cur_="$cur"
-
-       case "$cur_" in
-       ?*/*)
-               pfx="${cur_%/*}"
-               cur_="${cur_##*/}"
-               pfx="${pfx}/"
 
-               __gitcomp_file "$(__git_diff_index_files "$1" "$pfx")" "$pfx" "$cur_"
-               ;;
-       *)
-               __gitcomp_file "$(__git_diff_index_files "$1")" "" "$cur_"
-               ;;
-       esac
+       __gitcomp_file "$(__git_index_files "$1" "$pfx")" "$pfx" "$cur_"
 }
 
 __git_complete_file ()
@@ -614,7 +552,6 @@ __git_complete_remote_or_refspec ()
                        case "$cmd" in
                        push) no_complete_refspec=1 ;;
                        fetch)
-                               COMPREPLY=()
                                return
                                ;;
                        *) ;;
@@ -630,7 +567,6 @@ __git_complete_remote_or_refspec ()
                return
        fi
        if [ $no_complete_refspec = 1 ]; then
-               COMPREPLY=()
                return
        fi
        [ "$remote" = "." ] && remote=
@@ -951,7 +887,6 @@ _git_am ()
                        "
                return
        esac
-       COMPREPLY=()
 }
 
 _git_apply ()
@@ -971,7 +906,6 @@ _git_apply ()
                        "
                return
        esac
-       COMPREPLY=()
 }
 
 _git_add ()
@@ -1031,7 +965,6 @@ _git_bisect ()
                __gitcomp_nl "$(__git_refs)"
                ;;
        *)
-               COMPREPLY=()
                ;;
        esac
 }
@@ -1124,9 +1057,14 @@ _git_cherry ()
 
 _git_cherry_pick ()
 {
+       local dir="$(__gitdir)"
+       if [ -f "$dir"/CHERRY_PICK_HEAD ]; then
+               __gitcomp "--continue --quit --abort"
+               return
+       fi
        case "$cur" in
        --*)
-               __gitcomp "--edit --no-commit"
+               __gitcomp "--edit --no-commit --signoff --strategy= --mainline"
                ;;
        *)
                __gitcomp_nl "$(__git_refs)"
@@ -1170,7 +1108,6 @@ _git_clone ()
                return
                ;;
        esac
-       COMPREPLY=()
 }
 
 _git_commit ()
@@ -1211,7 +1148,7 @@ _git_commit ()
        esac
 
        if git rev-parse --verify --quiet HEAD >/dev/null; then
-               __git_complete_diff_index_file "HEAD"
+               __git_complete_index_file "--committable"
        else
                # This is the first commit
                __git_complete_index_file "--cached"
@@ -1347,7 +1284,6 @@ _git_fsck ()
                return
                ;;
        esac
-       COMPREPLY=()
 }
 
 _git_gc ()
@@ -1358,7 +1294,6 @@ _git_gc ()
                return
                ;;
        esac
-       COMPREPLY=()
 }
 
 _git_gitk ()
@@ -1435,7 +1370,6 @@ _git_init ()
                return
                ;;
        esac
-       COMPREPLY=()
 }
 
 _git_ls_files ()
@@ -1571,7 +1505,6 @@ _git_mergetool ()
                return
                ;;
        esac
-       COMPREPLY=()
 }
 
 _git_merge_base ()
@@ -1824,7 +1757,7 @@ _git_config ()
                local remote="${prev#remote.}"
                remote="${remote%.fetch}"
                if [ -z "$cur" ]; then
-                       COMPREPLY=("refs/heads/")
+                       __gitcompadd "refs/heads/" "" "" ""
                        return
                fi
                __gitcomp_nl "$(__git_refs_remotes "$remote")"
@@ -1884,7 +1817,6 @@ _git_config ()
                return
                ;;
        *.*)
-               COMPREPLY=()
                return
                ;;
        esac
@@ -2265,7 +2197,6 @@ _git_remote ()
                __gitcomp "$c"
                ;;
        *)
-               COMPREPLY=()
                ;;
        esac
 }
@@ -2381,8 +2312,6 @@ _git_stash ()
                *)
                        if [ -z "$(__git_find_on_cmdline "$save_opts")" ]; then
                                __gitcomp "$subcommands"
-                       else
-                               COMPREPLY=()
                        fi
                        ;;
                esac
@@ -2395,14 +2324,12 @@ _git_stash ()
                        __gitcomp "--index --quiet"
                        ;;
                show,--*|drop,--*|branch,--*)
-                       COMPREPLY=()
                        ;;
                show,*|apply,*|drop,*|pop,*|branch,*)
                        __gitcomp_nl "$(git --git-dir="$(__gitdir)" stash list \
                                        | sed -n -e 's/:.*//p')"
                        ;;
                *)
-                       COMPREPLY=()
                        ;;
                esac
        fi
@@ -2412,7 +2339,7 @@ _git_submodule ()
 {
        __git_has_doubledash && return
 
-       local subcommands="add status init update summary foreach sync"
+       local subcommands="add status init deinit update summary foreach sync"
        if [ -z "$(__git_find_on_cmdline "$subcommands")" ]; then
                case "$cur" in
                --*)
@@ -2519,7 +2446,6 @@ _git_svn ()
                        __gitcomp "--revision= --parent"
                        ;;
                *)
-                       COMPREPLY=()
                        ;;
                esac
        fi
@@ -2544,13 +2470,10 @@ _git_tag ()
 
        case "$prev" in
        -m|-F)
-               COMPREPLY=()
                ;;
        -*|tag)
                if [ $f = 1 ]; then
                        __gitcomp_nl "$(__git_tags)"
-               else
-                       COMPREPLY=()
                fi
                ;;
        *)
@@ -2707,14 +2630,6 @@ if [[ -n ${ZSH_VERSION-} ]]; then
 
        compdef _git git gitk
        return
-elif [[ -n ${BASH_VERSION-} ]]; then
-       if ((${BASH_VERSINFO[0]} < 4)); then
-               # compopt is not supported
-               __git_index_file_list_filter ()
-               {
-                       __git_index_file_list_filter_compat
-               }
-       fi
 fi
 
 __git_func_wrap ()