Merge branch 'jc/diff-format-doc'
[gitweb.git] / contrib / completion / git-completion.bash
index 865f31b66f932fa4356b6d004e85ee2eaa539afc..c21190d7510c173482b7784f9e8c4bf0b56cb1e1 100644 (file)
@@ -1,5 +1,3 @@
-#!bash
-#
 # bash/zsh completion support for core Git.
 #
 # Copyright (C) 2006,2007 Shawn O. Pearce <spearce@spearce.org>
@@ -13,6 +11,7 @@
 #    *) .git/remotes file names
 #    *) git 'subcommands'
 #    *) tree paths within 'ref:path/to/file' expressions
+#    *) file paths within current working directory and index
 #    *) common --long-options
 #
 # To use these routines:
 #        source ~/.git-completion.bash
 #    3) Consider changing your PS1 to also show the current branch,
 #       see git-prompt.sh for details.
-
-if [[ -n ${ZSH_VERSION-} ]]; then
-       autoload -U +X bashcompinit && bashcompinit
-fi
+#
+# If you use complex aliases of form '!f() { ... }; f', you can use the null
+# command ':' as the first command in the function body to declare the desired
+# completion style.  For example '!f() { : git commit ; ... }; f' will
+# tell the completion to use commit completion.  This also works with aliases
+# of form "!sh -c '...'".  For example, "!sh -c ': git commit ; ... '".
 
 case "$COMP_WORDBREAKS" in
 *:*) : great ;;
@@ -36,8 +37,6 @@ esac
 # returns location of .git repo
 __gitdir ()
 {
-       # Note: this function is duplicated in git-prompt.sh
-       # When updating it, make sure you update the other one to match.
        if [ -z "${1-}" ]; then
                if [ -n "${__git_dir-}" ]; then
                        echo "$__git_dir"
@@ -56,19 +55,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+
@@ -169,7 +155,6 @@ __git_reassemble_comp_words_by_ref()
 }
 
 if ! type _get_comp_words_by_ref >/dev/null 2>&1; then
-if [[ -z ${ZSH_VERSION:+set} ]]; then
 _get_comp_words_by_ref ()
 {
        local exclude cur_ words_ cword_
@@ -197,36 +182,26 @@ _get_comp_words_by_ref ()
                shift
        done
 }
-else
-_get_comp_words_by_ref ()
+fi
+
+__gitcompappend ()
 {
-       while [ $# -gt 0 ]; do
-               case "$1" in
-               cur)
-                       cur=${COMP_WORDS[COMP_CWORD]}
-                       ;;
-               prev)
-                       prev=${COMP_WORDS[COMP_CWORD-1]}
-                       ;;
-               words)
-                       words=("${COMP_WORDS[@]}")
-                       ;;
-               cword)
-                       cword=$COMP_CWORD
-                       ;;
-               -n)
-                       # assume COMP_WORDBREAKS is already set sanely
-                       shift
-                       ;;
-               esac
-               shift
+       local i=${#COMPREPLY[@]}
+       for x in $1; do
+               if [[ "$x" == "$3"* ]]; then
+                       COMPREPLY[i++]="$2$x$4"
+               fi
        done
 }
-fi
-fi
 
-# Generates completion reply with compgen, appending a space to possible
-# completion words, if necessary.
+__gitcompadd ()
+{
+       COMPREPLY=()
+       __gitcompappend "$@"
+}
+
+# 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).
@@ -238,19 +213,33 @@ __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.
+# Variation of __gitcomp_nl () that appends to the existing list of
+# completion candidates, COMPREPLY.
+__gitcomp_nl_append ()
+{
+       local IFS=$'\n'
+       __gitcompappend "$1" "${2-}" "${3-$cur}" "${4- }"
+}
+
+# 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).
@@ -259,9 +248,67 @@ __gitcomp ()
 #    the default space (optional).  If specified but empty, nothing is
 #    appended.
 __gitcomp_nl ()
+{
+       COMPREPLY=()
+       __gitcomp_nl_append "$@"
+}
+
+# Generates completion reply with compgen from newline-separated possible
+# completion filenames.
+# It accepts 1 to 3 arguments:
+# 1: List of possible completion filenames, separated by a single newline.
+# 2: A directory prefix to be added to each possible completion filename
+#    (optional).
+# 3: Generate possible completion matches for this word (optional).
+__gitcomp_file ()
 {
        local IFS=$'\n'
-       COMPREPLY=($(compgen -P "${2-}" -S "${4- }" -W "$1" -- "${3-$cur}"))
+
+       # XXX does not work when the directory prefix contains a tilde,
+       # since tilde expansion is not applied.
+       # This means that COMPREPLY will be empty and Bash default
+       # completion will be used.
+       __gitcompadd "$1" "${2-}" "${3-$cur}" ""
+
+       # 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
+}
+
+# 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 ()
+{
+       if [ "$2" == "--committable" ]; then
+               git -C "$1" diff-index --name-only --relative HEAD
+       else
+               # NOTE: $2 is not quoted in order to support multiple options
+               git -C "$1" ls-files --exclude-standard $2
+       fi 2>/dev/null
+}
+
+
+# __git_index_files accepts 1 or 2 arguments:
+# 1: Options to pass to ls-files (required).
+# 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_index_files ()
+{
+       local dir="$(__gitdir)" root="${2-.}" file
+
+       if [ -d "$dir" ]; then
+               __git_ls_files_helper "$root" "$1" |
+               while read -r file; do
+                       case "$file" in
+                       ?*/*) echo "${file%%/*}" ;;
+                       *) echo "$file" ;;
+                       esac
+               done | sort | uniq
+       fi
 }
 
 __git_heads ()
@@ -321,7 +368,7 @@ __git_refs ()
                                if [[ "$ref" == "$cur"* ]]; then
                                        echo "$ref"
                                fi
-                       done | uniq -u
+                       done | sort | uniq -u
                fi
                return
        fi
@@ -336,14 +383,9 @@ __git_refs ()
                done
                ;;
        *)
-               git ls-remote "$dir" HEAD ORIG_HEAD 'refs/tags/*' 'refs/heads/*' 'refs/remotes/*' 2>/dev/null | \
-               while read -r hash i; do
-                       case "$i" in
-                       *^{}) ;;
-                       refs/*) echo "${i#refs/*/}" ;;
-                       *) echo "$i" ;;
-                       esac
-               done
+               echo "HEAD"
+               git for-each-ref --format="%(refname:short)" -- \
+                       "refs/remotes/$dir/" 2>/dev/null | sed -e "s#^$dir/##"
                ;;
        esac
 }
@@ -428,7 +470,7 @@ __git_complete_revlist_file ()
                *)   pfx="$ref:$pfx" ;;
                esac
 
-               __gitcomp_nl "$(git --git-dir="$(__gitdir)" ls-tree "$ls" \
+               __gitcomp_nl "$(git --git-dir="$(__gitdir)" ls-tree "$ls" 2>/dev/null \
                                | sed '/^100... blob /{
                                           s,^.*        ,,
                                           s,$, ,
@@ -461,6 +503,25 @@ __git_complete_revlist_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"
+
+       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
@@ -486,7 +547,6 @@ __git_complete_remote_or_refspec ()
                        case "$cmd" in
                        push) no_complete_refspec=1 ;;
                        fetch)
-                               COMPREPLY=()
                                return
                                ;;
                        *) ;;
@@ -502,7 +562,6 @@ __git_complete_remote_or_refspec ()
                return
        fi
        if [ $no_complete_refspec = 1 ]; then
-               COMPREPLY=()
                return
        fi
        [ "$remote" = "." ] && remote=
@@ -562,10 +621,19 @@ __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 help -a|egrep '^  [a-zA-Z0-9]')
+       for i in $(__git_commands)
        do
                case $i in
                *--*)             : helper pattern;;
@@ -585,7 +653,7 @@ __git_list_porcelain_commands ()
 {
        local i IFS=" "$'\n'
        __git_compute_all_commands
-       for i in "help" $__git_all_commands
+       for i in $__git_all_commands
        do
                case $i in
                *--*)             : helper pattern;;
@@ -594,6 +662,8 @@ __git_list_porcelain_commands ()
                archimport)       : import;;
                cat-file)         : plumbing;;
                check-attr)       : plumbing;;
+               check-ignore)     : plumbing;;
+               check-mailmap)    : plumbing;;
                check-ref-format) : plumbing;;
                checkout-index)   : plumbing;;
                commit-tree)      : plumbing;;
@@ -618,7 +688,6 @@ __git_list_porcelain_commands ()
                index-pack)       : plumbing;;
                init-db)          : deprecated;;
                local-fetch)      : plumbing;;
-               lost-found)       : infrequent;;
                ls-files)         : plumbing;;
                ls-remote)        : plumbing;;
                ls-tree)          : plumbing;;
@@ -632,14 +701,12 @@ __git_list_porcelain_commands ()
                pack-refs)        : plumbing;;
                parse-remote)     : plumbing;;
                patch-id)         : plumbing;;
-               peek-remote)      : plumbing;;
                prune)            : plumbing;;
                prune-packed)     : plumbing;;
                quiltimport)      : import;;
                read-tree)        : plumbing;;
                receive-pack)     : plumbing;;
                remote-*)         : transport;;
-               repo-config)      : deprecated;;
                rerere)           : plumbing;;
                rev-list)         : plumbing;;
                rev-parse)        : plumbing;;
@@ -652,7 +719,6 @@ __git_list_porcelain_commands ()
                ssh-*)            : transport;;
                stripspace)       : plumbing;;
                symbolic-ref)     : plumbing;;
-               tar-tree)         : deprecated;;
                unpack-file)      : plumbing;;
                unpack-objects)   : plumbing;;
                update-index)     : plumbing;;
@@ -718,6 +784,10 @@ __git_aliased_command ()
                -*)     : option ;;
                *=*)    : setting env ;;
                git)    : git itself ;;
+               \(\))   : skip parens of shell function definition ;;
+               {)      : skip start of shell helper function ;;
+               :)      : skip null command ;;
+               \'*)    : skip opening quote after sh -c ;;
                *)
                        echo "$word"
                        return
@@ -753,6 +823,43 @@ __git_has_doubledash ()
        return 1
 }
 
+# Try to count non option arguments passed on the command line for the
+# specified git command.
+# When options are used, it is necessary to use the special -- option to
+# tell the implementation were non option arguments begin.
+# XXX this can not be improved, since options can appear everywhere, as
+# an example:
+#      git mv x -n y
+#
+# __git_count_arguments requires 1 argument: the git command executed.
+__git_count_arguments ()
+{
+       local word i c=0
+
+       # Skip "git" (first argument)
+       for ((i=1; i < ${#words[@]}; i++)); do
+               word="${words[i]}"
+
+               case "$word" in
+                       --)
+                               # Good; we can assume that the following are only non
+                               # option arguments.
+                               ((c = 0))
+                               ;;
+                       "$1")
+                               # Skip the specified git command and discard git
+                               # main options
+                               ((c = 0))
+                               ;;
+                       ?*)
+                               ((c++))
+                               ;;
+               esac
+       done
+
+       printf "%d" $c
+}
+
 __git_whitespacelist="nowarn warn error error-all fix"
 
 _git_am ()
@@ -776,7 +883,6 @@ _git_am ()
                        "
                return
        esac
-       COMPREPLY=()
 }
 
 _git_apply ()
@@ -796,13 +902,10 @@ _git_apply ()
                        "
                return
        esac
-       COMPREPLY=()
 }
 
 _git_add ()
 {
-       __git_has_doubledash && return
-
        case "$cur" in
        --*)
                __gitcomp "
@@ -811,7 +914,9 @@ _git_add ()
                        "
                return
        esac
-       COMPREPLY=()
+
+       # XXX should we check for --update and --all options ?
+       __git_complete_index_file "--others --modified --directory --no-empty-directory"
 }
 
 _git_archive ()
@@ -856,7 +961,6 @@ _git_bisect ()
                __gitcomp_nl "$(__git_refs)"
                ;;
        *)
-               COMPREPLY=()
                ;;
        esac
 }
@@ -949,9 +1053,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)"
@@ -961,15 +1070,15 @@ _git_cherry_pick ()
 
 _git_clean ()
 {
-       __git_has_doubledash && return
-
        case "$cur" in
        --*)
                __gitcomp "--dry-run --quiet"
                return
                ;;
        esac
-       COMPREPLY=()
+
+       # XXX should we check for -x option ?
+       __git_complete_index_file "--others --directory"
 }
 
 _git_clone ()
@@ -989,16 +1098,22 @@ _git_clone ()
                        --upload-pack
                        --template=
                        --depth
+                       --single-branch
+                       --branch
                        "
                return
                ;;
        esac
-       COMPREPLY=()
 }
 
 _git_commit ()
 {
-       __git_has_doubledash && return
+       case "$prev" in
+       -c|-C)
+               __gitcomp_nl "$(__git_refs)" "" "${cur}"
+               return
+               ;;
+       esac
 
        case "$cur" in
        --cleanup=*)
@@ -1027,7 +1142,13 @@ _git_commit ()
                        "
                return
        esac
-       COMPREPLY=()
+
+       if git rev-parse --verify --quiet HEAD >/dev/null; then
+               __git_complete_index_file "--committable"
+       else
+               # This is the first commit
+               __git_complete_index_file "--cached"
+       fi
 }
 
 _git_describe ()
@@ -1043,20 +1164,23 @@ _git_describe ()
        __gitcomp_nl "$(__git_refs)"
 }
 
+__git_diff_algorithms="myers minimal patience histogram"
+
 __git_diff_common_options="--stat --numstat --shortstat --summary
                        --patch-with-stat --name-only --name-status --color
                        --no-color --color-words --no-renames --check
                        --full-index --binary --abbrev --diff-filter=
                        --find-copies-harder
                        --text --ignore-space-at-eol --ignore-space-change
-                       --ignore-all-space --exit-code --quiet --ext-diff
-                       --no-ext-diff
+                       --ignore-all-space --ignore-blank-lines --exit-code
+                       --quiet --ext-diff --no-ext-diff
                        --no-prefix --src-prefix= --dst-prefix=
                        --inter-hunk-context=
-                       --patience
-                       --raw
+                       --patience --histogram --minimal
+                       --raw --word-diff
                        --dirstat --dirstat= --dirstat-by-file
                        --dirstat-by-file= --cumulative
+                       --diff-algorithm=
 "
 
 _git_diff ()
@@ -1064,6 +1188,10 @@ _git_diff ()
        __git_has_doubledash && return
 
        case "$cur" in
+       --diff-algorithm=*)
+               __gitcomp "$__git_diff_algorithms" "" "${cur##--diff-algorithm=}"
+               return
+               ;;
        --*)
                __gitcomp "--cached --staged --pickaxe-all --pickaxe-regex
                        --base --ours --theirs --no-index
@@ -1075,8 +1203,8 @@ _git_diff ()
        __git_complete_revlist_file
 }
 
-__git_mergetools_common="diffuse ecmerge emerge kdiff3 meld opendiff
-                       tkdiff vimdiff gvimdiff xxdiff araxis p4merge bc3 codecompare
+__git_mergetools_common="diffuse diffmerge ecmerge emerge kdiff3 meld opendiff
+                       tkdiff vimdiff gvimdiff xxdiff araxis p4merge bc codecompare
 "
 
 _git_difftool ()
@@ -1097,17 +1225,23 @@ _git_difftool ()
                return
                ;;
        esac
-       __git_complete_file
+       __git_complete_revlist_file
 }
 
+__git_fetch_recurse_submodules="yes on-demand no"
+
 __git_fetch_options="
        --quiet --verbose --append --upload-pack --force --keep --depth=
-       --tags --no-tags --all --prune --dry-run
+       --tags --no-tags --all --prune --dry-run --recurse-submodules=
 "
 
 _git_fetch ()
 {
        case "$cur" in
+       --recurse-submodules=*)
+               __gitcomp "$__git_fetch_recurse_submodules" "" "${cur##--recurse-submodules=}"
+               return
+               ;;
        --*)
                __gitcomp "$__git_fetch_options"
                return
@@ -1117,11 +1251,12 @@ _git_fetch ()
 }
 
 __git_format_patch_options="
-       --stdout --attach --no-attach --thread --thread= --output-directory
+       --stdout --attach --no-attach --thread --thread= --no-thread
        --numbered --start-number --numbered-files --keep-subject --signoff
        --signature --no-signature --in-reply-to= --cc= --full-index --binary
        --not --all --cover-letter --no-prefix --src-prefix= --dst-prefix=
        --inline --suffix= --ignore-if-in-upstream --subject-prefix=
+       --output-directory --reroll-count --to= --quiet --notes
 "
 
 _git_format_patch ()
@@ -1152,7 +1287,6 @@ _git_fsck ()
                return
                ;;
        esac
-       COMPREPLY=()
 }
 
 _git_gc ()
@@ -1163,7 +1297,6 @@ _git_gc ()
                return
                ;;
        esac
-       COMPREPLY=()
 }
 
 _git_gitk ()
@@ -1240,13 +1373,10 @@ _git_init ()
                return
                ;;
        esac
-       COMPREPLY=()
 }
 
 _git_ls_files ()
 {
-       __git_has_doubledash && return
-
        case "$cur" in
        --*)
                __gitcomp "--cached --deleted --modified --others --ignored
@@ -1259,7 +1389,10 @@ _git_ls_files ()
                return
                ;;
        esac
-       COMPREPLY=()
+
+       # XXX ignore options like --modified and always suggest all cached
+       # files.
+       __git_complete_index_file "--cached"
 }
 
 _git_ls_remote ()
@@ -1292,7 +1425,7 @@ __git_log_gitk_options="
 # Options that go well for log and shortlog (not gitk)
 __git_log_shortlog_options="
        --author= --committer= --grep=
-       --all-match
+       --all-match --invert-grep
 "
 
 __git_log_pretty_formats="oneline short medium full fuller email raw format:"
@@ -1331,6 +1464,7 @@ _git_log ()
                        --abbrev-commit --abbrev=
                        --relative-date --date=
                        --pretty= --format= --oneline
+                       --show-signature
                        --cherry-pick
                        --graph
                        --decorate --decorate=
@@ -1346,9 +1480,12 @@ _git_log ()
        __git_complete_revlist
 }
 
+# Common merge options shared by git-merge(1) and git-pull(1).
 __git_merge_options="
        --no-commit --no-stat --log --no-log --squash --strategy
        --commit --stat --no-squash --ff --no-ff --ff-only --edit --no-edit
+       --verify-signatures --no-verify-signatures --gpg-sign
+       --quiet --verbose --progress --no-progress
 "
 
 _git_merge ()
@@ -1357,7 +1494,8 @@ _git_merge ()
 
        case "$cur" in
        --*)
-               __gitcomp "$__git_merge_options"
+               __gitcomp "$__git_merge_options
+                       --rerere-autoupdate --no-rerere-autoupdate --abort"
                return
        esac
        __gitcomp_nl "$(__git_refs)"
@@ -1375,11 +1513,16 @@ _git_mergetool ()
                return
                ;;
        esac
-       COMPREPLY=()
 }
 
 _git_merge_base ()
 {
+       case "$cur" in
+       --*)
+               __gitcomp "--octopus --independent --is-ancestor --fork-point"
+               return
+               ;;
+       esac
        __gitcomp_nl "$(__git_refs)"
 }
 
@@ -1391,7 +1534,14 @@ _git_mv ()
                return
                ;;
        esac
-       COMPREPLY=()
+
+       if [ $(__git_count_arguments "mv") -gt 0 ]; then
+               # We need to show both cached and untracked files (including
+               # empty directories) since this may not be the last argument.
+               __git_complete_index_file "--cached --others --directory"
+       else
+               __git_complete_index_file "--cached"
+       fi
 }
 
 _git_name_rev ()
@@ -1451,6 +1601,10 @@ _git_pull ()
        __git_complete_strategy && return
 
        case "$cur" in
+       --recurse-submodules=*)
+               __gitcomp "$__git_fetch_recurse_submodules" "" "${cur##--recurse-submodules=}"
+               return
+               ;;
        --*)
                __gitcomp "
                        --rebase --no-rebase
@@ -1463,22 +1617,55 @@ _git_pull ()
        __git_complete_remote_or_refspec
 }
 
+__git_push_recurse_submodules="check on-demand"
+
+__git_complete_force_with_lease ()
+{
+       local cur_=$1
+
+       case "$cur_" in
+       --*=)
+               ;;
+       *:*)
+               __gitcomp_nl "$(__git_refs)" "" "${cur_#*:}"
+               ;;
+       *)
+               __gitcomp_nl "$(__git_refs)" "" "$cur_"
+               ;;
+       esac
+}
+
 _git_push ()
 {
        case "$prev" in
        --repo)
                __gitcomp_nl "$(__git_remotes)"
                return
+               ;;
+       --recurse-submodules)
+               __gitcomp "$__git_push_recurse_submodules"
+               return
+               ;;
        esac
        case "$cur" in
        --repo=*)
                __gitcomp_nl "$(__git_remotes)" "" "${cur##--repo=}"
                return
                ;;
+       --recurse-submodules=*)
+               __gitcomp "$__git_push_recurse_submodules" "" "${cur##--recurse-submodules=}"
+               return
+               ;;
+       --force-with-lease=*)
+               __git_complete_force_with_lease "${cur##--force-with-lease=}"
+               return
+               ;;
        --*)
                __gitcomp "
                        --all --mirror --tags --dry-run --force --verbose
+                       --quiet --prune --delete --follow-tags
                        --receive-pack= --repo= --set-upstream
+                       --force-with-lease --force-with-lease= --recurse-submodules=
                "
                return
                ;;
@@ -1505,7 +1692,8 @@ _git_rebase ()
                        --preserve-merges --stat --no-stat
                        --committer-date-is-author-date --ignore-date
                        --ignore-whitespace --whitespace=
-                       --autosquash
+                       --autosquash --fork-point --no-fork-point
+                       --autostash
                        "
 
                return
@@ -1582,7 +1770,7 @@ __git_config_get_set_variables ()
        while [ $c -gt 1 ]; do
                word="${words[c]}"
                case "$word" in
-               --global|--system|--file=*)
+               --system|--global|--local|--file=*)
                        config_file="$word"
                        break
                        ;;
@@ -1609,7 +1797,7 @@ __git_config_get_set_variables ()
 _git_config ()
 {
        case "$prev" in
-       branch.*.remote)
+       branch.*.remote|branch.*.pushremote)
                __gitcomp_nl "$(__git_remotes)"
                return
                ;;
@@ -1617,11 +1805,19 @@ _git_config ()
                __gitcomp_nl "$(__git_refs)"
                return
                ;;
+       branch.*.rebase)
+               __gitcomp "false true"
+               return
+               ;;
+       remote.pushdefault)
+               __gitcomp_nl "$(__git_remotes)"
+               return
+               ;;
        remote.*.fetch)
                local remote="${prev#remote.}"
                remote="${remote%.fetch}"
                if [ -z "$cur" ]; then
-                       COMPREPLY=("refs/heads/")
+                       __gitcomp_nl "refs/heads/" "" "" ""
                        return
                fi
                __gitcomp_nl "$(__git_refs_remotes "$remote")"
@@ -1656,6 +1852,10 @@ _git_config ()
                        "
                return
                ;;
+       diff.submodule)
+               __gitcomp "log short"
+               return
+               ;;
        help.format)
                __gitcomp "man info web html"
                return
@@ -1676,19 +1876,22 @@ _git_config ()
                __gitcomp "$__git_send_email_suppresscc_options"
                return
                ;;
+       sendemail.transferencoding)
+               __gitcomp "7bit 8bit quoted-printable base64"
+               return
+               ;;
        --get|--get-all|--unset|--unset-all)
                __gitcomp_nl "$(__git_config_get_set_variables)"
                return
                ;;
        *.*)
-               COMPREPLY=()
                return
                ;;
        esac
        case "$cur" in
        --*)
                __gitcomp "
-                       --global --system --file=
+                       --system --global --local --file=
                        --list --replace-all
                        --get --get-all --get-regexp
                        --add --unset --unset-all
@@ -1698,12 +1901,13 @@ _git_config ()
                ;;
        branch.*.*)
                local pfx="${cur%.*}." cur_="${cur##*.}"
-               __gitcomp "remote merge mergeoptions rebase" "$pfx" "$cur_"
+               __gitcomp "remote pushremote merge mergeoptions rebase" "$pfx" "$cur_"
                return
                ;;
        branch.*)
                local pfx="${cur%.*}." cur_="${cur#*.}"
                __gitcomp_nl "$(__git_heads)" "$pfx" "$cur_" "."
+               __gitcomp_nl_append $'autosetupmerge\nautosetuprebase\n' "$pfx" "$cur_"
                return
                ;;
        guitool.*.*)
@@ -1746,6 +1950,7 @@ _git_config ()
        remote.*)
                local pfx="${cur%.*}." cur_="${cur#*.}"
                __gitcomp_nl "$(__git_remotes)" "$pfx" "$cur_" "."
+               __gitcomp_nl_append "pushdefault" "$pfx" "$cur_"
                return
                ;;
        url.*.*)
@@ -1829,7 +2034,6 @@ _git_config ()
                core.fileMode
                core.fsyncobjectfiles
                core.gitProxy
-               core.ignoreCygwinFSTricks
                core.ignoreStat
                core.ignorecase
                core.logAllRefUpdates
@@ -1851,22 +2055,25 @@ _git_config ()
                core.whitespace
                core.worktree
                diff.autorefreshindex
-               diff.statGraphWidth
                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.headers
                format.numbered
                format.pretty
@@ -1991,6 +2198,7 @@ _git_config ()
                receive.fsckObjects
                receive.unpackLimit
                receive.updateserverinfo
+               remote.pushdefault
                remotes.
                repack.usedeltabaseoffset
                rerere.autoupdate
@@ -2061,7 +2269,6 @@ _git_remote ()
                __gitcomp "$c"
                ;;
        *)
-               COMPREPLY=()
                ;;
        esac
 }
@@ -2097,15 +2304,14 @@ _git_revert ()
 
 _git_rm ()
 {
-       __git_has_doubledash && return
-
        case "$cur" in
        --*)
                __gitcomp "--cached --dry-run --ignore-unmatch --quiet"
                return
                ;;
        esac
-       COMPREPLY=()
+
+       __git_complete_index_file "--cached"
 }
 
 _git_shortlog ()
@@ -2135,14 +2341,19 @@ _git_show ()
                        " "" "${cur#*=}"
                return
                ;;
+       --diff-algorithm=*)
+               __gitcomp "$__git_diff_algorithms" "" "${cur##--diff-algorithm=}"
+               return
+               ;;
        --*)
                __gitcomp "--pretty= --format= --abbrev-commit --oneline
+                       --show-signature
                        $__git_diff_common_options
                        "
                return
                ;;
        esac
-       __git_complete_file
+       __git_complete_revlist_file
 }
 
 _git_show_branch ()
@@ -2174,8 +2385,6 @@ _git_stash ()
                *)
                        if [ -z "$(__git_find_on_cmdline "$save_opts")" ]; then
                                __gitcomp "$subcommands"
-                       else
-                               COMPREPLY=()
                        fi
                        ;;
                esac
@@ -2188,14 +2397,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
@@ -2205,7 +2412,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
                --*)
@@ -2237,7 +2444,7 @@ _git_svn ()
                        --no-metadata --use-svm-props --use-svnsync-props
                        --log-window-size= --no-checkout --quiet
                        --repack-flags --use-log-author --localtime
-                       --ignore-paths= $remote_opts
+                       --ignore-paths= --include-paths= $remote_opts
                        "
                local init_opts="
                        --template= --shared= --trunk= --tags=
@@ -2312,7 +2519,6 @@ _git_svn ()
                        __gitcomp "--revision= --parent"
                        ;;
                *)
-                       COMPREPLY=()
                        ;;
                esac
        fi
@@ -2337,19 +2543,26 @@ _git_tag ()
 
        case "$prev" in
        -m|-F)
-               COMPREPLY=()
                ;;
        -*|tag)
                if [ $f = 1 ]; then
                        __gitcomp_nl "$(__git_tags)"
-               else
-                       COMPREPLY=()
                fi
                ;;
        *)
                __gitcomp_nl "$(__git_refs)"
                ;;
        esac
+
+       case "$cur" in
+       --*)
+               __gitcomp "
+                       --list --delete --verify --annotate --message --file
+                       --sign --cleanup --local-user --force --column --sort
+                       --contains --points-at
+                       "
+               ;;
+       esac
 }
 
 _git_whatchanged ()
@@ -2365,9 +2578,10 @@ __git_main ()
                i="${words[c]}"
                case "$i" in
                --git-dir=*) __git_dir="${i#--git-dir=}" ;;
+               --git-dir)   ((c++)) ; __git_dir="${words[c]}" ;;
                --bare)      __git_dir="." ;;
                --help) command="help"; break ;;
-               -c) c=$((++c)) ;;
+               -c|--work-tree|--namespace) ((c++)) ;;
                -*) ;;
                *) command="$i"; break ;;
                esac
@@ -2385,6 +2599,7 @@ __git_main ()
                        --exec-path
                        --exec-path=
                        --html-path
+                       --man-path
                        --info-path
                        --work-tree=
                        --namespace=
@@ -2403,6 +2618,7 @@ __git_main ()
 
        local expansion=$(__git_aliased_command "$command")
        if [ -n "$expansion" ]; then
+               words[1]=$expansion
                completion_func="_git_${expansion//-/_}"
                declare -f $completion_func >/dev/null && $completion_func
        fi
@@ -2430,20 +2646,72 @@ __gitk_main ()
        __git_complete_revlist
 }
 
-__git_func_wrap ()
-{
-       if [[ -n ${ZSH_VERSION-} ]]; then
-               emulate -L bash
-               setopt KSH_TYPESET
+if [[ -n ${ZSH_VERSION-} ]]; then
+       echo "WARNING: this script is deprecated, please see git-completion.zsh" 1>&2
 
-               # workaround zsh's bug that leaves 'words' as a special
-               # variable in versions < 4.3.12
-               typeset -h words
+       autoload -U +X compinit && compinit
 
-               # workaround zsh's bug that quotes spaces in the COMPREPLY
-               # array if IFS doesn't contain spaces.
-               typeset -h IFS
-       fi
+       __gitcomp ()
+       {
+               emulate -L zsh
+
+               local cur_="${3-$cur}"
+
+               case "$cur_" in
+               --*=)
+                       ;;
+               *)
+                       local c IFS=$' \t\n'
+                       local -a array
+                       for c in ${=1}; do
+                               c="$c${4-}"
+                               case $c in
+                               --*=*|*.) ;;
+                               *) c="$c " ;;
+                               esac
+                               array[${#array[@]}+1]="$c"
+                       done
+                       compset -P '*[=:]'
+                       compadd -Q -S '' -p "${2-}" -a -- array && _ret=0
+                       ;;
+               esac
+       }
+
+       __gitcomp_nl ()
+       {
+               emulate -L zsh
+
+               local IFS=$'\n'
+               compset -P '*[=:]'
+               compadd -Q -S "${4- }" -p "${2-}" -- ${=1} && _ret=0
+       }
+
+       __gitcomp_file ()
+       {
+               emulate -L zsh
+
+               local IFS=$'\n'
+               compset -P '*[=:]'
+               compadd -Q -p "${2-}" -f -- ${=1} && _ret=0
+       }
+
+       _git ()
+       {
+               local _ret=1 cur cword prev
+               cur=${words[CURRENT]}
+               prev=${words[CURRENT-1]}
+               let cword=CURRENT-1
+               emulate ksh -c __${service}_main
+               let _ret && _default && _ret=0
+               return _ret
+       }
+
+       compdef _git git gitk
+       return
+fi
+
+__git_func_wrap ()
+{
        local cur words cword prev
        _get_comp_words_by_ref -n =: cur words cword prev
        $1