Merge branch 'maint'
[gitweb.git] / contrib / completion / git-completion.bash
index 8431837f9705d9a940819db4fcd51283f433ba1a..d3fec329976c698d55f4873efb2826ed4471919a 100755 (executable)
 #       with the bash.showDirtyState variable, which defaults to true
 #       once GIT_PS1_SHOWDIRTYSTATE is enabled.
 #
+#       You can also see if currently something is stashed, by setting
+#       GIT_PS1_SHOWSTASHSTATE to a nonempty value. If something is stashed,
+#       then a '$' will be shown next to the branch name.
+#
+#       If you would like to see if there're untracked files, then you can
+#       set GIT_PS1_SHOWUNTRACKEDFILES to a nonempty value. If there're
+#       untracked files, then a '%' will be shown next to the branch name.
+#
 # To submit patches:
 #
 #    *) Read Documentation/SubmittingPatches
@@ -62,7 +70,7 @@ esac
 __gitdir ()
 {
        if [ -z "${1-}" ]; then
-               if [ -n "$__git_dir" ]; then
+               if [ -n "${__git_dir-}" ]; then
                        echo "$__git_dir"
                elif [ -d .git ]; then
                        echo .git
@@ -80,68 +88,91 @@ __gitdir ()
 # returns text to add to bash PS1 prompt (includes branch name)
 __git_ps1 ()
 {
-       local g="$(git rev-parse --git-dir 2>/dev/null)"
+       local g="$(__gitdir)"
        if [ -n "$g" ]; then
                local r
                local b
-               if [ -d "$g/rebase-apply" ]
-               then
-                       if test -f "$g/rebase-apply/rebasing"
-                       then
-                               r="|REBASE"
-                       elif test -f "$g/rebase-apply/applying"
-                       then
-                               r="|AM"
-                       else
-                               r="|AM/REBASE"
-                       fi
-                       b="$(git symbolic-ref HEAD 2>/dev/null)"
-               elif [ -f "$g/rebase-merge/interactive" ]
-               then
+               if [ -f "$g/rebase-merge/interactive" ]; then
                        r="|REBASE-i"
                        b="$(cat "$g/rebase-merge/head-name")"
-               elif [ -d "$g/rebase-merge" ]
-               then
+               elif [ -d "$g/rebase-merge" ]; then
                        r="|REBASE-m"
                        b="$(cat "$g/rebase-merge/head-name")"
-               elif [ -f "$g/MERGE_HEAD" ]
-               then
-                       r="|MERGING"
-                       b="$(git symbolic-ref HEAD 2>/dev/null)"
                else
-                       if [ -f "$g/BISECT_LOG" ]
-                       then
-                               r="|BISECTING"
-                       fi
-                       if ! b="$(git symbolic-ref HEAD 2>/dev/null)"
-                       then
-                               if ! b="$(git describe --exact-match HEAD 2>/dev/null)"
-                               then
-                                       b="$(cut -c1-7 "$g/HEAD")..."
+                       if [ -d "$g/rebase-apply" ]; then
+                               if [ -f "$g/rebase-apply/rebasing" ]; then
+                                       r="|REBASE"
+                               elif [ -f "$g/rebase-apply/applying" ]; then
+                                       r="|AM"
+                               else
+                                       r="|AM/REBASE"
                                fi
+                       elif [ -f "$g/MERGE_HEAD" ]; then
+                               r="|MERGING"
+                       elif [ -f "$g/BISECT_LOG" ]; then
+                               r="|BISECTING"
                        fi
+
+                       b="$(git symbolic-ref HEAD 2>/dev/null)" || {
+
+                               b="$(
+                               case "${GIT_PS1_DESCRIBE_STYLE-}" in
+                               (contains)
+                                       git describe --contains HEAD ;;
+                               (branch)
+                                       git describe --contains --all HEAD ;;
+                               (describe)
+                                       git describe HEAD ;;
+                               (* | default)
+                                       git describe --exact-match HEAD ;;
+                               esac 2>/dev/null)" ||
+
+                               b="$(cut -c1-7 "$g/HEAD" 2>/dev/null)..." ||
+                               b="unknown"
+                               b="($b)"
+                       }
                fi
 
                local w
                local i
+               local s
+               local u
+               local c
 
-               if test -n "${GIT_PS1_SHOWDIRTYSTATE-}"; then
-                       if test "$(git config --bool bash.showDirtyState)" != "false"; then
-                               git diff --no-ext-diff --ignore-submodules \
-                                       --quiet --exit-code || w="*"
-                               if git rev-parse --quiet --verify HEAD >/dev/null; then
-                                       git diff-index --cached --quiet \
-                                               --ignore-submodules HEAD -- || i="+"
-                               else
-                                       i="#"
+               if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then
+                       if [ "true" = "$(git rev-parse --is-bare-repository 2>/dev/null)" ]; then
+                               c="BARE:"
+                       else
+                               b="GIT_DIR!"
+                       fi
+               elif [ "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]; then
+                       if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ]; then
+                               if [ "$(git config --bool bash.showDirtyState)" != "false" ]; then
+                                       git diff --no-ext-diff --ignore-submodules \
+                                               --quiet --exit-code || w="*"
+                                       if git rev-parse --quiet --verify HEAD >/dev/null; then
+                                               git diff-index --cached --quiet \
+                                                       --ignore-submodules HEAD -- || i="+"
+                                       else
+                                               i="#"
+                                       fi
                                fi
                        fi
+                       if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ]; then
+                               git rev-parse --verify refs/stash >/dev/null 2>&1 && s="$"
+                       fi
+
+                       if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ]; then
+                          if [ -n "$(git ls-files --others --exclude-standard)" ]; then
+                             u="%"
+                          fi
+                       fi
                fi
 
                if [ -n "${1-}" ]; then
-                       printf "$1" "${b##refs/heads/}$w$i$r"
+                       printf "$1" "$c${b##refs/heads/}$w$i$s$u$r"
                else
-                       printf " (%s)" "${b##refs/heads/}$w$i$r"
+                       printf " (%s)" "$c${b##refs/heads/}$w$i$s$u$r"
                fi
        fi
 }
@@ -287,19 +318,15 @@ __git_remotes ()
                echo ${i#$d/remotes/}
        done
        [ "$ngoff" ] && shopt -u nullglob
-       for i in $(git --git-dir="$d" config --list); do
-               case "$i" in
-               remote.*.url=*)
-                       i="${i#remote.}"
-                       echo "${i/.url=*/}"
-                       ;;
-               esac
+       for i in $(git --git-dir="$d" config --get-regexp 'remote\..*\.url' 2>/dev/null); do
+               i="${i#remote.}"
+               echo "${i/.url*/}"
        done
 }
 
 __git_merge_strategies ()
 {
-       if [ -n "$__git_merge_strategylist" ]; then
+       if [ -n "${__git_merge_strategylist-}" ]; then
                echo "$__git_merge_strategylist"
                return
        fi
@@ -383,14 +410,93 @@ __git_complete_revlist ()
        esac
 }
 
+__git_complete_remote_or_refspec ()
+{
+       local cmd="${COMP_WORDS[1]}"
+       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local i c=2 remote="" pfx="" lhs=1 no_complete_refspec=0
+       while [ $c -lt $COMP_CWORD ]; do
+               i="${COMP_WORDS[c]}"
+               case "$i" in
+               --all|--mirror) [ "$cmd" = "push" ] && no_complete_refspec=1 ;;
+               -*) ;;
+               *) remote="$i"; break ;;
+               esac
+               c=$((++c))
+       done
+       if [ -z "$remote" ]; then
+               __gitcomp "$(__git_remotes)"
+               return
+       fi
+       if [ $no_complete_refspec = 1 ]; then
+               COMPREPLY=()
+               return
+       fi
+       [ "$remote" = "." ] && remote=
+       case "$cur" in
+       *:*)
+               case "$COMP_WORDBREAKS" in
+               *:*) : great ;;
+               *)   pfx="${cur%%:*}:" ;;
+               esac
+               cur="${cur#*:}"
+               lhs=0
+               ;;
+       +*)
+               pfx="+"
+               cur="${cur#+}"
+               ;;
+       esac
+       case "$cmd" in
+       fetch)
+               if [ $lhs = 1 ]; then
+                       __gitcomp "$(__git_refs2 "$remote")" "$pfx" "$cur"
+               else
+                       __gitcomp "$(__git_refs)" "$pfx" "$cur"
+               fi
+               ;;
+       pull)
+               if [ $lhs = 1 ]; then
+                       __gitcomp "$(__git_refs "$remote")" "$pfx" "$cur"
+               else
+                       __gitcomp "$(__git_refs)" "$pfx" "$cur"
+               fi
+               ;;
+       push)
+               if [ $lhs = 1 ]; then
+                       __gitcomp "$(__git_refs)" "$pfx" "$cur"
+               else
+                       __gitcomp "$(__git_refs "$remote")" "$pfx" "$cur"
+               fi
+               ;;
+       esac
+}
+
+__git_complete_strategy ()
+{
+       case "${COMP_WORDS[COMP_CWORD-1]}" in
+       -s|--strategy)
+               __gitcomp "$(__git_merge_strategies)"
+               return 0
+       esac
+       local cur="${COMP_WORDS[COMP_CWORD]}"
+       case "$cur" in
+       --strategy=*)
+               __gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}"
+               return 0
+               ;;
+       esac
+       return 1
+}
+
 __git_all_commands ()
 {
-       if [ -n "$__git_all_commandlist" ]; then
+       if [ -n "${__git_all_commandlist-}" ]; then
                echo "$__git_all_commandlist"
                return
        fi
        local i IFS=" "$'\n'
-       for i in $(git help -a|egrep '^ ')
+       for i in $(git help -a|egrep '^  [a-zA-Z0-9]')
        do
                case $i in
                *--*)             : helper pattern;;
@@ -403,7 +509,7 @@ __git_all_commandlist="$(__git_all_commands 2>/dev/null)"
 
 __git_porcelain_commands ()
 {
-       if [ -n "$__git_porcelain_commandlist" ]; then
+       if [ -n "${__git_porcelain_commandlist-}" ]; then
                echo "$__git_porcelain_commandlist"
                return
        fi
@@ -495,11 +601,11 @@ __git_porcelain_commandlist="$(__git_porcelain_commands 2>/dev/null)"
 __git_aliases ()
 {
        local i IFS=$'\n'
-       for i in $(git --git-dir="$(__gitdir)" config --list); do
+       for i in $(git --git-dir="$(__gitdir)" config --get-regexp "alias\..*" 2>/dev/null); do
                case "$i" in
                alias.*)
                        i="${i#alias.}"
-                       echo "${i/=*/}"
+                       echo "${i/ */}"
                        ;;
                esac
        done
@@ -518,8 +624,8 @@ __git_aliased_command ()
        done
 }
 
-# __git_find_subcommand requires 1 argument
-__git_find_subcommand ()
+# __git_find_on_cmdline requires 1 argument
+__git_find_on_cmdline ()
 {
        local word subcommand c=1
 
@@ -563,8 +669,10 @@ _git_am ()
                ;;
        --*)
                __gitcomp "
-                       --signoff --utf8 --binary --3way --interactive
-                       --whitespace=
+                       --3way --committer-date-is-author-date --ignore-date
+                       --ignore-whitespace --ignore-space-change
+                       --interactive --keep --no-utf8 --signoff --utf8
+                       --whitespace= --scissors
                        "
                return
        esac
@@ -584,6 +692,7 @@ _git_apply ()
                        --stat --numstat --summary --check --index
                        --cached --index-info --reverse --reject --unidiff-zero
                        --apply --no-add --exclude=
+                       --ignore-whitespace --ignore-space-change
                        --whitespace= --inaccurate-eof --verbose
                        "
                return
@@ -635,7 +744,7 @@ _git_bisect ()
        __git_has_doubledash && return
 
        local subcommands="start bad good skip reset visualize replay log run"
-       local subcommand="$(__git_find_subcommand "$subcommands")"
+       local subcommand="$(__git_find_on_cmdline "$subcommands")"
        if [ -z "$subcommand" ]; then
                __gitcomp "$subcommands"
                return
@@ -705,7 +814,21 @@ _git_checkout ()
 {
        __git_has_doubledash && return
 
-       __gitcomp "$(__git_refs)"
+       local cur="${COMP_WORDS[COMP_CWORD]}"
+       case "$cur" in
+       --conflict=*)
+               __gitcomp "diff3 merge" "" "${cur##--conflict=}"
+               ;;
+       --*)
+               __gitcomp "
+                       --quiet --ours --theirs --track --no-track --merge
+                       --conflict= --patch
+                       "
+               ;;
+       *)
+               __gitcomp "$(__git_refs)"
+               ;;
+       esac
 }
 
 _git_cherry ()
@@ -775,6 +898,7 @@ _git_commit ()
                __gitcomp "
                        --all --author= --signoff --verify --no-verify
                        --edit --amend --include --only --interactive
+                       --dry-run
                        "
                return
        esac
@@ -807,6 +931,8 @@ __git_diff_common_options="--stat --numstat --shortstat --summary
                        --inter-hunk-context=
                        --patience
                        --raw
+                       --dirstat --dirstat= --dirstat-by-file
+                       --dirstat-by-file= --cumulative
 "
 
 _git_diff ()
@@ -816,7 +942,7 @@ _git_diff ()
        local cur="${COMP_WORDS[COMP_CWORD]}"
        case "$cur" in
        --*)
-               __gitcomp "--cached --pickaxe-all --pickaxe-regex
+               __gitcomp "--cached --staged --pickaxe-all --pickaxe-regex
                        --base --ours --theirs
                        $__git_diff_common_options
                        "
@@ -826,42 +952,62 @@ _git_diff ()
        __git_complete_file
 }
 
-_git_fetch ()
+__git_mergetools_common="diffuse ecmerge emerge kdiff3 meld opendiff
+                       tkdiff vimdiff gvimdiff xxdiff araxis
+"
+
+_git_difftool ()
 {
        local cur="${COMP_WORDS[COMP_CWORD]}"
+       case "$cur" in
+       --tool=*)
+               __gitcomp "$__git_mergetools_common kompare" "" "${cur##--tool=}"
+               return
+               ;;
+       --*)
+               __gitcomp "--tool="
+               return
+               ;;
+       esac
+       COMPREPLY=()
+}
 
-       if [ "$COMP_CWORD" = 2 ]; then
-               __gitcomp "$(__git_remotes)"
-       else
-               case "$cur" in
-               *:*)
-                       local pfx=""
-                       case "$COMP_WORDBREAKS" in
-                       *:*) : great ;;
-                       *)   pfx="${cur%%:*}:" ;;
-                       esac
-                       __gitcomp "$(__git_refs)" "$pfx" "${cur#*:}"
-                       ;;
-               *)
-                       __gitcomp "$(__git_refs2 "${COMP_WORDS[2]}")"
-                       ;;
-               esac
-       fi
+__git_fetch_options="
+       --quiet --verbose --append --upload-pack --force --keep --depth=
+       --tags --no-tags
+"
+
+_git_fetch ()
+{
+       local cur="${COMP_WORDS[COMP_CWORD]}"
+       case "$cur" in
+       --*)
+               __gitcomp "$__git_fetch_options"
+               return
+               ;;
+       esac
+       __git_complete_remote_or_refspec
 }
 
 _git_format_patch ()
 {
        local cur="${COMP_WORDS[COMP_CWORD]}"
        case "$cur" in
+       --thread=*)
+               __gitcomp "
+                       deep shallow
+                       " "" "${cur##--thread=}"
+               return
+               ;;
        --*)
                __gitcomp "
-                       --stdout --attach --thread
+                       --stdout --attach --no-attach --thread --thread=
                        --output-directory
                        --numbered --start-number
                        --numbered-files
                        --keep-subject
                        --signoff
-                       --in-reply-to=
+                       --in-reply-to= --cc=
                        --full-index --binary
                        --not --all
                        --cover-letter
@@ -875,6 +1021,21 @@ _git_format_patch ()
        __git_complete_revlist
 }
 
+_git_fsck ()
+{
+       local cur="${COMP_WORDS[COMP_CWORD]}"
+       case "$cur" in
+       --*)
+               __gitcomp "
+                       --tags --root --unreachable --cache --no-reflogs --full
+                       --strict --verbose --lost-found
+                       "
+               return
+               ;;
+       esac
+       COMPREPLY=()
+}
+
 _git_gc ()
 {
        local cur="${COMP_WORDS[COMP_CWORD]}"
@@ -901,13 +1062,15 @@ _git_grep ()
                        --extended-regexp --basic-regexp --fixed-strings
                        --files-with-matches --name-only
                        --files-without-match
+                       --max-depth
                        --count
                        --and --or --not --all-match
                        "
                return
                ;;
        esac
-       COMPREPLY=()
+
+       __gitcomp "$(__git_refs)"
 }
 
 _git_help ()
@@ -979,7 +1142,7 @@ _git_ls_tree ()
 __git_log_common_options="
        --not --all
        --branches --tags --remotes
-       --first-parent --no-merges
+       --first-parent --merges --no-merges
        --max-count=
        --max-age= --since= --after=
        --min-age= --until= --before=
@@ -997,6 +1160,7 @@ __git_log_shortlog_options="
 "
 
 __git_log_pretty_formats="oneline short medium full fuller email raw format:"
+__git_log_date_formats="relative iso8601 rfc2822 short local default raw"
 
 _git_log ()
 {
@@ -1014,10 +1178,17 @@ _git_log ()
                        " "" "${cur##--pretty=}"
                return
                ;;
+       --format=*)
+               __gitcomp "$__git_log_pretty_formats
+                       " "" "${cur##--format=}"
+               return
+               ;;
        --date=*)
-               __gitcomp "
-                       relative iso8601 rfc2822 short local default
-               " "" "${cur##--date=}"
+               __gitcomp "$__git_log_date_formats" "" "${cur##--date=}"
+               return
+               ;;
+       --decorate=*)
+               __gitcomp "long short" "" "${cur##--decorate=}"
                return
                ;;
        --*)
@@ -1026,13 +1197,13 @@ _git_log ()
                        $__git_log_shortlog_options
                        $__git_log_gitk_options
                        --root --topo-order --date-order --reverse
-                       --follow
+                       --follow --full-diff
                        --abbrev-commit --abbrev=
                        --relative-date --date=
-                       --pretty=
+                       --pretty= --format= --oneline
                        --cherry-pick
                        --graph
-                       --decorate
+                       --decorate --decorate=
                        --walk-reflogs
                        --parents --children
                        $merge
@@ -1045,24 +1216,19 @@ _git_log ()
        __git_complete_revlist
 }
 
+__git_merge_options="
+       --no-commit --no-stat --log --no-log --squash --strategy
+       --commit --stat --no-squash --ff --no-ff
+"
+
 _git_merge ()
 {
+       __git_complete_strategy && return
+
        local cur="${COMP_WORDS[COMP_CWORD]}"
-       case "${COMP_WORDS[COMP_CWORD-1]}" in
-       -s|--strategy)
-               __gitcomp "$(__git_merge_strategies)"
-               return
-       esac
        case "$cur" in
-       --strategy=*)
-               __gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}"
-               return
-               ;;
        --*)
-               __gitcomp "
-                       --no-commit --no-stat --log --no-log --squash --strategy
-                       --commit --stat --no-squash --ff --no-ff
-                       "
+               __gitcomp "$__git_merge_options"
                return
        esac
        __gitcomp "$(__git_refs)"
@@ -1073,10 +1239,7 @@ _git_mergetool ()
        local cur="${COMP_WORDS[COMP_CWORD]}"
        case "$cur" in
        --tool=*)
-               __gitcomp "
-                       kdiff3 tkdiff meld xxdiff emerge
-                       vimdiff gvimdiff ecmerge opendiff
-                       " "" "${cur##--tool=}"
+               __gitcomp "$__git_mergetools_common tortoisemerge" "" "${cur##--tool=}"
                return
                ;;
        --*)
@@ -1111,40 +1274,44 @@ _git_name_rev ()
 
 _git_pull ()
 {
-       local cur="${COMP_WORDS[COMP_CWORD]}"
+       __git_complete_strategy && return
 
-       if [ "$COMP_CWORD" = 2 ]; then
-               __gitcomp "$(__git_remotes)"
-       else
-               __gitcomp "$(__git_refs "${COMP_WORDS[2]}")"
-       fi
+       local cur="${COMP_WORDS[COMP_CWORD]}"
+       case "$cur" in
+       --*)
+               __gitcomp "
+                       --rebase --no-rebase
+                       $__git_merge_options
+                       $__git_fetch_options
+               "
+               return
+               ;;
+       esac
+       __git_complete_remote_or_refspec
 }
 
 _git_push ()
 {
        local cur="${COMP_WORDS[COMP_CWORD]}"
-
-       if [ "$COMP_CWORD" = 2 ]; then
+       case "${COMP_WORDS[COMP_CWORD-1]}" in
+       --repo)
                __gitcomp "$(__git_remotes)"
-       else
-               case "$cur" in
-               *:*)
-                       local pfx=""
-                       case "$COMP_WORDBREAKS" in
-                       *:*) : great ;;
-                       *)   pfx="${cur%%:*}:" ;;
-                       esac
-
-                       __gitcomp "$(__git_refs "${COMP_WORDS[2]}")" "$pfx" "${cur#*:}"
-                       ;;
-               +*)
-                       __gitcomp "$(__git_refs)" + "${cur#+}"
-                       ;;
-               *)
-                       __gitcomp "$(__git_refs)"
-                       ;;
-               esac
-       fi
+               return
+       esac
+       case "$cur" in
+       --repo=*)
+               __gitcomp "$(__git_remotes)" "" "${cur##--repo=}"
+               return
+               ;;
+       --*)
+               __gitcomp "
+                       --all --mirror --tags --dry-run --force --verbose
+                       --receive-pack= --repo=
+               "
+               return
+               ;;
+       esac
+       __git_complete_remote_or_refspec
 }
 
 _git_rebase ()
@@ -1154,16 +1321,8 @@ _git_rebase ()
                __gitcomp "--continue --skip --abort"
                return
        fi
-       case "${COMP_WORDS[COMP_CWORD-1]}" in
-       -s|--strategy)
-               __gitcomp "$(__git_merge_strategies)"
-               return
-       esac
+       __git_complete_strategy && return
        case "$cur" in
-       --strategy=*)
-               __gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}"
-               return
-               ;;
        --*)
                __gitcomp "--onto --merge --strategy --interactive"
                return
@@ -1171,18 +1330,39 @@ _git_rebase ()
        __gitcomp "$(__git_refs)"
 }
 
+__git_send_email_confirm_options="always never auto cc compose"
+__git_send_email_suppresscc_options="author self cc bodycc sob cccmd body all"
+
 _git_send_email ()
 {
        local cur="${COMP_WORDS[COMP_CWORD]}"
        case "$cur" in
+       --confirm=*)
+               __gitcomp "
+                       $__git_send_email_confirm_options
+                       " "" "${cur##--confirm=}"
+               return
+               ;;
+       --suppress-cc=*)
+               __gitcomp "
+                       $__git_send_email_suppresscc_options
+                       " "" "${cur##--suppress-cc=}"
+
+               return
+               ;;
+       --smtp-encryption=*)
+               __gitcomp "ssl tls" "" "${cur##--smtp-encryption=}"
+               return
+               ;;
        --*)
-               __gitcomp "--bcc --cc --cc-cmd --chain-reply-to --compose
-                       --dry-run --envelope-sender --from --identity
+               __gitcomp "--annotate --bcc --cc --cc-cmd --chain-reply-to
+                       --compose --confirm= --dry-run --envelope-sender
+                       --from --identity
                        --in-reply-to --no-chain-reply-to --no-signed-off-by-cc
                        --no-suppress-from --no-thread --quiet
                        --signed-off-by-cc --smtp-pass --smtp-server
-                       --smtp-server-port --smtp-ssl --smtp-user --subject
-                       --suppress-cc --suppress-from --thread --to
+                       --smtp-server-port --smtp-encryption= --smtp-user
+                       --subject --suppress-cc= --suppress-from --thread --to
                        --validate --no-validate"
                return
                ;;
@@ -1190,6 +1370,36 @@ _git_send_email ()
        COMPREPLY=()
 }
 
+__git_config_get_set_variables ()
+{
+       local prevword word config_file= c=$COMP_CWORD
+       while [ $c -gt 1 ]; do
+               word="${COMP_WORDS[c]}"
+               case "$word" in
+               --global|--system|--file=*)
+                       config_file="$word"
+                       break
+                       ;;
+               -f|--file)
+                       config_file="$word $prevword"
+                       break
+                       ;;
+               esac
+               prevword=$word
+               c=$((--c))
+       done
+
+       git --git-dir="$(__gitdir)" config $config_file --list 2>/dev/null |
+       while read line
+       do
+               case "$line" in
+               *.*=*)
+                       echo "${line/=*/}"
+                       ;;
+               esac
+       done
+}
+
 _git_config ()
 {
        local cur="${COMP_WORDS[COMP_CWORD]}"
@@ -1221,7 +1431,8 @@ _git_config ()
                __gitcomp "$(__git_merge_strategies)"
                return
                ;;
-       color.branch|color.diff|color.interactive|color.status|color.ui)
+       color.branch|color.diff|color.interactive|\
+       color.showbranch|color.status|color.ui)
                __gitcomp "always never auto"
                return
                ;;
@@ -1236,6 +1447,30 @@ _git_config ()
                        "
                return
                ;;
+       help.format)
+               __gitcomp "man info web html"
+               return
+               ;;
+       log.date)
+               __gitcomp "$__git_log_date_formats"
+               return
+               ;;
+       sendemail.aliasesfiletype)
+               __gitcomp "mutt mailrc pine elm gnus"
+               return
+               ;;
+       sendemail.confirm)
+               __gitcomp "$__git_send_email_confirm_options"
+               return
+               ;;
+       sendemail.suppresscc)
+               __gitcomp "$__git_send_email_suppresscc_options"
+               return
+               ;;
+       --get|--get-all|--unset|--unset-all)
+               __gitcomp "$(__git_config_get_set_variables)"
+               return
+               ;;
        *.*)
                COMPREPLY=()
                return
@@ -1255,7 +1490,7 @@ _git_config ()
        branch.*.*)
                local pfx="${cur%.*}."
                cur="${cur##*.}"
-               __gitcomp "remote merge mergeoptions" "$pfx" "$cur"
+               __gitcomp "remote merge mergeoptions rebase" "$pfx" "$cur"
                return
                ;;
        branch.*)
@@ -1264,12 +1499,45 @@ _git_config ()
                __gitcomp "$(__git_heads)" "$pfx" "$cur" "."
                return
                ;;
+       guitool.*.*)
+               local pfx="${cur%.*}."
+               cur="${cur##*.}"
+               __gitcomp "
+                       argprompt cmd confirm needsfile noconsole norescan
+                       prompt revprompt revunmerged title
+                       " "$pfx" "$cur"
+               return
+               ;;
+       difftool.*.*)
+               local pfx="${cur%.*}."
+               cur="${cur##*.}"
+               __gitcomp "cmd path" "$pfx" "$cur"
+               return
+               ;;
+       man.*.*)
+               local pfx="${cur%.*}."
+               cur="${cur##*.}"
+               __gitcomp "cmd path" "$pfx" "$cur"
+               return
+               ;;
+       mergetool.*.*)
+               local pfx="${cur%.*}."
+               cur="${cur##*.}"
+               __gitcomp "cmd path trustExitCode" "$pfx" "$cur"
+               return
+               ;;
+       pager.*)
+               local pfx="${cur%.*}."
+               cur="${cur#*.}"
+               __gitcomp "$(__git_all_commands)" "$pfx" "$cur"
+               return
+               ;;
        remote.*.*)
                local pfx="${cur%.*}."
                cur="${cur##*.}"
                __gitcomp "
                        url proxy fetch push mirror skipDefaultUpdate
-                       receivepack uploadpack tagopt
+                       receivepack uploadpack tagopt pushurl
                        " "$pfx" "$cur"
                return
                ;;
@@ -1279,8 +1547,17 @@ _git_config ()
                __gitcomp "$(__git_remotes)" "$pfx" "$cur" "."
                return
                ;;
+       url.*.*)
+               local pfx="${cur%.*}."
+               cur="${cur##*.}"
+               __gitcomp "insteadOf pushInsteadOf" "$pfx" "$cur"
+               return
+               ;;
        esac
        __gitcomp "
+               add.ignore-errors
+               alias.
+               apply.ignorewhitespace
                apply.whitespace
                branch.autosetupmerge
                branch.autosetuprebase
@@ -1298,11 +1575,15 @@ _git_config ()
                color.diff.old
                color.diff.plain
                color.diff.whitespace
+               color.grep
+               color.grep.external
+               color.grep.match
                color.interactive
                color.interactive.header
                color.interactive.help
                color.interactive.prompt
                color.pager
+               color.showbranch
                color.status
                color.status.added
                color.status.changed
@@ -1315,6 +1596,7 @@ _git_config ()
                core.autocrlf
                core.bare
                core.compression
+               core.createObject
                core.deltaBaseCacheLimit
                core.editor
                core.excludesfile
@@ -1345,11 +1627,21 @@ _git_config ()
                diff.renameLimit
                diff.renameLimit.
                diff.renames
+               diff.suppressBlankEmpty
+               diff.tool
+               diff.wordRegex
+               difftool.
+               difftool.prompt
                fetch.unpackLimit
+               format.attach
+               format.cc
                format.headers
                format.numbered
                format.pretty
+               format.signoff
+               format.subjectprefix
                format.suffix
+               format.thread
                gc.aggressiveWindow
                gc.auto
                gc.autopacklimit
@@ -1360,6 +1652,7 @@ _git_config ()
                gc.rerereresolved
                gc.rerereunresolved
                gitcvs.allbinary
+               gitcvs.commitmsgannotation
                gitcvs.dbTableNamePrefix
                gitcvs.dbdriver
                gitcvs.dbname
@@ -1368,6 +1661,7 @@ _git_config ()
                gitcvs.enabled
                gitcvs.logfile
                gitcvs.usecrlfattr
+               guitool.
                gui.blamehistoryctx
                gui.commitmsgwidth
                gui.copyblamethreshold
@@ -1394,13 +1688,24 @@ _git_config ()
                http.sslVerify
                i18n.commitEncoding
                i18n.logOutputEncoding
+               imap.folder
+               imap.host
+               imap.pass
+               imap.port
+               imap.preformattedHTML
+               imap.sslverify
+               imap.tunnel
+               imap.user
                instaweb.browser
                instaweb.httpd
                instaweb.local
                instaweb.modulepath
                instaweb.port
+               interactive.singlekey
                log.date
                log.showroot
+               mailmap.file
+               man.
                man.viewer
                merge.conflictstyle
                merge.log
@@ -1408,7 +1713,9 @@ _git_config ()
                merge.stat
                merge.tool
                merge.verbosity
+               mergetool.
                mergetool.keepBackup
+               mergetool.prompt
                pack.compression
                pack.deltaCacheLimit
                pack.deltaCacheSize
@@ -1418,8 +1725,11 @@ _git_config ()
                pack.threads
                pack.window
                pack.windowMemory
+               pager.
                pull.octopus
                pull.twohead
+               push.default
+               rebase.stat
                receive.denyCurrentBranch
                receive.denyDeletes
                receive.denyNonFastForwards
@@ -1428,11 +1738,32 @@ _git_config ()
                repack.usedeltabaseoffset
                rerere.autoupdate
                rerere.enabled
+               sendemail.aliasesfile
+               sendemail.aliasesfiletype
+               sendemail.bcc
+               sendemail.cc
+               sendemail.cccmd
+               sendemail.chainreplyto
+               sendemail.confirm
+               sendemail.envelopesender
+               sendemail.multiedit
+               sendemail.signedoffbycc
+               sendemail.smtpencryption
+               sendemail.smtppass
+               sendemail.smtpserver
+               sendemail.smtpserverport
+               sendemail.smtpuser
+               sendemail.suppresscc
+               sendemail.suppressfrom
+               sendemail.thread
+               sendemail.to
+               sendemail.validate
                showbranch.default
                status.relativePaths
                status.showUntrackedFiles
                tar.umask
                transfer.unpackLimit
+               url.
                user.email
                user.name
                user.signingkey
@@ -1443,8 +1774,8 @@ _git_config ()
 
 _git_remote ()
 {
-       local subcommands="add rename rm show prune update"
-       local subcommand="$(__git_find_subcommand "$subcommands")"
+       local subcommands="add rename rm show prune update set-head"
+       local subcommand="$(__git_find_on_cmdline "$subcommands")"
        if [ -z "$subcommand" ]; then
                __gitcomp "$subcommands"
                return
@@ -1456,13 +1787,9 @@ _git_remote ()
                ;;
        update)
                local i c='' IFS=$'\n'
-               for i in $(git --git-dir="$(__gitdir)" config --list); do
-                       case "$i" in
-                       remotes.*)
-                               i="${i#remotes.}"
-                               c="$c ${i/=*/}"
-                               ;;
-                       esac
+               for i in $(git --git-dir="$(__gitdir)" config --get-regexp "remotes\..*" 2>/dev/null); do
+                       i="${i#remotes.}"
+                       c="$c ${i/ */}"
                done
                __gitcomp "$c"
                ;;
@@ -1472,6 +1799,11 @@ _git_remote ()
        esac
 }
 
+_git_replace ()
+{
+       __gitcomp "$(__git_refs)"
+}
+
 _git_reset ()
 {
        __git_has_doubledash && return
@@ -1479,7 +1811,7 @@ _git_reset ()
        local cur="${COMP_WORDS[COMP_CWORD]}"
        case "$cur" in
        --*)
-               __gitcomp "--merge --mixed --hard --soft"
+               __gitcomp "--merge --mixed --hard --soft --patch"
                return
                ;;
        esac
@@ -1541,8 +1873,13 @@ _git_show ()
                        " "" "${cur##--pretty=}"
                return
                ;;
+       --format=*)
+               __gitcomp "$__git_log_pretty_formats
+                       " "" "${cur##--format=}"
+               return
+               ;;
        --*)
-               __gitcomp "--pretty=
+               __gitcomp "--pretty= --format= --abbrev-commit --oneline
                        $__git_diff_common_options
                        "
                return
@@ -1559,7 +1896,8 @@ _git_show_branch ()
                __gitcomp "
                        --all --remotes --topo-order --current --more=
                        --list --independent --merge-base --no-name
-                       --sha1-name --topics --reflog
+                       --color --no-color
+                       --sha1-name --sparse --topics --reflog
                        "
                return
                ;;
@@ -1569,20 +1907,32 @@ _git_show_branch ()
 
 _git_stash ()
 {
+       local cur="${COMP_WORDS[COMP_CWORD]}"
+       local save_opts='--keep-index --no-keep-index --quiet --patch'
        local subcommands='save list show apply clear drop pop create branch'
-       local subcommand="$(__git_find_subcommand "$subcommands")"
+       local subcommand="$(__git_find_on_cmdline "$subcommands")"
        if [ -z "$subcommand" ]; then
-               __gitcomp "$subcommands"
+               case "$cur" in
+               --*)
+                       __gitcomp "$save_opts"
+                       ;;
+               *)
+                       if [ -z "$(__git_find_on_cmdline "$save_opts")" ]; then
+                               __gitcomp "$subcommands"
+                       else
+                               COMPREPLY=()
+                       fi
+                       ;;
+               esac
        else
-               local cur="${COMP_WORDS[COMP_CWORD]}"
                case "$subcommand,$cur" in
                save,--*)
-                       __gitcomp "--keep-index"
+                       __gitcomp "$save_opts"
                        ;;
-               apply,--*)
-                       __gitcomp "--index"
+               apply,--*|pop,--*)
+                       __gitcomp "--index --quiet"
                        ;;
-               show,--*|drop,--*|pop,--*|branch,--*)
+               show,--*|drop,--*|branch,--*)
                        COMPREPLY=()
                        ;;
                show,*|apply,*|drop,*|pop,*|branch,*)
@@ -1601,7 +1951,7 @@ _git_submodule ()
        __git_has_doubledash && return
 
        local subcommands="add status init update summary foreach sync"
-       if [ -z "$(__git_find_subcommand "$subcommands")" ]; then
+       if [ -z "$(__git_find_on_cmdline "$subcommands")" ]; then
                local cur="${COMP_WORDS[COMP_CWORD]}"
                case "$cur" in
                --*)
@@ -1623,7 +1973,7 @@ _git_svn ()
                proplist show-ignore show-externals branch tag blame
                migrate
                "
-       local subcommand="$(__git_find_subcommand "$subcommands")"
+       local subcommand="$(__git_find_on_cmdline "$subcommands")"
        if [ -z "$subcommand" ]; then
                __gitcomp "$subcommands"
        else
@@ -1771,6 +2121,7 @@ _git ()
                        --bare
                        --version
                        --exec-path
+                       --html-path
                        --work-tree=
                        --help
                        "
@@ -1800,8 +2151,10 @@ _git ()
        config)      _git_config ;;
        describe)    _git_describe ;;
        diff)        _git_diff ;;
+       difftool)    _git_difftool ;;
        fetch)       _git_fetch ;;
        format-patch) _git_format_patch ;;
+       fsck)        _git_fsck ;;
        gc)          _git_gc ;;
        grep)        _git_grep ;;
        help)        _git_help ;;
@@ -1819,6 +2172,7 @@ _git ()
        push)        _git_push ;;
        rebase)      _git_rebase ;;
        remote)      _git_remote ;;
+       replace)     _git_replace ;;
        reset)       _git_reset ;;
        revert)      _git_revert ;;
        rm)          _git_rm ;;
@@ -1841,7 +2195,7 @@ _gitk ()
        __git_has_doubledash && return
 
        local cur="${COMP_WORDS[COMP_CWORD]}"
-       local g="$(git rev-parse --git-dir 2>/dev/null)"
+       local g="$(__gitdir)"
        local merge=""
        if [ -f "$g/MERGE_HEAD" ]; then
                merge="--merge"