diff options
Diffstat (limited to 'contrib/completion/git-completion.bash')
-rw-r--r-- | contrib/completion/git-completion.bash | 1244 |
1 files changed, 870 insertions, 374 deletions
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 9e8ec95c3c..4bdd27ddc8 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -29,6 +29,15 @@ # tell the completion to use commit completion. This also works with aliases # of form "!sh -c '...'". For example, "!sh -c ': git commit ; ... '". # +# If you have a command that is not part of git, but you would still +# like completion, you can use __git_complete: +# +# __git_complete gl git_log +# +# Or if it's a main command (i.e. git or gitk): +# +# __git_complete gk gitk +# # Compatible with bash 3.2.57. # # You can set the following environment variables to influence the behavior of @@ -37,7 +46,13 @@ # GIT_COMPLETION_CHECKOUT_NO_GUESS # # When set to "1", do not include "DWIM" suggestions in git-checkout -# completion (e.g., completing "foo" when "origin/foo" exists). +# and git-switch completion (e.g., completing "foo" when "origin/foo" +# exists). +# +# GIT_COMPLETION_SHOW_ALL +# +# When set to "1" suggest all options, including options which are +# typically hidden (e.g. '--allow-empty' for 'git commit'). case "$COMP_WORDBREAKS" in *:*) : great ;; @@ -49,7 +64,7 @@ esac # variable. __git_find_repo_path () { - if [ -n "$__git_repo_path" ]; then + if [ -n "${__git_repo_path-}" ]; then # we already know where it is return fi @@ -62,7 +77,7 @@ __git_find_repo_path () test -d "$__git_dir" && __git_repo_path="$__git_dir" elif [ -n "${GIT_DIR-}" ]; then - test -d "${GIT_DIR-}" && + test -d "$GIT_DIR" && __git_repo_path="$GIT_DIR" elif [ -d .git ]; then __git_repo_path=.git @@ -300,6 +315,19 @@ __gitcomp_direct () COMPREPLY=($1) } +# Similar to __gitcomp_direct, but appends to COMPREPLY instead. +# Callers must take care of providing only words that match the current word +# to be completed and adding any prefix and/or suffix (trailing space!), if +# necessary. +# 1: List of newline-separated matching completion words, complete with +# prefix and suffix. +__gitcomp_direct_append () +{ + local IFS=$'\n' + + COMPREPLY+=($1) +} + __gitcompappend () { local x i=${#COMPREPLY[@]} @@ -339,7 +367,7 @@ __gitcomp () c="$c${4-}" if [[ $c == "$cur_"* ]]; then case $c in - --*=*|*.) ;; + --*=|*.) ;; *) c="$c " ;; esac COMPREPLY[i++]="${2-}$c" @@ -359,7 +387,7 @@ __gitcomp () c="$c${4-}" if [[ $c == "$cur_"* ]]; then case $c in - --*=*|*.) ;; + *=|*.) ;; *) c="$c " ;; esac COMPREPLY[i++]="${2-}$c" @@ -372,7 +400,7 @@ __gitcomp () # Clear the variables caching builtins' options when (re-)sourcing # the completion script. if [[ -n ${ZSH_VERSION-} ]]; then - unset $(set |sed -ne 's/^\(__gitcomp_builtin_[a-zA-Z0-9_][a-zA-Z0-9_]*\)=.*/\1/p') 2>/dev/null + unset ${(M)${(k)parameters[@]}:#__gitcomp_builtin_*} 2>/dev/null else unset $(compgen -v __gitcomp_builtin_) fi @@ -390,17 +418,24 @@ __gitcomp_builtin () # spaces must be replaced with underscore for multi-word # commands, e.g. "git remote add" becomes remote_add. local cmd="$1" - local incl="$2" - local excl="$3" + local incl="${2-}" + local excl="${3-}" local var=__gitcomp_builtin_"${cmd/-/_}" local options - eval "options=\$$var" + eval "options=\${$var-}" if [ -z "$options" ]; then + local completion_helper + if [ "${GIT_COMPLETION_SHOW_ALL-}" = "1" ]; then + completion_helper="--git-completion-helper-all" + else + completion_helper="--git-completion-helper" + fi # leading and trailing spaces are significant to make # option removal work correctly. - options=" $incl $(__git ${cmd/_/ } --git-completion-helper) " + options=" $incl $(__git ${cmd/_/ } $completion_helper) " || return + for i in $excl; do options="${options/ $i / }" done @@ -438,7 +473,7 @@ __gitcomp_nl () # Callers must take care of providing only paths that match the current path # to be completed and adding any prefix path components, if necessary. # 1: List of newline-separated matching paths, complete with all prefix -# path componens. +# path components. __gitcomp_file_direct () { local IFS=$'\n' @@ -502,7 +537,7 @@ __git_index_files () { local root="$2" match="$3" - __git_ls_files_helper "$root" "$1" "$match" | + __git_ls_files_helper "$root" "$1" "${match:-?}" | awk -F / -v pfx="${2//\\/\\\\}" '{ paths[$1] = 1 } @@ -522,7 +557,7 @@ __git_index_files () # Even when a directory name itself does not contain # any special characters, it will still be quoted if # any of its (stripped) trailing path components do. - # Because of this we may have seen the same direcory + # Because of this we may have seen the same directory # both quoted and unquoted. if (p in paths) # We have seen the same directory unquoted, @@ -548,7 +583,7 @@ __git_index_files () esc_idx, 1) } else if (esc == "n") { # Uh-oh, a newline character. - # We cant reliably put a pathname + # We cannot reliably put a pathname # containing a newline into COMPREPLY, # and the newline would create a mess. # Skip this path. @@ -563,7 +598,7 @@ __git_index_files () } } # Drop closing double quote, if there is one. - # (There isnt any if this is a directory, as it was + # (There is not any if this is a directory, as it was # already stripped with the trailing path components.) if (substr(p, length(p), 1) == "\"") out = out substr(p, 1, length(p) - 1) @@ -609,6 +644,19 @@ __git_heads () "refs/heads/$cur_*" "refs/heads/$cur_*/**" } +# Lists branches from remote repositories. +# 1: A prefix to be added to each listed branch (optional). +# 2: List only branches matching this word (optional; list all branches if +# unset or empty). +# 3: A suffix to be appended to each listed branch (optional). +__git_remote_heads () +{ + local pfx="${1-}" cur_="${2-}" sfx="${3-}" + + __git for-each-ref --format="${pfx//\%/%%}%(refname:strip=2)$sfx" \ + "refs/remotes/$cur_*" "refs/remotes/$cur_*/**" +} + # Lists tags from the local repository. # Accepts the same positional parameters as __git_heads() above. __git_tags () @@ -619,6 +667,26 @@ __git_tags () "refs/tags/$cur_*" "refs/tags/$cur_*/**" } +# List unique branches from refs/remotes used for 'git checkout' and 'git +# switch' tracking DWIMery. +# 1: A prefix to be added to each listed branch (optional) +# 2: List only branches matching this word (optional; list all branches if +# unset or empty). +# 3: A suffix to be appended to each listed branch (optional). +__git_dwim_remote_heads () +{ + local pfx="${1-}" cur_="${2-}" sfx="${3-}" + local fer_pfx="${pfx//\%/%%}" # "escape" for-each-ref format specifiers + + # employ the heuristic used by git checkout and git switch + # Try to find a remote branch that cur_es the completion word + # but only output if the branch name is unique + __git for-each-ref --format="$fer_pfx%(refname:strip=3)$sfx" \ + --sort="refname:strip=3" \ + "refs/remotes/*/$cur_*" "refs/remotes/*/$cur_*/**" | \ + uniq -u +} + # Lists refs from the local (by default) or from a remote repository. # It accepts 0, 1 or 2 arguments: # 1: The remote to list refs from (optional; ignored, if set but empty). @@ -676,7 +744,7 @@ __git_refs () track="" ;; *) - for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD REBASE_HEAD; do + for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD REBASE_HEAD CHERRY_PICK_HEAD; do case "$i" in $match*) if [ -e "$dir/$i" ]; then @@ -694,13 +762,7 @@ __git_refs () __git_dir="$dir" __git for-each-ref --format="$fer_pfx%($format)$sfx" \ "${refs[@]}" if [ -n "$track" ]; then - # employ the heuristic used by git checkout - # Try to find a remote branch that matches the completion word - # but only output if the branch name is unique - __git for-each-ref --format="$fer_pfx%(refname:strip=3)$sfx" \ - --sort="refname:strip=3" \ - "refs/remotes/*/$match*" "refs/remotes/*/$match*/**" | \ - uniq -u + __git_dwim_remote_heads "$pfx" "$match" "$sfx" fi return fi @@ -747,29 +809,51 @@ __git_refs () # Usage: __git_complete_refs [<option>]... # --remote=<remote>: The remote to list refs from, can be the name of a # configured remote, a path, or a URL. -# --track: List unique remote branches for 'git checkout's tracking DWIMery. +# --dwim: List unique remote branches for 'git switch's tracking DWIMery. # --pfx=<prefix>: A prefix to be added to each ref. # --cur=<word>: The current ref to be completed. Defaults to the current # word to be completed. # --sfx=<suffix>: A suffix to be appended to each ref instead of the default # space. +# --mode=<mode>: What set of refs to complete, one of 'refs' (the default) to +# complete all refs, 'heads' to complete only branches, or +# 'remote-heads' to complete only remote branches. Note that +# --remote is only compatible with --mode=refs. __git_complete_refs () { - local remote track pfx cur_="$cur" sfx=" " + local remote= dwim= pfx= cur_="$cur" sfx=" " mode="refs" while test $# != 0; do case "$1" in --remote=*) remote="${1##--remote=}" ;; - --track) track="yes" ;; + --dwim) dwim="yes" ;; + # --track is an old spelling of --dwim + --track) dwim="yes" ;; --pfx=*) pfx="${1##--pfx=}" ;; --cur=*) cur_="${1##--cur=}" ;; --sfx=*) sfx="${1##--sfx=}" ;; + --mode=*) mode="${1##--mode=}" ;; *) return 1 ;; esac shift done - __gitcomp_direct "$(__git_refs "$remote" "$track" "$pfx" "$cur_" "$sfx")" + # complete references based on the specified mode + case "$mode" in + refs) + __gitcomp_direct "$(__git_refs "$remote" "" "$pfx" "$cur_" "$sfx")" ;; + heads) + __gitcomp_direct "$(__git_heads "$pfx" "$cur_" "$sfx")" ;; + remote-heads) + __gitcomp_direct "$(__git_remote_heads "$pfx" "$cur_" "$sfx")" ;; + *) + return 1 ;; + esac + + # Append DWIM remote branch names if requested + if [ "$dwim" = "yes" ]; then + __gitcomp_direct_append "$(__git_dwim_remote_heads "$pfx" "$cur_" "$sfx")" + fi } # __git_refs2 requires 1 argument (to pass to __git_refs) @@ -853,9 +937,14 @@ __git_compute_merge_strategies () __git_merge_strategies=$(__git_list_merge_strategies) } +__git_merge_strategy_options="ours theirs subtree subtree= patience + histogram diff-algorithm= ignore-space-change ignore-all-space + ignore-space-at-eol renormalize no-renormalize no-renames + find-renames find-renames= rename-threshold=" + __git_complete_revlist_file () { - local pfx ls ref cur_="$cur" + local dequoted_word pfx ls ref cur_="$cur" case "$cur_" in *..?*:*) return @@ -863,14 +952,18 @@ __git_complete_revlist_file () ?*:*) ref="${cur_%%:*}" cur_="${cur_#*:}" - case "$cur_" in + + __git_dequote "$cur_" + + case "$dequoted_word" in ?*/*) - pfx="${cur_%/*}" - cur_="${cur_##*/}" + pfx="${dequoted_word%/*}" + cur_="${dequoted_word##*/}" ls="$ref:$pfx" pfx="$pfx/" ;; *) + cur_="$dequoted_word" ls="$ref" ;; esac @@ -880,21 +973,10 @@ __git_complete_revlist_file () *) pfx="$ref:$pfx" ;; esac - __gitcomp_nl "$(__git ls-tree "$ls" \ - | sed '/^100... blob /{ - s,^.* ,, - s,$, , - } - /^120000 blob /{ - s,^.* ,, - s,$, , - } - /^040000 tree /{ - s,^.* ,, - s,$,/, - } - s/^.* //')" \ - "$pfx" "$cur_" "" + __gitcomp_file "$(__git ls-tree "$ls" \ + | sed 's/^.* // + s/$//')" \ + "$pfx" "$cur_" ;; *...*) pfx="${cur_%...*}..." @@ -924,8 +1006,8 @@ __git_complete_revlist () __git_complete_remote_or_refspec () { - local cur_="$cur" cmd="${words[1]}" - local i c=2 remote="" pfx="" lhs=1 no_complete_refspec=0 + local cur_="$cur" cmd="${words[__git_cmd_idx]}" + local i c=$((__git_cmd_idx+1)) remote="" pfx="" lhs=1 no_complete_refspec=0 if [ "$cmd" = "remote" ]; then ((c++)) fi @@ -1003,12 +1085,21 @@ __git_complete_strategy () -s|--strategy) __gitcomp "$__git_merge_strategies" return 0 + ;; + -X) + __gitcomp "$__git_merge_strategy_options" + return 0 + ;; esac case "$cur" in --strategy=*) __gitcomp "$__git_merge_strategies" "" "${cur##--strategy=}" return 0 ;; + --strategy-option=*) + __gitcomp "$__git_merge_strategy_options" "" "${cur##--strategy-option=}" + return 0 + ;; esac return 1 } @@ -1017,7 +1108,7 @@ __git_all_commands= __git_compute_all_commands () { test -n "$__git_all_commands" || - __git_all_commands=$(git --list-cmds=main,others,alias,nohelpers) + __git_all_commands=$(__git --list-cmds=main,others,alias,nohelpers) } # Lists all set config variables starting with the given section prefix, @@ -1038,37 +1129,72 @@ __git_pretty_aliases () # __git_aliased_command requires 1 argument __git_aliased_command () { - local word cmdline=$(__git config --get "alias.$1") - for word in $cmdline; do - case "$word" in - \!gitk|gitk) - echo "gitk" - return - ;; - \!*) : shell command alias ;; - -*) : 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" + local cur=$1 last list= word cmdline + + while [[ -n "$cur" ]]; do + if [[ "$list" == *" $cur "* ]]; then + # loop detected return - esac + fi + + cmdline=$(__git config --get "alias.$cur") + list=" $cur $list" + last=$cur + cur= + + for word in $cmdline; do + case "$word" in + \!gitk|gitk) + cur="gitk" + break + ;; + \!*) : shell command alias ;; + -*) : 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 ;; + *) + cur="$word" + break + esac + done done + + cur=$last + if [[ "$cur" != "$1" ]]; then + echo "$cur" + fi } -# __git_find_on_cmdline requires 1 argument +# Check whether one of the given words is present on the command line, +# and print the first word found. +# +# Usage: __git_find_on_cmdline [<option>]... "<wordlist>" +# --show-idx: Optionally show the index of the found word in the $words array. __git_find_on_cmdline () { - local word subcommand c=1 + local word c="$__git_cmd_idx" show_idx + + while test $# -gt 1; do + case "$1" in + --show-idx) show_idx=y ;; + *) return 1 ;; + esac + shift + done + local wordlist="$1" + while [ $c -lt $cword ]; do - word="${words[c]}" - for subcommand in $1; do - if [ "$subcommand" = "$word" ]; then - echo "$subcommand" + for word in $wordlist; do + if [ "$word" = "${words[c]}" ]; then + if [ -n "${show_idx-}" ]; then + echo "$c $word" + else + echo "$word" + fi return fi done @@ -1076,6 +1202,40 @@ __git_find_on_cmdline () done } +# Similar to __git_find_on_cmdline, except that it loops backwards and thus +# prints the *last* word found. Useful for finding which of two options that +# supersede each other came last, such as "--guess" and "--no-guess". +# +# Usage: __git_find_last_on_cmdline [<option>]... "<wordlist>" +# --show-idx: Optionally show the index of the found word in the $words array. +__git_find_last_on_cmdline () +{ + local word c=$cword show_idx + + while test $# -gt 1; do + case "$1" in + --show-idx) show_idx=y ;; + *) return 1 ;; + esac + shift + done + local wordlist="$1" + + while [ $c -gt "$__git_cmd_idx" ]; do + ((c--)) + for word in $wordlist; do + if [ "$word" = "${words[c]}" ]; then + if [ -n "$show_idx" ]; then + echo "$c $word" + else + echo "$word" + fi + return + fi + done + done +} + # Echo the value of an option set on the command line or config # # $1: short option name @@ -1146,7 +1306,7 @@ __git_count_arguments () local word i c=0 # Skip "git" (first argument) - for ((i=1; i < ${#words[@]}; i++)); do + for ((i=$__git_cmd_idx; i < ${#words[@]}; i++)); do word="${words[i]}" case "$word" in @@ -1170,7 +1330,10 @@ __git_count_arguments () } __git_whitespacelist="nowarn warn error error-all fix" +__git_patchformat="mbox stgit stgit-series hg mboxrd" +__git_showcurrentpatch="diff raw" __git_am_inprogress_options="--skip --continue --resolved --abort --quit --show-current-patch" +__git_quoted_cr="nowarn warn strip" _git_am () { @@ -1184,6 +1347,18 @@ _git_am () __gitcomp "$__git_whitespacelist" "" "${cur##--whitespace=}" return ;; + --patch-format=*) + __gitcomp "$__git_patchformat" "" "${cur##--patch-format=}" + return + ;; + --show-current-patch=*) + __gitcomp "$__git_showcurrentpatch" "" "${cur##--show-current-patch=}" + return + ;; + --quoted-cr=*) + __gitcomp "$__git_quoted_cr" "" "${cur##--quoted-cr=}" + return + ;; --*) __gitcomp_builtin am "" \ "$__git_am_inprogress_options" @@ -1207,6 +1382,10 @@ _git_apply () _git_add () { case "$cur" in + --chmod=*) + __gitcomp "+x -x" "" "${cur##--chmod=}" + return + ;; --*) __gitcomp_builtin add return @@ -1232,10 +1411,7 @@ _git_archive () return ;; --*) - __gitcomp " - --format= --list --verbose - --prefix= --remote= --exec= --output - " + __gitcomp_builtin archive "--format= --list --verbose --prefix= --worktree-attributes" return ;; esac @@ -1267,15 +1443,19 @@ _git_bisect () esac } +__git_ref_fieldlist="refname objecttype objectsize objectname upstream push HEAD symref" + _git_branch () { - local i c=1 only_local_ref="n" has_r="n" + local i c="$__git_cmd_idx" only_local_ref="n" has_r="n" while [ $c -lt $cword ]; do i="${words[c]}" case "$i" in - -d|--delete|-m|--move) only_local_ref="y" ;; - -r|--remotes) has_r="y" ;; + -d|-D|--delete|-m|-M|--move|-c|-C|--copy) + only_local_ref="y" ;; + -r|--remotes) + has_r="y" ;; esac ((c++)) done @@ -1299,12 +1479,12 @@ _git_branch () _git_bundle () { - local cmd="${words[2]}" + local cmd="${words[__git_cmd_idx+1]}" case "$cword" in - 2) + $((__git_cmd_idx+1))) __gitcomp "create list-heads verify unbundle" ;; - 3) + $((__git_cmd_idx+2))) # looking for a file ;; *) @@ -1317,10 +1497,73 @@ _git_bundle () esac } +# Helper function to decide whether or not we should enable DWIM logic for +# git-switch and git-checkout. +# +# To decide between the following rules in decreasing priority order: +# - the last provided of "--guess" or "--no-guess" explicitly enable or +# disable completion of DWIM logic respectively. +# - If checkout.guess is false, disable completion of DWIM logic. +# - If the --no-track option is provided, take this as a hint to disable the +# DWIM completion logic +# - If GIT_COMPLETION_CHECKOUT_NO_GUESS is set, disable the DWIM completion +# logic, as requested by the user. +# - Enable DWIM logic otherwise. +# +__git_checkout_default_dwim_mode () +{ + local last_option dwim_opt="--dwim" + + if [ "${GIT_COMPLETION_CHECKOUT_NO_GUESS-}" = "1" ]; then + dwim_opt="" + fi + + # --no-track disables DWIM, but with lower priority than + # --guess/--no-guess/checkout.guess + if [ -n "$(__git_find_on_cmdline "--no-track")" ]; then + dwim_opt="" + fi + + # checkout.guess = false disables DWIM, but with lower priority than + # --guess/--no-guess + if [ "$(__git config --type=bool checkout.guess)" = "false" ]; then + dwim_opt="" + fi + + # Find the last provided --guess or --no-guess + last_option="$(__git_find_last_on_cmdline "--guess --no-guess")" + case "$last_option" in + --guess) + dwim_opt="--dwim" + ;; + --no-guess) + dwim_opt="" + ;; + esac + + echo "$dwim_opt" +} + _git_checkout () { __git_has_doubledash && return + local dwim_opt="$(__git_checkout_default_dwim_mode)" + + case "$prev" in + -b|-B|--orphan) + # Complete local branches (and DWIM branch + # remote branch names) for an option argument + # specifying a new branch name. This is for + # convenience, assuming new branches are + # possibly based on pre-existing branch names. + __git_complete_refs $dwim_opt --mode="heads" + return + ;; + *) + ;; + esac + case "$cur" in --conflict=*) __gitcomp "diff3 merge" "" "${cur##--conflict=}" @@ -1329,19 +1572,28 @@ _git_checkout () __gitcomp_builtin checkout ;; *) - # check if --track, --no-track, or --no-guess was specified - # if so, disable DWIM mode - local flags="--track --no-track --no-guess" track_opt="--track" - if [ "$GIT_COMPLETION_CHECKOUT_NO_GUESS" = "1" ] || - [ -n "$(__git_find_on_cmdline "$flags")" ]; then - track_opt='' + # At this point, we've already handled special completion for + # the arguments to -b/-B, and --orphan. There are 3 main + # things left we can possibly complete: + # 1) a start-point for -b/-B, -d/--detach, or --orphan + # 2) a remote head, for --track + # 3) an arbitrary reference, possibly including DWIM names + # + + if [ -n "$(__git_find_on_cmdline "-b -B -d --detach --orphan")" ]; then + __git_complete_refs --mode="refs" + elif [ -n "$(__git_find_on_cmdline "--track")" ]; then + __git_complete_refs --mode="remote-heads" + else + __git_complete_refs $dwim_opt --mode="refs" fi - __git_complete_refs $track_opt ;; esac } -__git_cherry_pick_inprogress_options="--continue --quit --abort" +__git_sequencer_inprogress_options="--continue --quit --abort --skip" + +__git_cherry_pick_inprogress_options=$__git_sequencer_inprogress_options _git_cherry_pick () { @@ -1350,6 +1602,9 @@ _git_cherry_pick () __gitcomp "$__git_cherry_pick_inprogress_options" return fi + + __git_complete_strategy && return + case "$cur" in --*) __gitcomp_builtin cherry-pick "" \ @@ -1376,7 +1631,18 @@ _git_clean () _git_clone () { + case "$prev" in + -c|--config) + __git_complete_config_variable_name_and_value + return + ;; + esac case "$cur" in + --config=*) + __git_complete_config_variable_name_and_value \ + --cur="${cur##--config=}" + return + ;; --*) __gitcomp_builtin clone return @@ -1437,9 +1703,16 @@ __git_diff_algorithms="myers minimal patience histogram" __git_diff_submodule_formats="diff log short" +__git_color_moved_opts="no default plain blocks zebra dimmed-zebra" + +__git_color_moved_ws_opts="no ignore-space-at-eol ignore-space-change + ignore-all-space allow-indentation-change" + __git_diff_common_options="--stat --numstat --shortstat --summary --patch-with-stat --name-only --name-status --color --no-color --color-words --no-renames --check + --color-moved --color-moved= --no-color-moved + --color-moved-ws= --no-color-moved-ws --full-index --binary --abbrev --diff-filter= --find-copies-harder --ignore-cr-at-eol --text --ignore-space-at-eol --ignore-space-change @@ -1453,8 +1726,16 @@ __git_diff_common_options="--stat --numstat --shortstat --summary --dirstat-by-file= --cumulative --diff-algorithm= --submodule --submodule= --ignore-submodules + --indent-heuristic --no-indent-heuristic + --textconv --no-textconv + --patch --no-patch + --anchored= " +__git_diff_difftool_options="--cached --staged --pickaxe-all --pickaxe-regex + --base --ours --theirs --no-index --relative --merge-base + $__git_diff_common_options" + _git_diff () { __git_has_doubledash && return @@ -1468,11 +1749,16 @@ _git_diff () __gitcomp "$__git_diff_submodule_formats" "" "${cur##--submodule=}" return ;; + --color-moved=*) + __gitcomp "$__git_color_moved_opts" "" "${cur##--color-moved=}" + return + ;; + --color-moved-ws=*) + __gitcomp "$__git_color_moved_ws_opts" "" "${cur##--color-moved-ws=}" + return + ;; --*) - __gitcomp "--cached --staged --pickaxe-all --pickaxe-regex - --base --ours --theirs --no-index - $__git_diff_common_options - " + __gitcomp "$__git_diff_difftool_options" return ;; esac @@ -1480,7 +1766,8 @@ _git_diff () } __git_mergetools_common="diffuse diffmerge ecmerge emerge kdiff3 meld opendiff - tkdiff vimdiff gvimdiff xxdiff araxis p4merge bc codecompare + tkdiff vimdiff nvimdiff gvimdiff xxdiff araxis p4merge + bc codecompare smerge " _git_difftool () @@ -1493,11 +1780,7 @@ _git_difftool () return ;; --*) - __gitcomp_builtin difftool "$__git_diff_common_options - --base --cached --ours --theirs - --pickaxe-all --pickaxe-regex - --relative --staged - " + __gitcomp_builtin difftool "$__git_diff_difftool_options" return ;; esac @@ -1513,6 +1796,10 @@ _git_fetch () __gitcomp "$__git_fetch_recurse_submodules" "" "${cur##--recurse-submodules=}" return ;; + --filter=*) + __gitcomp "blob:none blob:limit= sparse:oid=" "" "${cur##--filter=}" + return + ;; --*) __gitcomp_builtin fetch return @@ -1535,6 +1822,10 @@ _git_format_patch () " "" "${cur##--thread=}" return ;; + --base=*|--interdiff=*|--range-diff=*) + __git_complete_refs --cur="${cur#--*=}" + return + ;; --*) __gitcomp_builtin format-patch "$__git_format_patch_extra_options" return @@ -1555,7 +1846,7 @@ _git_fsck () _git_gitk () { - _gitk + __gitk_main } # Lists matching symbol names from a tag (as in ctags) file. @@ -1609,7 +1900,7 @@ _git_grep () esac case "$cword,$prev" in - 2,*|*,-*) + $((__git_cmd_idx+1)),*|*,-*) __git_complete_symbol && return ;; esac @@ -1625,11 +1916,11 @@ _git_help () return ;; esac - if test -n "$GIT_TESTING_ALL_COMMAND_LIST" + if test -n "${GIT_TESTING_ALL_COMMAND_LIST-}" then - __gitcomp "$GIT_TESTING_ALL_COMMAND_LIST $(git --list-cmds=alias,list-guide) gitk" + __gitcomp "$GIT_TESTING_ALL_COMMAND_LIST $(__git --list-cmds=alias,list-guide) gitk" else - __gitcomp "$(git --list-cmds=main,nohelpers,alias,list-guide) gitk" + __gitcomp "$(__git --list-cmds=main,nohelpers,alias,list-guide) gitk" fi } @@ -1709,8 +2000,8 @@ __git_log_shortlog_options=" --all-match --invert-grep " -__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_pretty_formats="oneline short medium full fuller reference email raw format: tformat: mboxrd" +__git_log_date_formats="relative iso8601 iso8601-strict rfc2822 short local default raw unix format:" _git_log () { @@ -1756,6 +2047,10 @@ _git_log () __gitcomp "$__git_diff_submodule_formats" "" "${cur##--submodule=}" return ;; + --no-walk=*) + __gitcomp "sorted unsorted" "" "${cur##--no-walk=}" + return + ;; --*) __gitcomp " $__git_log_common_options @@ -1763,16 +2058,18 @@ _git_log () $__git_log_gitk_options --root --topo-order --date-order --reverse --follow --full-diff - --abbrev-commit --abbrev= + --abbrev-commit --no-abbrev-commit --abbrev= --relative-date --date= --pretty= --format= --oneline --show-signature --cherry-mark --cherry-pick --graph - --decorate --decorate= + --decorate --decorate= --no-decorate --walk-reflogs + --no-walk --no-walk= --do-walk --parents --children + --expand-tabs --expand-tabs= --no-expand-tabs $merge $__git_diff_common_options --pickaxe-all --pickaxe-regex @@ -1976,15 +2273,18 @@ _git_range_diff () __git_complete_revlist } +__git_rebase_inprogress_options="--continue --skip --abort --quit --show-current-patch" +__git_rebase_interactive_inprogress_options="$__git_rebase_inprogress_options --edit-todo" + _git_rebase () { __git_find_repo_path if [ -f "$__git_repo_path"/rebase-merge/interactive ]; then - __gitcomp "--continue --skip --abort --quit --edit-todo --show-current-patch" + __gitcomp "$__git_rebase_interactive_inprogress_options" return elif [ -d "$__git_repo_path"/rebase-apply ] || \ [ -d "$__git_repo_path"/rebase-merge ]; then - __gitcomp "--continue --skip --abort --quit --show-current-patch" + __gitcomp "$__git_rebase_inprogress_options" return fi __git_complete_strategy && return @@ -1993,20 +2293,13 @@ _git_rebase () __gitcomp "$__git_whitespacelist" "" "${cur##--whitespace=}" return ;; + --onto=*) + __git_complete_refs --cur="${cur##--onto=}" + return + ;; --*) - __gitcomp " - --onto --merge --strategy --interactive - --rebase-merges --preserve-merges --stat --no-stat - --committer-date-is-author-date --ignore-date - --ignore-whitespace --whitespace= - --autosquash --no-autosquash - --fork-point --no-fork-point - --autostash --no-autostash - --verify --no-verify - --keep-empty --root --force-rebase --no-ff - --rerere-autoupdate - --exec - " + __gitcomp_builtin rebase "" \ + "$__git_rebase_interactive_inprogress_options" return esac @@ -2133,10 +2426,61 @@ _git_status () __git_complete_index_file "$complete_opt" } +_git_switch () +{ + local dwim_opt="$(__git_checkout_default_dwim_mode)" + + case "$prev" in + -c|-C|--orphan) + # Complete local branches (and DWIM branch + # remote branch names) for an option argument + # specifying a new branch name. This is for + # convenience, assuming new branches are + # possibly based on pre-existing branch names. + __git_complete_refs $dwim_opt --mode="heads" + return + ;; + *) + ;; + esac + + case "$cur" in + --conflict=*) + __gitcomp "diff3 merge" "" "${cur##--conflict=}" + ;; + --*) + __gitcomp_builtin switch + ;; + *) + # Unlike in git checkout, git switch --orphan does not take + # a start point. Thus we really have nothing to complete after + # the branch name. + if [ -n "$(__git_find_on_cmdline "--orphan")" ]; then + return + fi + + # At this point, we've already handled special completion for + # -c/-C, and --orphan. There are 3 main things left to + # complete: + # 1) a start-point for -c/-C or -d/--detach + # 2) a remote head, for --track + # 3) a branch name, possibly including DWIM remote branches + + if [ -n "$(__git_find_on_cmdline "-c -C -d --detach")" ]; then + __git_complete_refs --mode="refs" + elif [ -n "$(__git_find_on_cmdline "--track")" ]; then + __git_complete_refs --mode="remote-heads" + else + __git_complete_refs $dwim_opt --mode="heads" + fi + ;; + esac +} + __git_config_get_set_variables () { local prevword word config_file= c=$cword - while [ $c -gt 1 ]; do + while [ $c -gt "$__git_cmd_idx" ]; do word="${words[c]}" case "$word" in --system|--global|--local|--file=*) @@ -2159,181 +2503,282 @@ __git_config_vars= __git_compute_config_vars () { test -n "$__git_config_vars" || - __git_config_vars="$(git help --config-for-completion | sort | uniq)" + __git_config_vars="$(git help --config-for-completion | sort -u)" } -_git_config () +# Completes possible values of various configuration variables. +# +# Usage: __git_complete_config_variable_value [<option>]... +# --varname=<word>: The name of the configuration variable whose value is +# to be completed. Defaults to the previous word on the +# command line. +# --cur=<word>: The current value to be completed. Defaults to the current +# word to be completed. +__git_complete_config_variable_value () { - local varname + local varname="$prev" cur_="$cur" + + while test $# != 0; do + case "$1" in + --varname=*) varname="${1##--varname=}" ;; + --cur=*) cur_="${1##--cur=}" ;; + *) return 1 ;; + esac + shift + done if [ "${BASH_VERSINFO[0]:-0}" -ge 4 ]; then - varname="${prev,,}" + varname="${varname,,}" else - varname="$(echo "$prev" |tr A-Z a-z)" + varname="$(echo "$varname" |tr A-Z a-z)" fi case "$varname" in branch.*.remote|branch.*.pushremote) - __gitcomp_nl "$(__git_remotes)" + __gitcomp_nl "$(__git_remotes)" "" "$cur_" return ;; branch.*.merge) - __git_complete_refs + __git_complete_refs --cur="$cur_" return ;; branch.*.rebase) - __gitcomp "false true merges preserve interactive" + __gitcomp "false true merges preserve interactive" "" "$cur_" return ;; remote.pushdefault) - __gitcomp_nl "$(__git_remotes)" + __gitcomp_nl "$(__git_remotes)" "" "$cur_" return ;; remote.*.fetch) - local remote="${prev#remote.}" + local remote="${varname#remote.}" remote="${remote%.fetch}" - if [ -z "$cur" ]; then + if [ -z "$cur_" ]; then __gitcomp_nl "refs/heads/" "" "" "" return fi - __gitcomp_nl "$(__git_refs_remotes "$remote")" + __gitcomp_nl "$(__git_refs_remotes "$remote")" "" "$cur_" return ;; remote.*.push) - local remote="${prev#remote.}" + local remote="${varname#remote.}" remote="${remote%.push}" __gitcomp_nl "$(__git for-each-ref \ - --format='%(refname):%(refname)' refs/heads)" + --format='%(refname):%(refname)' refs/heads)" "" "$cur_" return ;; pull.twohead|pull.octopus) __git_compute_merge_strategies - __gitcomp "$__git_merge_strategies" - return - ;; - color.branch|color.diff|color.interactive|\ - color.showbranch|color.status|color.ui) - __gitcomp "always never auto" + __gitcomp "$__git_merge_strategies" "" "$cur_" return ;; color.pager) - __gitcomp "false true" + __gitcomp "false true" "" "$cur_" return ;; color.*.*) __gitcomp " normal black red green yellow blue magenta cyan white bold dim ul blink reverse - " + " "" "$cur_" + return + ;; + color.*) + __gitcomp "false true always never auto" "" "$cur_" return ;; diff.submodule) - __gitcomp "log short" + __gitcomp "$__git_diff_submodule_formats" "" "$cur_" return ;; help.format) - __gitcomp "man info web html" + __gitcomp "man info web html" "" "$cur_" return ;; log.date) - __gitcomp "$__git_log_date_formats" + __gitcomp "$__git_log_date_formats" "" "$cur_" return ;; sendemail.aliasfiletype) - __gitcomp "mutt mailrc pine elm gnus" + __gitcomp "mutt mailrc pine elm gnus" "" "$cur_" return ;; sendemail.confirm) - __gitcomp "$__git_send_email_confirm_options" + __gitcomp "$__git_send_email_confirm_options" "" "$cur_" return ;; sendemail.suppresscc) - __gitcomp "$__git_send_email_suppresscc_options" + __gitcomp "$__git_send_email_suppresscc_options" "" "$cur_" return ;; sendemail.transferencoding) - __gitcomp "7bit 8bit quoted-printable base64" - return - ;; - --get|--get-all|--unset|--unset-all) - __gitcomp_nl "$(__git_config_get_set_variables)" + __gitcomp "7bit 8bit quoted-printable base64" "" "$cur_" return ;; *.*) return ;; esac - case "$cur" in - --*) - __gitcomp_builtin config - return - ;; +} + +# Completes configuration sections, subsections, variable names. +# +# Usage: __git_complete_config_variable_name [<option>]... +# --cur=<word>: The current configuration section/variable name to be +# completed. Defaults to the current word to be completed. +# --sfx=<suffix>: A suffix to be appended to each fully completed +# configuration variable name (but not to sections or +# subsections) instead of the default space. +__git_complete_config_variable_name () +{ + local cur_="$cur" sfx + + while test $# != 0; do + case "$1" in + --cur=*) cur_="${1##--cur=}" ;; + --sfx=*) sfx="${1##--sfx=}" ;; + *) return 1 ;; + esac + shift + done + + case "$cur_" in branch.*.*) - local pfx="${cur%.*}." cur_="${cur##*.}" - __gitcomp "remote pushRemote merge mergeOptions rebase" "$pfx" "$cur_" + local pfx="${cur_%.*}." + cur_="${cur_##*.}" + __gitcomp "remote pushRemote merge mergeOptions rebase" "$pfx" "$cur_" "$sfx" return ;; branch.*) - local pfx="${cur%.*}." cur_="${cur#*.}" + local pfx="${cur%.*}." + cur_="${cur#*.}" __gitcomp_direct "$(__git_heads "$pfx" "$cur_" ".")" - __gitcomp_nl_append $'autoSetupMerge\nautoSetupRebase\n' "$pfx" "$cur_" + __gitcomp_nl_append $'autoSetupMerge\nautoSetupRebase\n' "$pfx" "$cur_" "$sfx" return ;; guitool.*.*) - local pfx="${cur%.*}." cur_="${cur##*.}" + local pfx="${cur_%.*}." + cur_="${cur_##*.}" __gitcomp " argPrompt cmd confirm needsFile noConsole noRescan prompt revPrompt revUnmerged title - " "$pfx" "$cur_" + " "$pfx" "$cur_" "$sfx" return ;; difftool.*.*) - local pfx="${cur%.*}." cur_="${cur##*.}" - __gitcomp "cmd path" "$pfx" "$cur_" + local pfx="${cur_%.*}." + cur_="${cur_##*.}" + __gitcomp "cmd path" "$pfx" "$cur_" "$sfx" return ;; man.*.*) - local pfx="${cur%.*}." cur_="${cur##*.}" - __gitcomp "cmd path" "$pfx" "$cur_" + local pfx="${cur_%.*}." + cur_="${cur_##*.}" + __gitcomp "cmd path" "$pfx" "$cur_" "$sfx" return ;; mergetool.*.*) - local pfx="${cur%.*}." cur_="${cur##*.}" - __gitcomp "cmd path trustExitCode" "$pfx" "$cur_" + local pfx="${cur_%.*}." + cur_="${cur_##*.}" + __gitcomp "cmd path trustExitCode" "$pfx" "$cur_" "$sfx" return ;; pager.*) - local pfx="${cur%.*}." cur_="${cur#*.}" + local pfx="${cur_%.*}." + cur_="${cur_#*.}" __git_compute_all_commands - __gitcomp_nl "$__git_all_commands" "$pfx" "$cur_" + __gitcomp_nl "$__git_all_commands" "$pfx" "$cur_" "$sfx" return ;; remote.*.*) - local pfx="${cur%.*}." cur_="${cur##*.}" + local pfx="${cur_%.*}." + cur_="${cur_##*.}" __gitcomp " url proxy fetch push mirror skipDefaultUpdate receivepack uploadpack tagOpt pushurl - " "$pfx" "$cur_" + " "$pfx" "$cur_" "$sfx" return ;; remote.*) - local pfx="${cur%.*}." cur_="${cur#*.}" + local pfx="${cur_%.*}." + cur_="${cur_#*.}" __gitcomp_nl "$(__git_remotes)" "$pfx" "$cur_" "." - __gitcomp_nl_append "pushDefault" "$pfx" "$cur_" + __gitcomp_nl_append "pushDefault" "$pfx" "$cur_" "$sfx" return ;; url.*.*) - local pfx="${cur%.*}." cur_="${cur##*.}" - __gitcomp "insteadOf pushInsteadOf" "$pfx" "$cur_" + local pfx="${cur_%.*}." + cur_="${cur_##*.}" + __gitcomp "insteadOf pushInsteadOf" "$pfx" "$cur_" "$sfx" return ;; *.*) __git_compute_config_vars - __gitcomp "$__git_config_vars" + __gitcomp "$__git_config_vars" "" "$cur_" "$sfx" ;; *) __git_compute_config_vars - __gitcomp "$(echo "$__git_config_vars" | sed 's/\.[^ ]*/./g')" + __gitcomp "$(echo "$__git_config_vars" | + awk -F . '{ + sections[$1] = 1 + } + END { + for (s in sections) + print s "." + } + ')" "" "$cur_" + ;; + esac +} + +# Completes '='-separated configuration sections/variable names and values +# for 'git -c section.name=value'. +# +# Usage: __git_complete_config_variable_name_and_value [<option>]... +# --cur=<word>: The current configuration section/variable name/value to be +# completed. Defaults to the current word to be completed. +__git_complete_config_variable_name_and_value () +{ + local cur_="$cur" + + while test $# != 0; do + case "$1" in + --cur=*) cur_="${1##--cur=}" ;; + *) return 1 ;; + esac + shift + done + + case "$cur_" in + *=*) + __git_complete_config_variable_value \ + --varname="${cur_%%=*}" --cur="${cur_#*=}" + ;; + *) + __git_complete_config_variable_name --cur="$cur_" --sfx='=' + ;; + esac +} + +_git_config () +{ + case "$prev" in + --get|--get-all|--unset|--unset-all) + __gitcomp_nl "$(__git_config_get_set_variables)" + return + ;; + *.*) + __git_complete_config_variable_value + return + ;; + esac + case "$cur" in + --*) + __gitcomp_builtin config + ;; + *) + __git_complete_config_variable_name + ;; esac } @@ -2395,6 +2840,10 @@ _git_remote () _git_replace () { case "$cur" in + --format=*) + __gitcomp "short medium long" "" "${cur##--format=}" + return + ;; --*) __gitcomp_builtin replace return @@ -2427,7 +2876,29 @@ _git_reset () __git_complete_refs } -__git_revert_inprogress_options="--continue --quit --abort" +_git_restore () +{ + case "$prev" in + -s) + __git_complete_refs + return + ;; + esac + + case "$cur" in + --conflict=*) + __gitcomp "diff3 merge" "" "${cur##--conflict=}" + ;; + --source=*) + __git_complete_refs --cur="${cur##--source=}" + ;; + --*) + __gitcomp_builtin restore + ;; + esac +} + +__git_revert_inprogress_options=$__git_sequencer_inprogress_options _git_revert () { @@ -2436,6 +2907,7 @@ _git_revert () __gitcomp "$__git_revert_inprogress_options" return fi + __git_complete_strategy && return case "$cur" in --*) __gitcomp_builtin revert "" \ @@ -2493,9 +2965,18 @@ _git_show () __gitcomp "$__git_diff_submodule_formats" "" "${cur##--submodule=}" return ;; + --color-moved=*) + __gitcomp "$__git_color_moved_opts" "" "${cur##--color-moved=}" + return + ;; + --color-moved-ws=*) + __gitcomp "$__git_color_moved_ws_opts" "" "${cur##--color-moved-ws=}" + return + ;; --*) - __gitcomp "--pretty= --format= --abbrev-commit --oneline - --show-signature + __gitcomp "--pretty= --format= --abbrev-commit --no-abbrev-commit + --oneline --show-signature + --expand-tabs --expand-tabs= --no-expand-tabs $__git_diff_common_options " return @@ -2515,72 +2996,78 @@ _git_show_branch () __git_complete_revlist } +_git_sparse_checkout () +{ + local subcommands="list init set disable" + local subcommand="$(__git_find_on_cmdline "$subcommands")" + if [ -z "$subcommand" ]; then + __gitcomp "$subcommands" + return + fi + + case "$subcommand,$cur" in + init,--*) + __gitcomp "--cone" + ;; + set,--*) + __gitcomp "--stdin" + ;; + *) + ;; + esac +} + _git_stash () { - local save_opts='--all --keep-index --no-keep-index --quiet --patch --include-untracked' local subcommands='push list show apply clear drop pop create branch' local subcommand="$(__git_find_on_cmdline "$subcommands save")" - if [ -n "$(__git_find_on_cmdline "-p")" ]; then - subcommand="push" - fi + if [ -z "$subcommand" ]; then - case "$cur" in - --*) - __gitcomp "$save_opts" + case "$((cword - __git_cmd_idx)),$cur" in + *,--*) + __gitcomp_builtin stash_push ;; - sa*) - if [ -z "$(__git_find_on_cmdline "$save_opts")" ]; then - __gitcomp "save" - fi + 1,sa*) + __gitcomp "save" ;; - *) - if [ -z "$(__git_find_on_cmdline "$save_opts")" ]; then - __gitcomp "$subcommands" - fi + 1,*) + __gitcomp "$subcommands" ;; esac - else - case "$subcommand,$cur" in - push,--*) - __gitcomp "$save_opts --message" - ;; - save,--*) - __gitcomp "$save_opts" - ;; - apply,--*|pop,--*) - __gitcomp "--index --quiet" - ;; - drop,--*) - __gitcomp "--quiet" - ;; - list,--*) - __gitcomp "--name-status --oneline --patch-with-stat" - ;; - show,--*|branch,--*) - ;; - branch,*) - if [ $cword -eq 3 ]; then - __git_complete_refs - else - __gitcomp_nl "$(__git stash list \ - | sed -n -e 's/:.*//p')" - fi - ;; - show,*|apply,*|drop,*|pop,*) + return + fi + + case "$subcommand,$cur" in + list,--*) + # NEEDSWORK: can we somehow unify this with the options in _git_log() and _git_show() + __gitcomp_builtin stash_list "$__git_log_common_options $__git_diff_common_options" + ;; + show,--*) + __gitcomp_builtin stash_show "$__git_diff_common_options" + ;; + *,--*) + __gitcomp_builtin "stash_$subcommand" + ;; + branch,*) + if [ $cword -eq $((__git_cmd_idx+2)) ]; then + __git_complete_refs + else __gitcomp_nl "$(__git stash list \ | sed -n -e 's/:.*//p')" - ;; - *) - ;; - esac - fi + fi + ;; + show,*|apply,*|drop,*|pop,*) + __gitcomp_nl "$(__git stash list \ + | sed -n -e 's/:.*//p')" + ;; + esac } _git_submodule () { __git_has_doubledash && return - local subcommands="add status init deinit update summary foreach sync" + local subcommands="add status init deinit update set-branch set-url summary foreach sync absorbgitdirs" local subcommand="$(__git_find_on_cmdline "$subcommands")" if [ -z "$subcommand" ]; then case "$cur" in @@ -2611,6 +3098,9 @@ _git_submodule () --force --rebase --merge --reference --depth --recursive --jobs " ;; + set-branch,--*) + __gitcomp "--default --branch" + ;; summary,--*) __gitcomp "--cached --files --summary-limit" ;; @@ -2641,6 +3131,7 @@ _git_svn () --log-window-size= --no-checkout --quiet --repack-flags --use-log-author --localtime --add-author-from + --recursive --ignore-paths= --include-paths= $remote_opts " local init_opts=" @@ -2722,7 +3213,7 @@ _git_svn () _git_tag () { - local i c=1 f=0 + local i c="$__git_cmd_idx" f=0 while [ $c -lt $cword ]; do i="${words[c]}" case "$i" in @@ -2762,33 +3253,84 @@ _git_whatchanged () _git_log } +__git_complete_worktree_paths () +{ + local IFS=$'\n' + # Generate completion reply from worktree list skipping the first + # entry: it's the path of the main worktree, which can't be moved, + # removed, locked, etc. + __gitcomp_nl "$(git worktree list --porcelain | + sed -n -e '2,$ s/^worktree //p')" +} + _git_worktree () { local subcommands="add list lock move prune remove unlock" - local subcommand="$(__git_find_on_cmdline "$subcommands")" - if [ -z "$subcommand" ]; then + local subcommand subcommand_idx + + subcommand="$(__git_find_on_cmdline --show-idx "$subcommands")" + subcommand_idx="${subcommand% *}" + subcommand="${subcommand#* }" + + case "$subcommand,$cur" in + ,*) __gitcomp "$subcommands" - else - case "$subcommand,$cur" in - add,--*) - __gitcomp_builtin worktree_add - ;; - list,--*) - __gitcomp_builtin worktree_list - ;; - lock,--*) - __gitcomp_builtin worktree_lock - ;; - prune,--*) - __gitcomp_builtin worktree_prune - ;; - remove,--*) - __gitcomp "--force" + ;; + *,--*) + __gitcomp_builtin worktree_$subcommand + ;; + add,*) # usage: git worktree add [<options>] <path> [<commit-ish>] + # Here we are not completing an --option, it's either the + # path or a ref. + case "$prev" in + -b|-B) # Complete refs for branch to be created/reseted. + __git_complete_refs ;; - *) + -*) # The previous word is an -o|--option without an + # unstuck argument: have to complete the path for + # the new worktree, so don't list anything, but let + # Bash fall back to filename completion. + ;; + *) # The previous word is not an --option, so it must + # be either the 'add' subcommand, the unstuck + # argument of an option (e.g. branch for -b|-B), or + # the path for the new worktree. + if [ $cword -eq $((subcommand_idx+1)) ]; then + # Right after the 'add' subcommand: have to + # complete the path, so fall back to Bash + # filename completion. + : + else + case "${words[cword-2]}" in + -b|-B) # After '-b <branch>': have to + # complete the path, so fall back + # to Bash filename completion. + ;; + *) # After the path: have to complete + # the ref to be checked out. + __git_complete_refs + ;; + esac + fi ;; esac - fi + ;; + lock,*|remove,*|unlock,*) + __git_complete_worktree_paths + ;; + move,*) + if [ $cword -eq $((subcommand_idx+1)) ]; then + # The first parameter must be an existing working + # tree to be moved. + __git_complete_worktree_paths + else + # The second parameter is the destination: it could + # be any path, so don't list anything, but let Bash + # fall back to filename completion. + : + fi + ;; + esac } __git_complete_common () { @@ -2816,15 +3358,19 @@ __git_support_parseopt_helper () { esac } +__git_have_func () { + declare -f -- "$1" >/dev/null 2>&1 +} + __git_complete_command () { local command="$1" local completion_func="_git_${command//-/_}" - if ! declare -f $completion_func >/dev/null 2>/dev/null && - declare -f _completion_loader >/dev/null 2>/dev/null + if ! __git_have_func $completion_func && + __git_have_func _completion_loader then _completion_loader "git-$command" fi - if declare -f $completion_func >/dev/null 2>/dev/null + if __git_have_func $completion_func then $completion_func return 0 @@ -2841,39 +3387,63 @@ __git_main () { local i c=1 command __git_dir __git_repo_path local __git_C_args C_args_count=0 + local __git_cmd_idx while [ $c -lt $cword ]; do 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|--work-tree|--namespace) ((c++)) ;; - -C) __git_C_args[C_args_count++]=-C + --git-dir=*) + __git_dir="${i#--git-dir=}" + ;; + --git-dir) + ((c++)) + __git_dir="${words[c]}" + ;; + --bare) + __git_dir="." + ;; + --help) + command="help" + break + ;; + -c|--work-tree|--namespace) + ((c++)) + ;; + -C) + __git_C_args[C_args_count++]=-C ((c++)) __git_C_args[C_args_count++]="${words[c]}" ;; - -*) ;; - *) command="$i"; break ;; + -*) + ;; + *) + command="$i" + __git_cmd_idx="$c" + break + ;; esac ((c++)) done - if [ -z "$command" ]; then + if [ -z "${command-}" ]; then case "$prev" in --git-dir|-C|--work-tree) # these need a path argument, let's fall back to # Bash filename completion return ;; - -c|--namespace) + -c) + __git_complete_config_variable_name_and_value + return + ;; + --namespace) # we don't support completing these options' arguments return ;; esac case "$cur" in - --*) __gitcomp " + --*) + __gitcomp " --paginate --no-pager --git-dir= @@ -2891,11 +3461,11 @@ __git_main () " ;; *) - if test -n "$GIT_TESTING_PORCELAIN_COMMAND_LIST" + if test -n "${GIT_TESTING_PORCELAIN_COMMAND_LIST-}" then __gitcomp "$GIT_TESTING_PORCELAIN_COMMAND_LIST" else - __gitcomp "$(git --list-cmds=list-mainporcelain,others,nohelpers,alias,list-complete,config)" + __gitcomp "$(__git --list-cmds=list-mainporcelain,others,nohelpers,alias,list-complete,config)" fi ;; esac @@ -2935,102 +3505,20 @@ __gitk_main () __git_complete_revlist } -if [[ -n ${ZSH_VERSION-} ]] && - # Don't define these functions when sourced from 'git-completion.zsh', - # it has its own implementations. - [[ -z ${GIT_SOURCING_ZSH_COMPLETION-} ]]; then - echo "WARNING: this script is deprecated, please see git-completion.zsh" 1>&2 - - autoload -U +X compinit && compinit - - __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_direct () - { - emulate -L zsh - - local IFS=$'\n' - compset -P '*[=:]' - compadd -Q -- ${=1} && _ret=0 - } - - __gitcomp_nl () - { - emulate -L zsh - - local IFS=$'\n' - compset -P '*[=:]' - compadd -Q -S "${4- }" -p "${2-}" -- ${=1} && _ret=0 - } - - __gitcomp_file_direct () - { - emulate -L zsh - - local IFS=$'\n' - compset -P '*[=:]' - compadd -Q -f -- ${=1} && _ret=0 - } - - __gitcomp_file () - { - emulate -L zsh - - 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 +if [[ -n ${ZSH_VERSION-} && -z ${GIT_SOURCING_ZSH_COMPLETION-} ]]; then + echo "ERROR: this script is obsolete, please see git-completion.zsh" 1>&2 return fi __git_func_wrap () { local cur words cword prev + local __git_cmd_idx=0 _get_comp_words_by_ref -n =: cur words cword prev $1 } -# Setup completion for certain functions defined above by setting common -# variables and workarounds. -# This is NOT a public function; use at your own risk. -__git_complete () +___git_complete () { local wrapper="__git_wrap${2}" eval "$wrapper () { __git_func_wrap $2 ; }" @@ -3038,25 +3526,33 @@ __git_complete () || complete -o default -o nospace -F $wrapper $1 } -# wrapper for backwards compatibility -_git () +# Setup the completion for git commands +# 1: command or alias +# 2: function to call (e.g. `git`, `gitk`, `git_fetch`) +__git_complete () { - __git_wrap__git_main -} + local func -# wrapper for backwards compatibility -_gitk () -{ - __git_wrap__gitk_main + if __git_have_func $2; then + func=$2 + elif __git_have_func __$2_main; then + func=__$2_main + elif __git_have_func _$2; then + func=_$2 + else + echo "ERROR: could not find function '$2'" 1>&2 + return 1 + fi + ___git_complete $1 $func } -__git_complete git __git_main -__git_complete gitk __gitk_main +___git_complete git __git_main +___git_complete gitk __gitk_main # The following are necessary only for Cygwin, and only are needed # when the user has tab-completed the executable name and consequently # included the '.exe' suffix. # -if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then -__git_complete git.exe __git_main +if [ "$OSTYPE" = cygwin ]; then + ___git_complete git.exe __git_main fi |