diff options
26 files changed, 2137 insertions, 1495 deletions
diff --git a/.editorconfig b/.editorconfig index aa18e0e5c..b5321de59 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,7 +4,5 @@ root = true end_of_line = lf insert_final_newline = true charset = utf-8 - -[*.sh] -indent_size = 4 -indent_style = tab +indent_size = 2 +indent_style = space diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index c3990c4ee..e16ebb39b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,4 +1,5 @@ # Plugin owners -plugins/gitfast/ @felipec -plugins/sdk/ @rgoldberg -plugins/git-lfs/ @vietduc01100001 +plugins/aws/ @maksyms +plugins/git-lfs/ @vietduc01100001 +plugins/gitfast/ @felipec +plugins/sdk/ @rgoldberg diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 8c20544c8..788f77368 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,7 +1,7 @@ --- name: Feature request about: Suggest a feature for Oh My Zsh -labels: 'feature' +labels: 'Feature' --- diff --git a/.github/ISSUE_TEMPLATE/support.md b/.github/ISSUE_TEMPLATE/support.md index 697f0c0c8..8c4f08044 100644 --- a/.github/ISSUE_TEMPLATE/support.md +++ b/.github/ISSUE_TEMPLATE/support.md @@ -1,7 +1,7 @@ --- name: Support about: Request support for any problem you're having with Oh My Zsh -labels: 'support' +labels: 'Support' --- diff --git a/lib/functions.zsh b/lib/functions.zsh index 58f0e3fb0..15526cd5f 100644 --- a/lib/functions.zsh +++ b/lib/functions.zsh @@ -134,6 +134,7 @@ zmodload zsh/langinfo # -P causes spaces to be encoded as '%20' instead of '+' function omz_urlencode() { emulate -L zsh + local -a opts zparseopts -D -E -a opts r m P local in_str=$1 diff --git a/lib/history.zsh b/lib/history.zsh index 8d922a30b..794076904 100644 --- a/lib/history.zsh +++ b/lib/history.zsh @@ -6,7 +6,8 @@ function omz_history { if [[ -n "$clear" ]]; then # if -c provided, clobber the history file echo -n >| "$HISTFILE" - echo >&2 History file deleted. Reload the session to see its effects. + fc -p "$HISTFILE" + echo >&2 History file deleted. elif [[ -n "$list" ]]; then # if -l provided, run as if calling `fc' directly builtin fc "$@" @@ -36,3 +37,4 @@ setopt hist_expire_dups_first # delete duplicates first when HISTFILE size excee setopt hist_ignore_dups # ignore duplicated commands history list setopt hist_ignore_space # ignore commands that start with space setopt hist_verify # show command with history expansion to user before running it +setopt share_history # share command history data diff --git a/plugins/aws/README.md b/plugins/aws/README.md index 57c3b54ac..011bbd8b4 100644 --- a/plugins/aws/README.md +++ b/plugins/aws/README.md @@ -15,6 +15,13 @@ plugins=(... aws) It also sets `$AWS_EB_PROFILE` to `<profile>` for the Elastic Beanstalk CLI. Run `asp` without arguments to clear the profile. +* `acp [<profile>]`: in addition to `asp` functionality, it actually changes the profile by + assuming the role specified in the `<profile>` configuration. It supports MFA and sets + `$AWS_ACCESS_KEY_ID`, `$AWS_SECRET_ACCESS_KEY` and `$AWS_SESSION_TOKEN`, if obtained. It + requires the roles to be configured as per the + [official guide](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-role.html). + Run `acp` without arguments to clear the profile. + * `agp`: gets the current value of `$AWS_PROFILE`. * `aws_change_access_key`: changes the AWS access key of a profile. @@ -33,6 +40,36 @@ plugins=(... aws) The plugin creates an `aws_prompt_info` function that you can use in your theme, which displays the current `$AWS_PROFILE`. It uses two variables to control how that is shown: -- ZSH_THEME_AWS_PREFIX: sets the prefix of the AWS_PROFILE. Defaults to `<aws:`. +* ZSH_THEME_AWS_PREFIX: sets the prefix of the AWS_PROFILE. Defaults to `<aws:`. + +* ZSH_THEME_AWS_SUFFIX: sets the suffix of the AWS_PROFILE. Defaults to `>`. + +## Configuration + +[Configuration and credential file settings](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html) by AWS -- ZSH_THEME_AWS_SUFFIX: sets the suffix of the AWS_PROFILE. Defaults to `>`. +### Scenario: IAM roles with a source profile and MFA authentication + +Source profile credentials in `~/.aws/credentials`: + +``` +[source-profile-name] +aws_access_key_id = ... +aws_secret_access_key = ... +``` + +Role configuration in `~/.aws/config`: + +``` +[profile source-profile-name] +mfa_serial = arn:aws:iam::111111111111:mfa/myuser +region = us-east-1 +output = json + +[profile profile-with-role] +role_arn = arn:aws:iam::9999999999999:role/myrole +mfa_serial = arn:aws:iam::111111111111:mfa/myuser +source_profile = source-profile-name +region = us-east-1 +output = json +``` diff --git a/plugins/aws/aws.plugin.zsh b/plugins/aws/aws.plugin.zsh index 7994963c3..e1566b113 100644 --- a/plugins/aws/aws.plugin.zsh +++ b/plugins/aws/aws.plugin.zsh @@ -23,31 +23,131 @@ function asp() { export AWS_EB_PROFILE=$1 } +# AWS profile switch +function acp() { + if [[ -z "$1" ]]; then + unset AWS_DEFAULT_PROFILE AWS_PROFILE AWS_EB_PROFILE + unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN + echo AWS profile cleared. + return + fi + + local -a available_profiles + available_profiles=($(aws_profiles)) + if [[ -z "${available_profiles[(r)$1]}" ]]; then + echo "${fg[red]}Profile '$1' not found in '${AWS_CONFIG_FILE:-$HOME/.aws/config}'" >&2 + echo "Available profiles: ${(j:, :)available_profiles:-no profiles found}${reset_color}" >&2 + return 1 + fi + + local profile="$1" + + # Get fallback credentials for if the aws command fails or no command is run + local aws_access_key_id="$(aws configure get aws_access_key_id --profile $profile)" + local aws_secret_access_key="$(aws configure get aws_secret_access_key --profile $profile)" + local aws_session_token="$(aws configure get aws_session_token --profile $profile)" + + + # First, if the profile has MFA configured, lets get the token and session duration + local mfa_serial="$(aws configure get mfa_serial --profile $profile)" + local sess_duration="$(aws configure get duration_seconds --profile $profile)" + + if [[ -n "$mfa_serial" ]]; then + local -a mfa_opt + local mfa_token + echo -n "Please enter your MFA token for $mfa_serial: " + read -r mfa_token + if [[ -z "$sess_duration" ]]; then + echo -n "Please enter the session duration in seconds (900-43200; default: 3600, which is the default maximum for a role): " + read -r sess_duration + fi + mfa_opt=(--serial-number "$mfa_serial" --token-code "$mfa_token" --duration-seconds "${sess_duration:-3600}") + + # Now see whether we need to just MFA for the current role, or assume a different one + local role_arn="$(aws configure get role_arn --profile $profile)" + local sess_name="$(aws configure get role_session_name --profile $profile)" + + if [[ -n "$role_arn" ]]; then + # Means we need to assume a specified role + aws_command=(aws sts assume-role --role-arn "$role_arn" "${mfa_opt[@]}") + + # Check whether external_id is configured to use while assuming the role + local external_id="$(aws configure get external_id --profile $profile)" + if [[ -n "$external_id" ]]; then + aws_command+=(--external-id "$external_id") + fi + + # Get source profile to use to assume role + local source_profile="$(aws configure get source_profile --profile $profile)" + if [[ -z "$sess_name" ]]; then + sess_name="${source_profile:-profile}" + fi + aws_command+=(--profile="${source_profile:-profile}" --role-session-name "${sess_name}") + + echo "Assuming role $role_arn using profile ${source_profile:-profile}" + else + # Means we only need to do MFA + aws_command=(aws sts get-session-token --profile="$profile" "${mfa_opt[@]}") + echo "Obtaining session token for profile $profile" + fi + + # Format output of aws command for easier processing + aws_command+=(--query '[Credentials.AccessKeyId,Credentials.SecretAccessKey,Credentials.SessionToken]' --output text) + + # Run the aws command to obtain credentials + local -a credentials + credentials=(${(ps:\t:)"$(${aws_command[@]})"}) + + if [[ -n "$credentials" ]]; then + aws_access_key_id="${credentials[1]}" + aws_secret_access_key="${credentials[2]}" + aws_session_token="${credentials[3]}" + fi + fi + + # Switch to AWS profile + if [[ -n "${aws_access_key_id}" && -n "$aws_secret_access_key" ]]; then + export AWS_DEFAULT_PROFILE="$profile" + export AWS_PROFILE="$profile" + export AWS_EB_PROFILE="$profile" + export AWS_ACCESS_KEY_ID="$aws_access_key_id" + export AWS_SECRET_ACCESS_KEY="$aws_secret_access_key" + + if [[ -n "$aws_session_token" ]]; then + export AWS_SESSION_TOKEN="$aws_session_token" + else + unset AWS_SESSION_TOKEN + fi + + echo "Switched to AWS Profile: $profile" + fi +} + function aws_change_access_key() { if [[ -z "$1" ]]; then echo "usage: $0 <profile>" return 1 fi - echo Insert the credentials when asked. + echo "Insert the credentials when asked." asp "$1" || return 1 AWS_PAGER="" aws iam create-access-key AWS_PAGER="" aws configure --profile "$1" - echo You can now safely delete the old access key running \`aws iam delete-access-key --access-key-id ID\` - echo Your current keys are: + echo "You can now safely delete the old access key running \`aws iam delete-access-key --access-key-id ID\`" + echo "Your current keys are:" AWS_PAGER="" aws iam list-access-keys } function aws_profiles() { [[ -r "${AWS_CONFIG_FILE:-$HOME/.aws/config}" ]] || return 1 - grep '\[profile' "${AWS_CONFIG_FILE:-$HOME/.aws/config}"|sed -e 's/.*profile \([a-zA-Z0-9@_\.-]*\).*/\1/' + grep --color=never -Eo '\[.*\]' "${AWS_CONFIG_FILE:-$HOME/.aws/config}" | sed -E 's/^[[:space:]]*\[(profile)?[[:space:]]*([-_[:alnum:]\.@]+)\][[:space:]]*$/\2/g' } function _aws_profiles() { reply=($(aws_profiles)) } -compctl -K _aws_profiles asp aws_change_access_key +compctl -K _aws_profiles asp acp aws_change_access_key # AWS prompt function aws_prompt_info() { @@ -55,7 +155,7 @@ function aws_prompt_info() { echo "${ZSH_THEME_AWS_PREFIX:=<aws:}${AWS_PROFILE}${ZSH_THEME_AWS_SUFFIX:=>}" } -if [ "$SHOW_AWS_PROMPT" != false ]; then +if [[ "$SHOW_AWS_PROMPT" != false && "$RPROMPT" != *'$(aws_prompt_info)'* ]]; then RPROMPT='$(aws_prompt_info)'"$RPROMPT" fi diff --git a/plugins/battery/battery.plugin.zsh b/plugins/battery/battery.plugin.zsh index 755ec8d64..a525fd7da 100644 --- a/plugins/battery/battery.plugin.zsh +++ b/plugins/battery/battery.plugin.zsh @@ -18,10 +18,7 @@ if [[ "$OSTYPE" = darwin* ]]; then } function battery_pct() { - local battery_status="$(ioreg -rc AppleSmartBattery)" - local -i capacity=$(sed -n -e '/MaxCapacity/s/^.*"MaxCapacity"\ =\ //p' <<< $battery_status) - local -i current=$(sed -n -e '/CurrentCapacity/s/^.*"CurrentCapacity"\ =\ //p' <<< $battery_status) - echo $(( current * 100 / capacity )) + pmset -g batt | grep -Eo "\d+%" | cut -d% -f1 } function battery_pct_remaining() { diff --git a/plugins/colored-man-pages/README.md b/plugins/colored-man-pages/README.md index f34941e73..4cbf64d3e 100644 --- a/plugins/colored-man-pages/README.md +++ b/plugins/colored-man-pages/README.md @@ -16,3 +16,17 @@ You can also try to color other pages by prefixing the respective command with ` ```zsh colored git help clone ``` + +## Customization + +The plugin declares global associative array `less_termcap`, which maps termcap capabilities to escape +sequences for the `less` pager. This mapping can be further customized by the user after the plugin is +loaded. Check out sources for more. + +For example: `less_termcap[md]` maps to `LESS_TERMCAP_md` which is the escape sequence that tells `less` +how to print something in bold. It's currently shown in bold red, but if you want to change it, you +can redefine `less_termcap[md]` in your zshrc file, after OMZ is sourced: + +```zsh +less_termcap[md]="${fg_bold[blue]}" # this tells less to print bold text in bold blue +``` diff --git a/plugins/colored-man-pages/colored-man-pages.plugin.zsh b/plugins/colored-man-pages/colored-man-pages.plugin.zsh index ec518472c..37faed672 100644 --- a/plugins/colored-man-pages/colored-man-pages.plugin.zsh +++ b/plugins/colored-man-pages/colored-man-pages.plugin.zsh @@ -1,39 +1,48 @@ -if [[ "$OSTYPE" = solaris* ]] -then - if [[ ! -x "$HOME/bin/nroff" ]] - then - mkdir -p "$HOME/bin" - cat > "$HOME/bin/nroff" <<EOF -#!/bin/sh -if [ -n "\$_NROFF_U" -a "\$1,\$2,\$3" = "-u0,-Tlp,-man" ]; then - shift - exec /usr/bin/nroff -u\$_NROFF_U "\$@" -fi -#-- Some other invocation of nroff -exec /usr/bin/nroff "\$@" -EOF - chmod +x "$HOME/bin/nroff" - fi -fi +# Requires colors autoload. +# See termcap(5). + +# Set up once, and then reuse. This way it supports user overrides after the +# plugin is loaded. +typeset -AHg less_termcap + +# bold & blinking mode +less_termcap[mb]="${fg_bold[red]}" +less_termcap[md]="${fg_bold[red]}" +less_termcap[me]="${reset_color}" +# standout mode +less_termcap[so]="${fg_bold[yellow]}${bg[blue]}" +less_termcap[se]="${reset_color}" +# underlining +less_termcap[us]="${fg_bold[green]}" +less_termcap[ue]="${reset_color}" + +# Absolute path to this file's directory. +typeset __colored_man_pages_dir="${0:A:h}" function colored() { - command env \ - LESS_TERMCAP_mb=$(printf "\e[1;31m") \ - LESS_TERMCAP_md=$(printf "\e[1;31m") \ - LESS_TERMCAP_me=$(printf "\e[0m") \ - LESS_TERMCAP_se=$(printf "\e[0m") \ - LESS_TERMCAP_so=$(printf "\e[1;44;33m") \ - LESS_TERMCAP_ue=$(printf "\e[0m") \ - LESS_TERMCAP_us=$(printf "\e[1;32m") \ - PAGER="${commands[less]:-$PAGER}" \ - _NROFF_U=1 \ - PATH="$HOME/bin:$PATH" \ - "$@" + local -a environment + + # Convert associative array to plain array of NAME=VALUE items. + local k v + for k v in "${(@kv)less_termcap}"; do + environment+=( "LESS_TERMCAP_${k}=${v}" ) + done + + # Prefer `less` whenever available, since we specifically configured + # environment for it. + environment+=( PAGER="${commands[less]:-$PAGER}" ) + + # See ./nroff script. + if [[ "$OSTYPE" = solaris* ]]; then + environment+=( PATH="${__colored_man_pages_dir}:$PATH" ) + fi + + command env $environment "$@" } # Colorize man and dman/debman (from debian-goodies) function man \ - dman \ - debman { - colored $0 "$@" + dman \ + debman { + colored $0 "$@" } diff --git a/plugins/colored-man-pages/nroff b/plugins/colored-man-pages/nroff new file mode 100755 index 000000000..4ae155d29 --- /dev/null +++ b/plugins/colored-man-pages/nroff @@ -0,0 +1,12 @@ +#!/bin/sh + +# The whole point of this wrapper is to replace emboldening factor -u0 with +# -u1 under certain circumstances on Solaris. + +if [ "$1,$2,$3" = "-u0,-Tlp,-man" ]; then + shift + exec /usr/bin/nroff -u1 "$@" +else + # Some other invocation of nroff + exec /usr/bin/nroff "$@" +fi diff --git a/plugins/colorize/colorize.plugin.zsh b/plugins/colorize/colorize.plugin.zsh index 80b69190f..cb5ac36f5 100644 --- a/plugins/colorize/colorize.plugin.zsh +++ b/plugins/colorize/colorize.plugin.zsh @@ -6,7 +6,8 @@ alias cless="colorize_less" ZSH_COLORIZE_PLUGIN_PATH=$0:A colorize_check_requirements() { - local available_tools=("chroma" "pygmentize") + local -a available_tools + available_tools=("chroma" "pygmentize") if [ -z "$ZSH_COLORIZE_TOOL" ]; then if (( $+commands[pygmentize] )); then diff --git a/plugins/fzf/fzf.plugin.zsh b/plugins/fzf/fzf.plugin.zsh index 2f48215d5..524089983 100644 --- a/plugins/fzf/fzf.plugin.zsh +++ b/plugins/fzf/fzf.plugin.zsh @@ -154,10 +154,10 @@ unset -f setup_using_opensuse_package setup_using_debian_package setup_using_bas if [[ -z "$FZF_DEFAULT_COMMAND" ]]; then if (( $+commands[rg] )); then - export FZF_DEFAULT_COMMAND='rg --files --hidden' + export FZF_DEFAULT_COMMAND='rg --files --hidden --glob "!.git/*"' elif (( $+commands[fd] )); then export FZF_DEFAULT_COMMAND='fd --type f --hidden --exclude .git' elif (( $+commands[ag] )); then - export FZF_DEFAULT_COMMAND='ag -l --hidden -g ""' + export FZF_DEFAULT_COMMAND='ag -l --hidden -g "" --ignore .git' fi fi diff --git a/plugins/git/git.plugin.zsh b/plugins/git/git.plugin.zsh index ccb9d8461..bf2619976 100644 --- a/plugins/git/git.plugin.zsh +++ b/plugins/git/git.plugin.zsh @@ -31,11 +31,14 @@ function work_in_progress() { # Check if main exists and use instead of master function git_main_branch() { - if [[ -n "$(git branch --list main)" ]]; then - echo main - else - echo master - fi + local branch + for branch in main trunk; do + if command git show-ref -q --verify refs/heads/$branch; then + echo $branch + return + fi + done + echo master } # diff --git a/plugins/gitfast/_git b/plugins/gitfast/_git index 78a6dbb3d..988f5b1c6 100644 --- a/plugins/gitfast/_git +++ b/plugins/gitfast/_git @@ -2,25 +2,24 @@ # zsh completion wrapper for git # -# Copyright (c) 2012-2013 Felipe Contreras <felipe.contreras@gmail.com> +# Copyright (c) 2012-2020 Felipe Contreras <felipe.contreras@gmail.com> # -# You need git's bash completion script installed somewhere, by default it -# would be the location bash-completion uses. +# The recommended way to install this script is to make a copy of it as a +# file named '_git' inside any directory in your fpath. # -# If your script is somewhere else, you can configure it on your ~/.zshrc: +# For example, create a directory '~/.zsh/', copy this file to '~/.zsh/_git', +# and then add the following to your ~/.zshrc file: # -# zstyle ':completion:*:*:git:*' script ~/.git-completion.zsh +# fpath=(~/.zsh $fpath) # -# The recommended way to install this script is to copy to '~/.zsh/_git', and -# then add the following to your ~/.zshrc file: +# You need git's bash completion script installed. By default bash-completion's +# location will be used (e.g. pkg-config --variable=completionsdir bash-completion). +# +# If your bash completion script is somewhere else, you can specify the +# location in your ~/.zshrc: +# +# zstyle ':completion:*:*:git:*' script ~/.git-completion.bash # -# fpath=(~/.zsh $fpath) - -complete () -{ - # do nothing - return 0 -} zstyle -T ':completion:*:*:git:*' tag-order && \ zstyle ':completion:*:*:git:*' tag-order 'common-commands' @@ -28,18 +27,32 @@ zstyle -T ':completion:*:*:git:*' tag-order && \ zstyle -s ":completion:*:*:git:*" script script if [ -z "$script" ]; then local -a locations - local e + local e bash_completion + + bash_completion=$(pkg-config --variable=completionsdir bash-completion 2>/dev/null) || + bash_completion='/usr/share/bash-completion/completions/' + locations=( - "$(dirname ${funcsourcetrace[1]%:*})/git-completion.bash" - '/etc/bash_completion.d/git' # fedora, old debian - '/usr/share/bash-completion/completions/git' # arch, ubuntu, new debian - '/usr/share/bash-completion/git' # gentoo + "$(dirname ${funcsourcetrace[1]%:*})"/git-completion.bash + "$HOME/.local/share/bash-completion/completions/git" + "$bash_completion/git" + '/etc/bash_completion.d/git' # old debian ) for e in $locations; do test -f $e && script="$e" && break done fi -ZSH_VERSION='' . "$script" + +local old_complete="$functions[complete]" +functions[complete]=: +COMP_WORDBREAKS=':' +GIT_SOURCING_ZSH_COMPLETION=y . "$script" +functions[complete]="$old_complete" + +__gitcompadd () +{ + compadd -Q -p "${2-}" -S "${3- }" ${@[4,-1]} -- ${=1} && _ret=0 +} __gitcomp () { @@ -47,59 +60,85 @@ __gitcomp () local cur_="${3-$cur}" - case "$cur_" in - --*=) - ;; - *) - local c IFS=$' \t\n' - local -a array - for c in ${=1}; do - c="$c${4-}" + [[ "$cur_" == *= ]] && return + + local c IFS=$' \t\n' sfx + for c in ${=1}; do + if [[ $c == "--" ]]; then + [[ "$cur_" == --no-* ]] && continue + __gitcompadd "--no-..." + break + fi + + if [[ -z "${4-}" ]]; then case $c in - --*=*|*.) ;; - *) c="$c " ;; + *=) c="${c%=}"; sfx="=" ;; + *.) sfx="" ;; + *) sfx=" " ;; esac - array+=("$c") - done - compset -P '*[=:]' - compadd -Q -S '' -p "${2-}" -a -- array && _ret=0 - ;; - esac + else + sfx="$4" + fi + __gitcompadd "$c" "${2-}" "$sfx" -q + done } -__gitcomp_direct () +__gitcomp_nl () { emulate -L zsh - local IFS=$'\n' - compset -P '*[=:]' - compadd -Q -- ${=1} && _ret=0 + IFS=$'\n' __gitcompadd "$1" "${2-}" "${4- }" } -__gitcomp_nl () +__gitcomp_file () { emulate -L zsh - local IFS=$'\n' - compset -P '*[=:]' - compadd -Q -S "${4- }" -p "${2-}" -- ${=1} && _ret=0 + compadd -f -p "${2-}" -- ${(f)1} && _ret=0 +} + +__gitcomp_direct () +{ + __gitcomp_nl "$1" "" "" "" +} + +__gitcomp_file_direct () +{ + __gitcomp_file "$1" "" } __gitcomp_nl_append () { - emulate -L zsh + __gitcomp_nl "$@" +} + +__gitcomp_direct_append () +{ + __gitcomp_direct "$@" +} - local IFS=$'\n' - compadd -Q -S "${4- }" -p "${2-}" -- ${=1} && _ret=0 +_git_zsh () +{ + __gitcomp "v1.2" } -__gitcomp_file () +__git_complete_command () { emulate -L zsh - local IFS=$'\n' compset -P '*[=:]' - compadd -Q -p "${2-}" -f -- ${=1} && _ret=0 + + local command="$1" + local completion_func="_git_${command//-/_}" + if (( $+functions[$completion_func] )); then + emulate ksh -c $completion_func + return 0 + elif emulate ksh -c "__git_support_parseopt_helper $command"; then + emulate ksh -c "__git_complete_common $command" + return 0 + else + return 1 + fi } __git_zsh_bash_func () @@ -108,14 +147,12 @@ __git_zsh_bash_func () local command=$1 - local completion_func="_git_${command//-/_}" - declare -f $completion_func >/dev/null && $completion_func && return + __git_complete_command "$command" && return local expansion=$(__git_aliased_command "$command") if [ -n "$expansion" ]; then words[1]=$expansion - completion_func="_git_${expansion//-/_}" - declare -f $completion_func >/dev/null && $completion_func + __git_complete_command "$expansion" fi } @@ -140,9 +177,11 @@ __git_zsh_cmd_common () push:'update remote refs along with associated objects' rebase:'forward-port local commits to the updated upstream head' reset:'reset current HEAD to the specified state' + restore:'restore working tree files' rm:'remove files from the working tree and from the index' show:'show various types of objects' status:'show the working tree status' + switch:'switch branches' tag:'create, list, delete or verify a tag object signed with GPG') _describe -t common-commands 'common commands' list && _ret=0 } @@ -150,8 +189,9 @@ __git_zsh_cmd_common () __git_zsh_cmd_alias () { local -a list - list=(${${${(0)"$(git config -z --get-regexp '^alias\.')"}#alias.}%$'\n'*}) - _describe -t alias-commands 'aliases' list $* && _ret=0 + list=(${${(0)"$(git config -z --get-regexp '^alias\.*')"}#alias.}) + list=(${(f)"$(printf "%s:alias for '%s'\n" ${(f@)list})"}) + _describe -t alias-commands 'aliases' list && _ret=0 } __git_zsh_cmd_all () @@ -166,33 +206,43 @@ __git_zsh_main () { local curcontext="$curcontext" state state_descr line typeset -A opt_args - local -a orig_words + local -a orig_words __git_C_args orig_words=( ${words[@]} ) _arguments -C \ - '(-p --paginate --no-pager)'{-p,--paginate}'[pipe all output into ''less'']' \ - '(-p --paginate)--no-pager[do not pipe git output into a pager]' \ - '--git-dir=-[set the path to the repository]: :_directories' \ - '--bare[treat the repository as a bare repository]' \ + '(-p --paginate -P --no-pager)'{-p,--paginate}'[pipe all output into ''less'']' \ + '(-p --paginate -P --no-pager)'{-P,--no-pager}'[do not pipe git output into a pager]' \ + '(--bare)--git-dir=[set the path to the repository]: :_directories' \ + '(--git-dir)--bare[treat the repository as a bare repository]' \ '(- :)--version[prints the git suite version]' \ - '--exec-path=-[path to where your core git programs are installed]:: :_directories' \ - '--html-path[print the path where git''s HTML documentation is installed]' \ - '--info-path[print the path where the Info files are installed]' \ - '--man-path[print the manpath (see `man(1)`) for the man pages]' \ - '--work-tree=-[set the path to the working tree]: :_directories' \ - '--namespace=-[set the git namespace]' \ + '--exec-path=[path to where your core git programs are installed]: :_directories' \ + '(- :)--exec-path[print the path where your core git programs are installed]' \ + '(- :)--html-path[print the path where git''s HTML documentation is installed]' \ + '(- :)--info-path[print the path where the Info files are installed]' \ + '(- :)--man-path[print the manpath (see `man(1)`) for the man pages]' \ + '--work-tree=[set the path to the working tree]: :_directories' \ + '--namespace=[set the git namespace]:' \ '--no-replace-objects[do not use replacement refs to replace git objects]' \ '(- :)--help[prints the synopsis and a list of the most commonly used commands]: :->arg' \ + '*-C[run as if git was started in the given path]: :_directories' \ + '*-c[pass a configuration parameter to the command]: :->config' \ '(-): :->command' \ '(-)*:: :->arg' && return case $state in (command) - _alternative \ - 'alias-commands:alias:__git_zsh_cmd_alias' \ - 'common-commands:common:__git_zsh_cmd_common' \ - 'all-commands:all:__git_zsh_cmd_all' && _ret=0 + _tags common-commands alias-commands all-commands + while _tags; do + _requested common-commands && __git_zsh_cmd_common + _requested alias-commands && __git_zsh_cmd_alias + _requested all-commands && __git_zsh_cmd_all + let _ret || break + done + ;; + (config) + compset -P '*[=:]' + emulate ksh -c __git_complete_config_variable_name_and_value ;; (arg) local command="${words[1]}" __git_dir @@ -200,9 +250,13 @@ __git_zsh_main () if (( $+opt_args[--bare] )); then __git_dir='.' else - __git_dir=${opt_args[--git-dir]} + __git_dir=${~opt_args[--git-dir]} fi + for x in ${(s.:.)opt_args[-C]}; do + __git_C_args+=('-C' ${~x}) + done + (( $+opt_args[--help] )) && command='help' words=( ${orig_words[@]} ) @@ -227,6 +281,8 @@ _git () emulate ksh -c __${service}_main elif (( $+functions[_${service}] )); then emulate ksh -c _${service} + elif (( $+functions[_${service//-/_}] )); then + emulate ksh -c _${service//-/_} fi let _ret && _default && _ret=0 diff --git a/plugins/gitfast/git-completion.bash b/plugins/gitfast/git-completion.bash index b6ff5dc08..4497a294f 100644 --- a/plugins/gitfast/git-completion.bash +++ b/plugins/gitfast/git-completion.bash @@ -29,25 +29,28 @@ # tell the completion to use commit completion. This also works with aliases # of form "!sh -c '...'". For example, "!sh -c ': git commit ; ... '". # +# Compatible with bash 3.2.57. +# # You can set the following environment variables to influence the behavior of # the completion routines: # # 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). - -case "$COMP_WORDBREAKS" in -*:*) : great ;; -*) COMP_WORDBREAKS="$COMP_WORDBREAKS:" -esac +# 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'). # Discovers the path to the git repository taking any '--git-dir=<path>' and # '-C <path>' options into account and stores it in the $__git_repo_path # 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 @@ -92,6 +95,70 @@ __git () ${__git_dir:+--git-dir="$__git_dir"} "$@" 2>/dev/null } +# Removes backslash escaping, single quotes and double quotes from a word, +# stores the result in the variable $dequoted_word. +# 1: The word to dequote. +__git_dequote () +{ + local rest="$1" len ch + + dequoted_word="" + + while test -n "$rest"; do + len=${#dequoted_word} + dequoted_word="$dequoted_word${rest%%[\\\'\"]*}" + rest="${rest:$((${#dequoted_word}-$len))}" + + case "${rest:0:1}" in + \\) + ch="${rest:1:1}" + case "$ch" in + $'\n') + ;; + *) + dequoted_word="$dequoted_word$ch" + ;; + esac + rest="${rest:2}" + ;; + \') + rest="${rest:1}" + len=${#dequoted_word} + dequoted_word="$dequoted_word${rest%%\'*}" + rest="${rest:$((${#dequoted_word}-$len+1))}" + ;; + \") + rest="${rest:1}" + while test -n "$rest" ; do + len=${#dequoted_word} + dequoted_word="$dequoted_word${rest%%[\\\"]*}" + rest="${rest:$((${#dequoted_word}-$len))}" + case "${rest:0:1}" in + \\) + ch="${rest:1:1}" + case "$ch" in + \"|\\|\$|\`) + dequoted_word="$dequoted_word$ch" + ;; + $'\n') + ;; + *) + dequoted_word="$dequoted_word\\$ch" + ;; + esac + rest="${rest:2}" + ;; + \") + rest="${rest:1}" + break + ;; + esac + done + ;; + esac + done +} + # The following function is based on code from: # # bash_completion - programmable completion functions for bash 3.2+ @@ -234,6 +301,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[@]} @@ -262,15 +342,38 @@ __gitcomp () local cur_="${3-$cur}" case "$cur_" in - --*=) + *=) + ;; + --no-*) + local c i=0 IFS=$' \t\n' + for c in $1; do + if [[ $c == "--" ]]; then + continue + fi + c="$c${4-}" + if [[ $c == "$cur_"* ]]; then + case $c in + --*=|*.) ;; + *) c="$c " ;; + esac + COMPREPLY[i++]="${2-}$c" + fi + done ;; *) local c i=0 IFS=$' \t\n' for c in $1; do + if [[ $c == "--" ]]; then + c="--no-...${4-}" + if [[ $c == "$cur_"* ]]; then + COMPREPLY[i++]="${2-}$c " + fi + break + fi c="$c${4-}" if [[ $c == "$cur_"* ]]; then case $c in - --*=*|*.) ;; + *=|*.) ;; *) c="$c " ;; esac COMPREPLY[i++]="${2-}$c" @@ -280,6 +383,163 @@ __gitcomp () esac } +# Clear the variables caching builtins' options when (re-)sourcing +# the completion script. +if [[ -n ${ZSH_VERSION-} ]]; then + unset ${(M)${(k)parameters[@]}:#__gitcomp_builtin_*} 2>/dev/null +else + unset $(compgen -v __gitcomp_builtin_) +fi + +__gitcomp_builtin_add_default=" --dry-run --verbose --interactive --patch --edit --force --update --renormalize --intent-to-add --all --ignore-removal --refresh --ignore-errors --ignore-missing --chmod= --pathspec-from-file= --pathspec-file-nul --no-dry-run -- --no-verbose --no-interactive --no-patch --no-edit --no-force --no-update --no-renormalize --no-intent-to-add --no-all --no-ignore-removal --no-refresh --no-ignore-errors --no-ignore-missing --no-chmod --no-pathspec-from-file --no-pathspec-file-nul" +__gitcomp_builtin_am_default=" --interactive --3way --quiet --signoff --utf8 --keep --keep-non-patch --message-id --keep-cr --no-keep-cr --scissors --whitespace= --ignore-space-change --ignore-whitespace --directory= --exclude= --include= --patch-format= --reject --resolvemsg= --continue --resolved --skip --abort --quit --show-current-patch --committer-date-is-author-date --ignore-date --rerere-autoupdate --gpg-sign -- --no-interactive --no-3way --no-quiet --no-signoff --no-utf8 --no-keep --no-keep-non-patch --no-message-id --no-scissors --no-whitespace --no-ignore-space-change --no-ignore-whitespace --no-directory --no-exclude --no-include --no-patch-format --no-reject --no-resolvemsg --no-committer-date-is-author-date --no-ignore-date --no-rerere-autoupdate --no-gpg-sign" +__gitcomp_builtin_apply_default=" --exclude= --include= --no-add --stat --numstat --summary --check --index --intent-to-add --cached --apply --3way --build-fake-ancestor= --whitespace= --ignore-space-change --ignore-whitespace --reverse --unidiff-zero --reject --allow-overlap --verbose --inaccurate-eof --recount --directory= --add -- --no-stat --no-numstat --no-summary --no-check --no-index --no-intent-to-add --no-cached --no-apply --no-3way --no-build-fake-ancestor --no-whitespace --no-ignore-space-change --no-ignore-whitespace --no-reverse --no-unidiff-zero --no-reject --no-allow-overlap --no-verbose --no-inaccurate-eof --no-recount --no-directory" +__gitcomp_builtin_archive_default=" --output= --remote= --exec= --no-output -- --no-remote --no-exec" +__gitcomp_builtin_bisect__helper_default=" --next-all --write-terms --bisect-clean-state --check-expected-revs --bisect-reset --bisect-write --check-and-set-terms --bisect-next-check --bisect-terms --bisect-start --bisect-next --bisect-auto-next --bisect-autostart --no-log --log" +__gitcomp_builtin_blame_default=" --incremental --root --show-stats --progress --score-debug --show-name --show-number --porcelain --line-porcelain --show-email --ignore-rev= --ignore-revs-file= --color-lines --color-by-age --minimal --contents= --abbrev --no-incremental -- --no-root --no-show-stats --no-progress --no-score-debug --no-show-name --no-show-number --no-porcelain --no-line-porcelain --no-show-email --no-ignore-rev --no-ignore-revs-file --no-color-lines --no-color-by-age --no-minimal --no-contents --no-abbrev" +__gitcomp_builtin_branch_default=" --verbose --quiet --track --set-upstream-to= --unset-upstream --color --remotes --contains --no-contains --abbrev --all --delete --move --copy --list --show-current --create-reflog --edit-description --merged --no-merged --column --sort= --points-at= --ignore-case --format= -- --no-verbose --no-quiet --no-track --no-set-upstream-to --no-unset-upstream --no-color --no-remotes --no-abbrev --no-all --no-delete --no-move --no-copy --no-list --no-show-current --no-create-reflog --no-edit-description --no-column --no-points-at --no-ignore-case --no-format" +__gitcomp_builtin_bugreport_default=" --output-directory= --suffix= --no-output-directory -- --no-suffix" +__gitcomp_builtin_cat_file_default=" --textconv --filters --path= --allow-unknown-type --buffer --batch --batch-check --follow-symlinks --batch-all-objects --unordered --no-path -- --no-allow-unknown-type --no-buffer --no-follow-symlinks --no-batch-all-objects --no-unordered" +__gitcomp_builtin_check_attr_default=" --all --cached --stdin --no-all -- --no-cached --no-stdin" +__gitcomp_builtin_check_ignore_default=" --quiet --verbose --stdin --non-matching --no-index --index -- --no-quiet --no-verbose --no-stdin --no-non-matching" +__gitcomp_builtin_check_mailmap_default=" --stdin --no-stdin" +__gitcomp_builtin_checkout_default=" --guess --overlay --quiet --recurse-submodules --progress --merge --conflict= --detach --track --orphan= --ignore-other-worktrees --ours --theirs --patch --ignore-skip-worktree-bits --pathspec-from-file= --pathspec-file-nul --no-guess -- --no-overlay --no-quiet --no-recurse-submodules --no-progress --no-merge --no-conflict --no-detach --no-track --no-orphan --no-ignore-other-worktrees --no-patch --no-ignore-skip-worktree-bits --no-pathspec-from-file --no-pathspec-file-nul" +__gitcomp_builtin_checkout_index_default=" --all --force --quiet --no-create --index --stdin --temp --prefix= --stage= --create -- --no-all --no-force --no-quiet --no-index --no-stdin --no-temp --no-prefix" +__gitcomp_builtin_cherry_default=" --abbrev --verbose --no-abbrev -- --no-verbose" +__gitcomp_builtin_cherry_pick_default=" --quit --continue --abort --skip --cleanup= --no-commit --edit --signoff --mainline= --rerere-autoupdate --strategy= --strategy-option= --gpg-sign --ff --allow-empty --allow-empty-message --keep-redundant-commits --commit -- --no-cleanup --no-edit --no-signoff --no-mainline --no-rerere-autoupdate --no-strategy --no-strategy-option --no-gpg-sign --no-ff --no-allow-empty --no-allow-empty-message --no-keep-redundant-commits" +__gitcomp_builtin_clean_default=" --quiet --dry-run --interactive --exclude= --no-quiet -- --no-dry-run --no-interactive" +__gitcomp_builtin_clone_default=" --verbose --quiet --progress --no-checkout --bare --mirror --local --no-hardlinks --shared --recurse-submodules --recursive --jobs= --template= --reference= --reference-if-able= --dissociate --origin= --branch= --upload-pack= --depth= --shallow-since= --shallow-exclude= --single-branch --no-tags --shallow-submodules --separate-git-dir= --config= --server-option= --ipv4 --ipv6 --filter= --remote-submodules --sparse --checkout --hardlinks --tags -- --no-verbose --no-quiet --no-progress --no-bare --no-mirror --no-local --no-shared --no-recurse-submodules --no-recursive --no-jobs --no-template --no-reference --no-reference-if-able --no-dissociate --no-origin --no-branch --no-upload-pack --no-depth --no-shallow-since --no-shallow-exclude --no-single-branch --no-shallow-submodules --no-separate-git-dir --no-config --no-server-option --no-ipv4 --no-ipv6 --no-filter --no-remote-submodules --no-sparse" +__gitcomp_builtin_column_default=" --command= --mode --raw-mode= --width= --indent= --nl= --padding= --no-command -- --no-mode --no-raw-mode --no-width --no-indent --no-nl --no-padding" +__gitcomp_builtin_commit_default=" --quiet --verbose --file= --author= --date= --message= --reedit-message= --reuse-message= --fixup= --squash= --reset-author --signoff --template= --edit --cleanup= --status --gpg-sign --all --include --interactive --patch --only --no-verify --dry-run --short --branch --ahead-behind --porcelain --long --null --amend --no-post-rewrite --untracked-files --pathspec-from-file= --pathspec-file-nul --verify --post-rewrite -- --no-quiet --no-verbose --no-file --no-author --no-date --no-message --no-reedit-message --no-reuse-message --no-fixup --no-squash --no-reset-author --no-signoff --no-template --no-edit --no-cleanup --no-status --no-gpg-sign --no-all --no-include --no-interactive --no-patch --no-only --no-dry-run --no-short --no-branch --no-ahead-behind --no-porcelain --no-long --no-null --no-amend --no-untracked-files --no-pathspec-from-file --no-pathspec-file-nul" +__gitcomp_builtin_commit_graph_default=" --object-dir= --no-object-dir" +__gitcomp_builtin_config_default=" --global --system --local --worktree --file= --blob= --get --get-all --get-regexp --get-urlmatch --replace-all --add --unset --unset-all --rename-section --remove-section --list --edit --get-color --get-colorbool --type= --bool --int --bool-or-int --bool-or-str --path --expiry-date --null --name-only --includes --show-origin --show-scope --default= --no-global -- --no-system --no-local --no-worktree --no-file --no-blob --no-get --no-get-all --no-get-regexp --no-get-urlmatch --no-replace-all --no-add --no-unset --no-unset-all --no-rename-section --no-remove-section --no-list --no-edit --no-get-color --no-get-colorbool --no-type --no-null --no-name-only --no-includes --no-show-origin --no-show-scope --no-default" +__gitcomp_builtin_count_objects_default=" --verbose --human-readable --no-verbose -- --no-human-readable" +__gitcomp_builtin_credential_cache_default=" --timeout= --socket= --no-timeout -- --no-socket" +__gitcomp_builtin_credential_cache__daemon_default=" --debug --no-debug" +__gitcomp_builtin_credential_store_default=" --file= --no-file" +__gitcomp_builtin_describe_default=" --contains --debug --all --tags --long --first-parent --abbrev --exact-match --candidates= --match= --exclude= --always --dirty --broken --no-contains -- --no-debug --no-all --no-tags --no-long --no-first-parent --no-abbrev --no-exact-match --no-candidates --no-match --no-exclude --no-always --no-dirty --no-broken" +__gitcomp_builtin_difftool_default=" --gui --dir-diff --no-prompt --symlinks --tool= --tool-help --trust-exit-code --extcmd= --no-index -- --no-gui --no-dir-diff --no-symlinks --no-tool --no-tool-help --no-trust-exit-code --no-extcmd" +__gitcomp_builtin_env__helper_default=" --type= --default= --exit-code --no-default -- --no-exit-code" +__gitcomp_builtin_fast_export_default=" --progress= --signed-tags= --tag-of-filtered-object= --reencode= --export-marks= --import-marks= --import-marks-if-exists= --fake-missing-tagger --full-tree --use-done-feature --no-data --refspec= --anonymize --anonymize-map= --reference-excluded-parents --show-original-ids --mark-tags --data -- --no-progress --no-signed-tags --no-tag-of-filtered-object --no-reencode --no-export-marks --no-import-marks --no-import-marks-if-exists --no-fake-missing-tagger --no-full-tree --no-use-done-feature --no-refspec --no-anonymize --no-reference-excluded-parents --no-show-original-ids --no-mark-tags" +__gitcomp_builtin_fetch_default=" --verbose --quiet --all --set-upstream --append --upload-pack= --force --multiple --tags --jobs= --prune --prune-tags --recurse-submodules --dry-run --write-fetch-head --keep --update-head-ok --progress --depth= --shallow-since= --shallow-exclude= --deepen= --unshallow --update-shallow --refmap= --server-option= --ipv4 --ipv6 --negotiation-tip= --filter= --auto-maintenance --auto-gc --show-forced-updates --write-commit-graph --stdin --no-verbose -- --no-quiet --no-all --no-set-upstream --no-append --no-upload-pack --no-force --no-multiple --no-tags --no-jobs --no-prune --no-prune-tags --no-recurse-submodules --no-dry-run --no-write-fetch-head --no-keep --no-update-head-ok --no-progress --no-depth --no-shallow-since --no-shallow-exclude --no-deepen --no-update-shallow --no-server-option --no-ipv4 --no-ipv6 --no-negotiation-tip --no-filter --no-auto-maintenance --no-auto-gc --no-show-forced-updates --no-write-commit-graph --no-stdin" +__gitcomp_builtin_fmt_merge_msg_default=" --log --message= --file= --no-log -- --no-message --no-file" +__gitcomp_builtin_for_each_ref_default=" --shell --perl --python --tcl --count= --format= --color --sort= --points-at= --merged --no-merged --contains --no-contains --ignore-case -- --no-shell --no-perl --no-python --no-tcl --no-count --no-format --no-color --no-points-at --no-ignore-case" +__gitcomp_builtin_format_patch_default=" --numbered --no-numbered --signoff --stdout --cover-letter --numbered-files --suffix= --start-number= --reroll-count= --rfc --cover-from-description= --subject-prefix= --output-directory= --keep-subject --no-binary --zero-commit --ignore-if-in-upstream --no-stat --add-header= --to= --cc= --from --in-reply-to= --attach --inline --thread --signature= --base= --signature-file= --quiet --progress --interdiff= --range-diff= --creation-factor= --binary -- --no-numbered --no-signoff --no-stdout --no-cover-letter --no-numbered-files --no-suffix --no-start-number --no-reroll-count --no-cover-from-description --no-zero-commit --no-ignore-if-in-upstream --no-add-header --no-to --no-cc --no-from --no-in-reply-to --no-attach --no-thread --no-signature --no-base --no-signature-file --no-quiet --no-progress --no-interdiff --no-range-diff --no-creation-factor" +__gitcomp_builtin_fsck_default=" --verbose --unreachable --dangling --tags --root --cache --reflogs --full --connectivity-only --strict --lost-found --progress --name-objects --no-verbose -- --no-unreachable --no-dangling --no-tags --no-root --no-cache --no-reflogs --no-full --no-connectivity-only --no-strict --no-lost-found --no-progress --no-name-objects" +__gitcomp_builtin_fsck_objects_default=" --verbose --unreachable --dangling --tags --root --cache --reflogs --full --connectivity-only --strict --lost-found --progress --name-objects --no-verbose -- --no-unreachable --no-dangling --no-tags --no-root --no-cache --no-reflogs --no-full --no-connectivity-only --no-strict --no-lost-found --no-progress --no-name-objects" +__gitcomp_builtin_gc_default=" --quiet --prune --aggressive --keep-largest-pack --no-quiet -- --no-prune --no-aggressive --no-keep-largest-pack" +__gitcomp_builtin_grep_default=" --cached --no-index --untracked --exclude-standard --recurse-submodules --invert-match --ignore-case --word-regexp --text --textconv --recursive --max-depth= --extended-regexp --basic-regexp --fixed-strings --perl-regexp --line-number --column --full-name --files-with-matches --name-only --files-without-match --only-matching --count --color --break --heading --context= --before-context= --after-context= --threads= --show-function --function-context --and --or --not --quiet --all-match --index -- --no-cached --no-untracked --no-exclude-standard --no-recurse-submodules --no-invert-match --no-ignore-case --no-word-regexp --no-text --no-textconv --no-recursive --no-extended-regexp --no-basic-regexp --no-fixed-strings --no-perl-regexp --no-line-number --no-column --no-full-name --no-files-with-matches --no-name-only --no-files-without-match --no-only-matching --no-count --no-color --no-break --no-heading --no-context --no-before-context --no-after-context --no-threads --no-show-function --no-function-context --no-or --no-quiet --no-all-match" +__gitcomp_builtin_hash_object_default=" --stdin --stdin-paths --no-filters --literally --path= --filters -- --no-stdin --no-stdin-paths --no-literally --no-path" +__gitcomp_builtin_help_default=" --all --guides --config --man --web --info --verbose --no-all -- --no-guides --no-config --no-man --no-web --no-info --no-verbose" +__gitcomp_builtin_init_default=" --template= --bare --shared --quiet --separate-git-dir= --initial-branch= --object-format= --no-template -- --no-bare --no-quiet --no-separate-git-dir --no-initial-branch --no-object-format" +__gitcomp_builtin_init_db_default=" --template= --bare --shared --quiet --separate-git-dir= --initial-branch= --object-format= --no-template -- --no-bare --no-quiet --no-separate-git-dir --no-initial-branch --no-object-format" +__gitcomp_builtin_interpret_trailers_default=" --in-place --trim-empty --where= --if-exists= --if-missing= --only-trailers --only-input --unfold --parse --no-divider --trailer= --divider -- --no-in-place --no-trim-empty --no-where --no-if-exists --no-if-missing --no-only-trailers --no-only-input --no-unfold --no-trailer" +__gitcomp_builtin_log_default=" --quiet --source --use-mailmap --mailmap --decorate-refs= --decorate-refs-exclude= --decorate --no-quiet -- --no-source --no-use-mailmap --no-mailmap --no-decorate-refs --no-decorate-refs-exclude --no-decorate" +__gitcomp_builtin_ls_files_default=" --cached --deleted --modified --others --ignored --stage --killed --directory --eol --empty-directory --unmerged --resolve-undo --exclude= --exclude-from= --exclude-per-directory= --exclude-standard --full-name --recurse-submodules --error-unmatch --with-tree= --abbrev --debug --no-cached -- --no-deleted --no-modified --no-others --no-ignored --no-stage --no-killed --no-directory --no-eol --no-empty-directory --no-unmerged --no-resolve-undo --no-exclude-per-directory --no-recurse-submodules --no-error-unmatch --no-with-tree --no-abbrev --no-debug" +__gitcomp_builtin_ls_remote_default=" --quiet --upload-pack= --tags --heads --refs --get-url --sort= --symref --server-option= --no-quiet -- --no-upload-pack --no-tags --no-heads --no-refs --no-get-url --no-symref --no-server-option" +__gitcomp_builtin_ls_tree_default=" --long --name-only --name-status --full-name --full-tree --abbrev --no-long -- --no-name-only --no-name-status --no-full-name --no-full-tree --no-abbrev" +__gitcomp_builtin_merge_default=" --stat --summary --log --squash --commit --edit --cleanup= --ff --ff-only --rerere-autoupdate --verify-signatures --strategy= --strategy-option= --message= --file --verbose --quiet --abort --quit --continue --allow-unrelated-histories --progress --gpg-sign --autostash --overwrite-ignore --signoff --no-verify --verify -- --no-stat --no-summary --no-log --no-squash --no-commit --no-edit --no-cleanup --no-ff --no-rerere-autoupdate --no-verify-signatures --no-strategy --no-strategy-option --no-message --no-verbose --no-quiet --no-abort --no-quit --no-continue --no-allow-unrelated-histories --no-progress --no-gpg-sign --no-autostash --no-overwrite-ignore --no-signoff" +__gitcomp_builtin_merge_base_default=" --all --octopus --independent --is-ancestor --fork-point --no-all" +__gitcomp_builtin_merge_file_default=" --stdout --diff3 --ours --theirs --union --marker-size= --quiet --no-stdout -- --no-diff3 --no-ours --no-theirs --no-union --no-marker-size --no-quiet" +__gitcomp_builtin_mktree_default=" --missing --batch --no-missing -- --no-batch" +__gitcomp_builtin_multi_pack_index_default=" --object-dir= --progress --batch-size= --no-object-dir -- --no-progress" +__gitcomp_builtin_mv_default=" --verbose --dry-run --no-verbose -- --no-dry-run" +__gitcomp_builtin_name_rev_default=" --name-only --tags --refs= --exclude= --all --stdin --undefined --always --no-name-only -- --no-tags --no-refs --no-exclude --no-all --no-stdin --no-undefined --no-always" +__gitcomp_builtin_notes_default=" --ref= --no-ref" +__gitcomp_builtin_pack_objects_default=" --quiet --progress --all-progress --all-progress-implied --index-version= --max-pack-size= --local --incremental --window= --window-memory= --depth= --reuse-delta --reuse-object --delta-base-offset --threads= --non-empty --revs --unpacked --all --reflog --indexed-objects --stdout --include-tag --keep-unreachable --pack-loose-unreachable --unpack-unreachable --sparse --thin --shallow --honor-pack-keep --keep-pack= --compression= --keep-true-parents --use-bitmap-index --write-bitmap-index --filter= --missing= --exclude-promisor-objects --delta-islands --uri-protocol= --no-quiet -- --no-progress --no-all-progress --no-all-progress-implied --no-local --no-incremental --no-window --no-depth --no-reuse-delta --no-reuse-object --no-delta-base-offset --no-threads --no-non-empty --no-revs --no-stdout --no-include-tag --no-keep-unreachable --no-pack-loose-unreachable --no-unpack-unreachable --no-sparse --no-thin --no-shallow --no-honor-pack-keep --no-keep-pack --no-compression --no-keep-true-parents --no-use-bitmap-index --no-write-bitmap-index --no-filter --no-exclude-promisor-objects --no-delta-islands --no-uri-protocol" +__gitcomp_builtin_pack_refs_default=" --all --prune --no-all -- --no-prune" +__gitcomp_builtin_pickaxe_default=" --incremental --root --show-stats --progress --score-debug --show-name --show-number --porcelain --line-porcelain --show-email --ignore-rev= --ignore-revs-file= --color-lines --color-by-age --minimal --contents= --abbrev --no-incremental -- --no-root --no-show-stats --no-progress --no-score-debug --no-show-name --no-show-number --no-porcelain --no-line-porcelain --no-show-email --no-ignore-rev --no-ignore-revs-file --no-color-lines --no-color-by-age --no-minimal --no-contents --no-abbrev" +__gitcomp_builtin_prune_default=" --dry-run --verbose --progress --expire= --exclude-promisor-objects --no-dry-run -- --no-verbose --no-progress --no-expire --no-exclude-promisor-objects" +__gitcomp_builtin_prune_packed_default=" --dry-run --quiet --no-dry-run -- --no-quiet" +__gitcomp_builtin_pull_default=" --verbose --quiet --progress --recurse-submodules --rebase --stat --log --signoff --squash --commit --edit --cleanup= --ff --ff-only --verify-signatures --autostash --strategy= --strategy-option= --gpg-sign --allow-unrelated-histories --all --append --upload-pack= --force --tags --prune --jobs --dry-run --keep --depth= --shallow-since= --shallow-exclude= --deepen= --unshallow --update-shallow --refmap= --server-option= --ipv4 --ipv6 --negotiation-tip= --show-forced-updates --set-upstream --no-verbose -- --no-quiet --no-progress --no-recurse-submodules --no-rebase --no-stat --no-log --no-signoff --no-squash --no-commit --no-edit --no-cleanup --no-ff --no-verify-signatures --no-autostash --no-strategy --no-strategy-option --no-gpg-sign --no-allow-unrelated-histories --no-all --no-append --no-upload-pack --no-force --no-tags --no-prune --no-jobs --no-dry-run --no-keep --no-depth --no-shallow-since --no-shallow-exclude --no-deepen --no-update-shallow --no-server-option --no-ipv4 --no-ipv6 --no-negotiation-tip --no-show-forced-updates --no-set-upstream" +__gitcomp_builtin_push_default=" --verbose --quiet --repo= --all --mirror --delete --tags --dry-run --porcelain --force --force-with-lease --recurse-submodules= --receive-pack= --exec= --set-upstream --progress --prune --no-verify --follow-tags --signed --atomic --push-option= --ipv4 --ipv6 --verify -- --no-verbose --no-quiet --no-repo --no-all --no-mirror --no-delete --no-tags --no-dry-run --no-porcelain --no-force --no-force-with-lease --no-recurse-submodules --no-receive-pack --no-exec --no-set-upstream --no-progress --no-prune --no-follow-tags --no-signed --no-atomic --no-push-option --no-ipv4 --no-ipv6" +__gitcomp_builtin_range_diff_default=" --creation-factor= --no-dual-color --notes --patch --no-patch --unified --function-context --raw --patch-with-raw --patch-with-stat --numstat --shortstat --dirstat --cumulative --dirstat-by-file --check --summary --name-only --name-status --stat --stat-width= --stat-name-width= --stat-graph-width= --stat-count= --compact-summary --binary --full-index --color --ws-error-highlight= --abbrev --src-prefix= --dst-prefix= --line-prefix= --no-prefix --inter-hunk-context= --output-indicator-new= --output-indicator-old= --output-indicator-context= --break-rewrites --find-renames --irreversible-delete --find-copies --find-copies-harder --no-renames --rename-empty --follow --minimal --ignore-all-space --ignore-space-change --ignore-space-at-eol --ignore-cr-at-eol --ignore-blank-lines --indent-heuristic --patience --histogram --diff-algorithm= --anchored= --word-diff --word-diff-regex= --color-words --color-moved --color-moved-ws= --relative --text --exit-code --quiet --ext-diff --textconv --ignore-submodules --submodule --ita-invisible-in-index --ita-visible-in-index --pickaxe-all --pickaxe-regex --find-object= --diff-filter= --output= --dual-color -- --no-creation-factor --no-notes --no-function-context --no-compact-summary --no-full-index --no-color --no-abbrev --no-find-copies-harder --no-rename-empty --no-follow --no-minimal --no-indent-heuristic --no-color-moved --no-color-moved-ws --no-relative --no-text --no-exit-code --no-quiet --no-ext-diff --no-textconv" +__gitcomp_builtin_read_tree_default=" --index-output= --empty --verbose --trivial --aggressive --reset --prefix= --exclude-per-directory= --dry-run --no-sparse-checkout --debug-unpack --recurse-submodules --quiet --sparse-checkout -- --no-empty --no-verbose --no-trivial --no-aggressive --no-reset --no-dry-run --no-debug-unpack --no-recurse-submodules --no-quiet" +__gitcomp_builtin_rebase_default=" --onto= --keep-base --no-verify --quiet --verbose --no-stat --signoff --committer-date-is-author-date --reset-author-date --ignore-whitespace --whitespace= --force-rebase --no-ff --continue --skip --abort --quit --edit-todo --show-current-patch --apply --merge --interactive --rerere-autoupdate --empty= --autosquash --gpg-sign --autostash --exec= --rebase-merges --fork-point --strategy= --strategy-option= --root --reschedule-failed-exec --reapply-cherry-picks --verify --stat --ff -- --no-onto --no-keep-base --no-quiet --no-verbose --no-signoff --no-committer-date-is-author-date --no-reset-author-date --no-ignore-whitespace --no-whitespace --no-force-rebase --no-rerere-autoupdate --no-autosquash --no-gpg-sign --no-autostash --no-exec --no-rebase-merges --no-fork-point --no-strategy --no-strategy-option --no-root --no-reschedule-failed-exec --no-reapply-cherry-picks" +__gitcomp_builtin_rebase__interactive_default=" --ff --rebase-merges --rebase-cousins --autosquash --signoff --verbose --continue --skip --edit-todo --show-current-patch --shorten-ids --expand-ids --check-todo-list --rearrange-squash --add-exec-commands --onto= --restrict-revision= --squash-onto= --upstream= --head-name= --gpg-sign --strategy= --strategy-opts= --switch-to= --onto-name= --cmd= --rerere-autoupdate --reschedule-failed-exec --no-ff -- --no-rebase-merges --no-rebase-cousins --no-autosquash --no-signoff --no-verbose --no-head-name --no-gpg-sign --no-strategy --no-strategy-opts --no-switch-to --no-onto-name --no-cmd --no-rerere-autoupdate --no-reschedule-failed-exec" +__gitcomp_builtin_receive_pack_default=" --quiet --no-quiet" +__gitcomp_builtin_reflog_default=" --quiet --source --use-mailmap --mailmap --decorate-refs= --decorate-refs-exclude= --decorate --no-quiet -- --no-source --no-use-mailmap --no-mailmap --no-decorate-refs --no-decorate-refs-exclude --no-decorate" +__gitcomp_builtin_remote_default=" --verbose --no-verbose" +__gitcomp_builtin_repack_default=" --quiet --local --write-bitmap-index --delta-islands --unpack-unreachable= --keep-unreachable --window= --window-memory= --depth= --threads= --max-pack-size= --pack-kept-objects --keep-pack= --no-quiet -- --no-local --no-write-bitmap-index --no-delta-islands --no-unpack-unreachable --no-keep-unreachable --no-window --no-window-memory --no-depth --no-threads --no-max-pack-size --no-pack-kept-objects --no-keep-pack" +__gitcomp_builtin_replace_default=" --list --delete --edit --graft --convert-graft-file --raw --format= --no-raw -- --no-format" +__gitcomp_builtin_rerere_default=" --rerere-autoupdate --no-rerere-autoupdate" +__gitcomp_builtin_reset_default=" --quiet --mixed --soft --hard --merge --keep --recurse-submodules --patch --intent-to-add --pathspec-from-file= --pathspec-file-nul --no-quiet -- --no-mixed --no-soft --no-hard --no-merge --no-keep --no-recurse-submodules --no-patch --no-intent-to-add --no-pathspec-from-file --no-pathspec-file-nul" +__gitcomp_builtin_restore_default=" --source= --staged --worktree --ignore-unmerged --overlay --quiet --recurse-submodules --progress --merge --conflict= --ours --theirs --patch --ignore-skip-worktree-bits --pathspec-from-file= --pathspec-file-nul --no-source -- --no-staged --no-worktree --no-ignore-unmerged --no-overlay --no-quiet --no-recurse-submodules --no-progress --no-merge --no-conflict --no-patch --no-ignore-skip-worktree-bits --no-pathspec-from-file --no-pathspec-file-nul" +__gitcomp_builtin_revert_default=" --quit --continue --abort --skip --cleanup= --no-commit --edit --signoff --mainline= --rerere-autoupdate --strategy= --strategy-option= --gpg-sign --commit -- --no-cleanup --no-edit --no-signoff --no-mainline --no-rerere-autoupdate --no-strategy --no-strategy-option --no-gpg-sign" +__gitcomp_builtin_rm_default=" --dry-run --quiet --cached --ignore-unmatch --pathspec-from-file= --pathspec-file-nul --no-dry-run -- --no-quiet --no-cached --no-ignore-unmatch --no-pathspec-from-file --no-pathspec-file-nul" +__gitcomp_builtin_send_pack_default=" --verbose --quiet --receive-pack= --exec= --remote= --all --dry-run --mirror --force --signed --push-option= --progress --thin --atomic --stateless-rpc --stdin --helper-status --force-with-lease --no-verbose -- --no-quiet --no-receive-pack --no-exec --no-remote --no-all --no-dry-run --no-mirror --no-force --no-signed --no-push-option --no-progress --no-thin --no-atomic --no-stateless-rpc --no-stdin --no-helper-status --no-force-with-lease" +__gitcomp_builtin_shortlog_default=" --committer --numbered --summary --email --group= --no-committer -- --no-numbered --no-summary --no-email --no-group" +__gitcomp_builtin_show_default=" --quiet --source --use-mailmap --mailmap --decorate-refs= --decorate-refs-exclude= --decorate --no-quiet -- --no-source --no-use-mailmap --no-mailmap --no-decorate-refs --no-decorate-refs-exclude --no-decorate" +__gitcomp_builtin_show_branch_default=" --all --remotes --color --more --list --no-name --current --sha1-name --merge-base --independent --topo-order --topics --sparse --date-order --reflog --name -- --no-all --no-remotes --no-color --no-more --no-list --no-current --no-sha1-name --no-merge-base --no-independent --no-topo-order --no-topics --no-sparse --no-date-order" +__gitcomp_builtin_show_index_default=" --object-format= --no-object-format" +__gitcomp_builtin_show_ref_default=" --tags --heads --verify --head --dereference --hash --abbrev --quiet --exclude-existing --no-tags -- --no-heads --no-verify --no-head --no-dereference --no-hash --no-abbrev --no-quiet" +__gitcomp_builtin_sparse_checkout_default="" +__gitcomp_builtin_stage_default=" --dry-run --verbose --interactive --patch --edit --force --update --renormalize --intent-to-add --all --ignore-removal --refresh --ignore-errors --ignore-missing --chmod= --pathspec-from-file= --pathspec-file-nul --no-dry-run -- --no-verbose --no-interactive --no-patch --no-edit --no-force --no-update --no-renormalize --no-intent-to-add --no-all --no-ignore-removal --no-refresh --no-ignore-errors --no-ignore-missing --no-chmod --no-pathspec-from-file --no-pathspec-file-nul" +__gitcomp_builtin_stash_default="" +__gitcomp_builtin_status_default=" --verbose --short --branch --show-stash --ahead-behind --porcelain --long --null --untracked-files --ignored --ignore-submodules --column --no-renames --find-renames --renames -- --no-verbose --no-short --no-branch --no-show-stash --no-ahead-behind --no-porcelain --no-long --no-null --no-untracked-files --no-ignored --no-ignore-submodules --no-column" +__gitcomp_builtin_stripspace_default=" --strip-comments --comment-lines" +__gitcomp_builtin_switch_default=" --create= --force-create= --guess --discard-changes --quiet --recurse-submodules --progress --merge --conflict= --detach --track --orphan= --ignore-other-worktrees --no-create -- --no-force-create --no-guess --no-discard-changes --no-quiet --no-recurse-submodules --no-progress --no-merge --no-conflict --no-detach --no-track --no-orphan --no-ignore-other-worktrees" +__gitcomp_builtin_symbolic_ref_default=" --quiet --delete --short --no-quiet -- --no-delete --no-short" +__gitcomp_builtin_tag_default=" --list --delete --verify --annotate --message= --file= --edit --sign --cleanup= --local-user= --force --create-reflog --column --contains --no-contains --merged --no-merged --sort= --points-at --format= --color --ignore-case -- --no-annotate --no-file --no-edit --no-sign --no-cleanup --no-local-user --no-force --no-create-reflog --no-column --no-points-at --no-format --no-color --no-ignore-case" +__gitcomp_builtin_update_index_default=" --ignore-submodules --add --replace --remove --unmerged --refresh --really-refresh --cacheinfo --chmod= --assume-unchanged --no-assume-unchanged --skip-worktree --no-skip-worktree --ignore-skip-worktree-entries --info-only --force-remove --stdin --index-info --unresolve --again --ignore-missing --verbose --clear-resolve-undo --index-version= --split-index --untracked-cache --test-untracked-cache --force-untracked-cache --force-write-index --fsmonitor --fsmonitor-valid --no-fsmonitor-valid -- --no-ignore-submodules --no-add --no-replace --no-remove --no-unmerged --no-ignore-skip-worktree-entries --no-info-only --no-force-remove --no-ignore-missing --no-verbose --no-index-version --no-split-index --no-untracked-cache --no-test-untracked-cache --no-force-untracked-cache --no-force-write-index --no-fsmonitor" +__gitcomp_builtin_update_ref_default=" --no-deref --stdin --create-reflog --deref -- --no-stdin --no-create-reflog" +__gitcomp_builtin_update_server_info_default=" --force --no-force" +__gitcomp_builtin_upload_pack_default=" --stateless-rpc --advertise-refs --strict --timeout= --no-stateless-rpc -- --no-advertise-refs --no-strict --no-timeout" +__gitcomp_builtin_verify_commit_default=" --verbose --raw --no-verbose -- --no-raw" +__gitcomp_builtin_verify_pack_default=" --verbose --stat-only --object-format= --no-verbose -- --no-stat-only --no-object-format" +__gitcomp_builtin_verify_tag_default=" --verbose --raw --format= --no-verbose -- --no-raw --no-format" +__gitcomp_builtin_version_default=" --build-options --no-build-options" +__gitcomp_builtin_whatchanged_default=" --quiet --source --use-mailmap --mailmap --decorate-refs= --decorate-refs-exclude= --decorate --no-quiet -- --no-source --no-use-mailmap --no-mailmap --no-decorate-refs --no-decorate-refs-exclude --no-decorate" +__gitcomp_builtin_write_tree_default=" --missing-ok --prefix= --no-missing-ok -- --no-prefix" +__gitcomp_builtin_send_email_default=" --numbered --no-numbered --signoff --stdout --cover-letter --numbered-files --suffix= --start-number= --reroll-count= --rfc --cover-from-description= --subject-prefix= --output-directory= --keep-subject --no-binary --zero-commit --ignore-if-in-upstream --no-stat --add-header= --to= --cc= --from --in-reply-to= --attach --inline --thread --signature= --base= --signature-file= --quiet --progress --interdiff= --range-diff= --creation-factor= --binary -- --no-numbered --no-signoff --no-stdout --no-cover-letter --no-numbered-files --no-suffix --no-start-number --no-reroll-count --no-cover-from-description --no-zero-commit --no-ignore-if-in-upstream --no-add-header --no-to --no-cc --no-from --no-in-reply-to --no-attach --no-thread --no-signature --no-base --no-signature-file --no-quiet --no-progress --no-interdiff --no-range-diff --no-creation-factor" + +__gitcomp_builtin_get_default () +{ + eval "test -n \"\$${1}_default\" && echo \"\$${1}_default\"" +} + +# This function is equivalent to +# +# __gitcomp "$(git xxx --git-completion-helper) ..." +# +# except that the output is cached. Accept 1-3 arguments: +# 1: the git command to execute, this is also the cache key +# 2: extra options to be added on top (e.g. negative forms) +# 3: options to be excluded +__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 var=__gitcomp_builtin_"${cmd//-/_}" + local options + 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 + completion="$(__git ${cmd/_/ } $completion_helper || + __gitcomp_builtin_get_default $var)" || return + # leading and trailing spaces are significant to make + # option removal work correctly. + options=" $incl $completion " + + for i in $excl; do + options="${options/ $i / }" + done + eval "$var=\"$options\"" + fi + + __gitcomp "$options" +} + # Variation of __gitcomp_nl () that appends to the existing list of # completion candidates, COMPREPLY. __gitcomp_nl_append () @@ -303,6 +563,24 @@ __gitcomp_nl () __gitcomp_nl_append "$@" } +# Fills the COMPREPLY array with prefiltered paths without any additional +# processing. +# Callers must take care of providing only paths that match the current path +# to be completed and adding any prefix path components, if necessary. +# 1: List of newline-separated matching paths, complete with all prefix +# path components. +__gitcomp_file_direct () +{ + local IFS=$'\n' + + COMPREPLY=($1) + + # use a hack to enable file mode in bash < 4 + compopt -o filenames +o nospace 2>/dev/null || + compgen -f /non-existing-dir/ >/dev/null || + true +} + # Generates completion reply with compgen from newline-separated possible # completion filenames. # It accepts 1 to 3 arguments: @@ -322,7 +600,8 @@ __gitcomp_file () # use a hack to enable file mode in bash < 4 compopt -o filenames +o nospace 2>/dev/null || - compgen -f /non-existing-dir/ > /dev/null + compgen -f /non-existing-dir/ >/dev/null || + true } # Execute 'git ls-files', unless the --committable option is specified, in @@ -332,10 +611,12 @@ __gitcomp_file () __git_ls_files_helper () { if [ "$2" == "--committable" ]; then - __git -C "$1" diff-index --name-only --relative HEAD + __git -C "$1" -c core.quotePath=false diff-index \ + --name-only --relative HEAD -- "${3//\\/\\\\}*" else # NOTE: $2 is not quoted in order to support multiple options - __git -C "$1" ls-files --exclude-standard $2 + __git -C "$1" -c core.quotePath=false ls-files \ + --exclude-standard $2 -- "${3//\\/\\\\}*" fi } @@ -346,17 +627,103 @@ __git_ls_files_helper () # If provided, only files within the specified directory are listed. # Sub directories are never recursed. Path must have a trailing # slash. +# 3: List only paths matching this path component (optional). __git_index_files () { - local root="${2-.}" file + local root="$2" match="$3" - __git_ls_files_helper "$root" "$1" | - while read -r file; do - case "$file" in - ?*/*) echo "${file%%/*}" ;; - *) echo "$file" ;; - esac - done | sort | uniq + __git_ls_files_helper "$root" "$1" "${match:-?}" | + awk -F / -v pfx="${2//\\/\\\\}" '{ + paths[$1] = 1 + } + END { + for (p in paths) { + if (substr(p, 1, 1) != "\"") { + # No special characters, easy! + print pfx p + continue + } + + # The path is quoted. + p = dequote(p) + if (p == "") + continue + + # Even when a directory name itself does not contain + # any special characters, it will still be quoted if + # any of its (stripped) trailing path components do. + # Because of this we may have seen the same directory + # both quoted and unquoted. + if (p in paths) + # We have seen the same directory unquoted, + # skip it. + continue + else + print pfx p + } + } + function dequote(p, bs_idx, out, esc, esc_idx, dec) { + # Skip opening double quote. + p = substr(p, 2) + + # Interpret backslash escape sequences. + while ((bs_idx = index(p, "\\")) != 0) { + out = out substr(p, 1, bs_idx - 1) + esc = substr(p, bs_idx + 1, 1) + p = substr(p, bs_idx + 2) + + if ((esc_idx = index("abtvfr\"\\", esc)) != 0) { + # C-style one-character escape sequence. + out = out substr("\a\b\t\v\f\r\"\\", + esc_idx, 1) + } else if (esc == "n") { + # Uh-oh, a newline character. + # We cannot reliably put a pathname + # containing a newline into COMPREPLY, + # and the newline would create a mess. + # Skip this path. + return "" + } else { + # Must be a \nnn octal value, then. + dec = esc * 64 + \ + substr(p, 1, 1) * 8 + \ + substr(p, 2, 1) + out = out sprintf("%c", dec) + p = substr(p, 3) + } + } + # Drop closing double quote, if there is one. + # (There 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) + else + out = out p + + return out + }' +} + +# __git_complete_index_file requires 1 argument: +# 1: the options to pass to ls-file +# +# The exception is --committable, which finds the files appropriate commit. +__git_complete_index_file () +{ + local dequoted_word pfx="" cur_ + + __git_dequote "$cur" + + case "$dequoted_word" in + ?*/*) + pfx="${dequoted_word%/*}/" + cur_="${dequoted_word##*/}" + ;; + *) + cur_="$dequoted_word" + esac + + __gitcomp_file_direct "$(__git_index_files "$1" "$pfx" "$cur_")" } # Lists branches from the local repository. @@ -372,6 +739,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 () @@ -382,6 +762,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). @@ -439,7 +839,7 @@ __git_refs () track="" ;; *) - for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD; do + for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD REBASE_HEAD; do case "$i" in $match*) if [ -e "$dir/$i" ]; then @@ -457,13 +857,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 @@ -510,29 +904,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) @@ -594,7 +1010,7 @@ __git_is_configured_remote () __git_list_merge_strategies () { - git merge -s help 2>&1 | + LANG=C LC_ALL=C git merge -s help 2>&1 | sed -n -e '/[Aa]vailable strategies are: /,/^$/{ s/\.$// s/.*:// @@ -604,6 +1020,7 @@ __git_list_merge_strategies () }' } +__git_merge_strategies_default='octopus ours recursive resolve subtree' __git_merge_strategies= # 'git merge -s help' (and thus detection of the merge strategy # list) fails, unfortunately, if run outside of any git working @@ -613,12 +1030,18 @@ __git_merge_strategies= __git_compute_merge_strategies () { test -n "$__git_merge_strategies" || - __git_merge_strategies=$(__git_list_merge_strategies) + { __git_merge_strategies=$(__git_list_merge_strategies); + __git_merge_strategies="${__git_merge_strategies:-__git_merge_strategies_default}"; } } +__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 @@ -626,14 +1049,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 @@ -643,21 +1070,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_%...*}..." @@ -675,26 +1091,6 @@ __git_complete_revlist_file () esac } - -# __git_complete_index_file requires 1 argument: -# 1: the options to pass to ls-file -# -# The exception is --committable, which finds the files appropriate commit. -__git_complete_index_file () -{ - local pfx="" cur_="$cur" - - case "$cur_" in - ?*/*) - pfx="${cur_%/*}" - cur_="${cur_##*/}" - pfx="${pfx}/" - ;; - esac - - __gitcomp_file "$(__git_index_files "$1" ${pfx:+"$pfx"})" "$pfx" "$cur_" -} - __git_complete_file () { __git_complete_revlist_file @@ -726,6 +1122,7 @@ __git_complete_remote_or_refspec () *) ;; esac ;; + --multiple) no_complete_refspec=1; break ;; -*) ;; *) remote="$i"; break ;; esac @@ -785,136 +1182,30 @@ __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 } -__git_commands () { - if test -n "${GIT_TESTING_COMMAND_COMPLETION:-}" - then - printf "%s" "${GIT_TESTING_COMMAND_COMPLETION}" - else - git help -a|egrep '^ [a-zA-Z0-9]' - fi -} - -__git_list_all_commands () -{ - local i IFS=" "$'\n' - for i in $(__git_commands) - do - case $i in - *--*) : helper pattern;; - *) echo $i;; - esac - done -} - __git_all_commands= __git_compute_all_commands () { test -n "$__git_all_commands" || - __git_all_commands=$(__git_list_all_commands) -} - -__git_list_porcelain_commands () -{ - local i IFS=" "$'\n' - __git_compute_all_commands - for i in $__git_all_commands - do - case $i in - *--*) : helper pattern;; - applymbox) : ask gittus;; - applypatch) : ask gittus;; - archimport) : import;; - cat-file) : plumbing;; - check-attr) : plumbing;; - check-ignore) : plumbing;; - check-mailmap) : plumbing;; - check-ref-format) : plumbing;; - checkout-index) : plumbing;; - column) : internal helper;; - commit-tree) : plumbing;; - count-objects) : infrequent;; - credential) : credentials;; - credential-*) : credentials helper;; - cvsexportcommit) : export;; - cvsimport) : import;; - cvsserver) : daemon;; - daemon) : daemon;; - diff-files) : plumbing;; - diff-index) : plumbing;; - diff-tree) : plumbing;; - fast-import) : import;; - fast-export) : export;; - fsck-objects) : plumbing;; - fetch-pack) : plumbing;; - fmt-merge-msg) : plumbing;; - for-each-ref) : plumbing;; - hash-object) : plumbing;; - http-*) : transport;; - index-pack) : plumbing;; - init-db) : deprecated;; - local-fetch) : plumbing;; - ls-files) : plumbing;; - ls-remote) : plumbing;; - ls-tree) : plumbing;; - mailinfo) : plumbing;; - mailsplit) : plumbing;; - merge-*) : plumbing;; - mktree) : plumbing;; - mktag) : plumbing;; - pack-objects) : plumbing;; - pack-redundant) : plumbing;; - pack-refs) : plumbing;; - parse-remote) : plumbing;; - patch-id) : plumbing;; - prune) : plumbing;; - prune-packed) : plumbing;; - quiltimport) : import;; - read-tree) : plumbing;; - receive-pack) : plumbing;; - remote-*) : transport;; - rerere) : plumbing;; - rev-list) : plumbing;; - rev-parse) : plumbing;; - runstatus) : plumbing;; - sh-setup) : internal;; - shell) : daemon;; - show-ref) : plumbing;; - send-pack) : plumbing;; - show-index) : plumbing;; - ssh-*) : transport;; - stripspace) : plumbing;; - symbolic-ref) : plumbing;; - unpack-file) : plumbing;; - unpack-objects) : plumbing;; - update-index) : plumbing;; - update-ref) : plumbing;; - update-server-info) : daemon;; - upload-archive) : plumbing;; - upload-pack) : plumbing;; - write-tree) : plumbing;; - var) : infrequent;; - verify-pack) : infrequent;; - verify-tag) : plumbing;; - *) echo $i;; - esac - done -} - -__git_porcelain_commands= -__git_compute_porcelain_commands () -{ - test -n "$__git_porcelain_commands" || - __git_porcelain_commands=$(__git_list_porcelain_commands) + __git_all_commands=$(__git --list-cmds=main,others,alias,nohelpers) } # Lists all set config variables starting with the given section prefix, @@ -932,45 +1223,75 @@ __git_pretty_aliases () __git_get_config_variables "pretty" } -__git_aliases () -{ - __git_get_config_variables "alias" -} - # __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=1 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 @@ -978,6 +1299,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 1 ]; 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 @@ -1072,12 +1427,15 @@ __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_am () { __git_find_repo_path if [ -d "$__git_repo_path"/rebase-apply ]; then - __gitcomp "--skip --continue --resolved --abort" + __gitcomp "$__git_am_inprogress_options" return fi case "$cur" in @@ -1085,13 +1443,17 @@ _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 + ;; --*) - __gitcomp " - --3way --committer-date-is-author-date --ignore-date - --ignore-whitespace --ignore-space-change - --interactive --keep --no-utf8 --signoff --utf8 - --whitespace= --scissors - " + __gitcomp_builtin am "" \ + "$__git_am_inprogress_options" return esac } @@ -1104,14 +1466,7 @@ _git_apply () return ;; --*) - __gitcomp " - --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 - --recount --directory= - " + __gitcomp_builtin apply return esac } @@ -1119,11 +1474,12 @@ _git_apply () _git_add () { case "$cur" in + --chmod=*) + __gitcomp "+x -x" "" "${cur##--chmod=}" + return + ;; --*) - __gitcomp " - --interactive --refresh --patch --update --dry-run - --ignore-errors --intent-to-add --force --edit --chmod= - " + __gitcomp_builtin add return esac @@ -1147,10 +1503,7 @@ _git_archive () return ;; --*) - __gitcomp " - --format= --list --verbose - --prefix= --remote= --exec= --output - " + __gitcomp_builtin archive "--format= --list --verbose --prefix= --worktree-attributes" return ;; esac @@ -1182,6 +1535,8 @@ _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" @@ -1200,13 +1555,7 @@ _git_branch () __git_complete_refs --cur="${cur##--set-upstream-to=}" ;; --*) - __gitcomp " - --color --no-color --verbose --abbrev= --no-abbrev - --track --no-track --contains --no-contains --merged --no-merged - --set-upstream-to= --edit-description --list - --unset-upstream --delete --move --copy --remotes - --column --no-column --sort= --points-at - " + __gitcomp_builtin branch ;; *) if [ $only_local_ref = "y" -a $has_r = "n" ]; then @@ -1238,49 +1587,111 @@ _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 priority order +# 1) the last provided of "--guess" or "--no-guess" explicitly enable or +# disable completion of DWIM logic respectively. +# 2) If the --no-track option is provided, take this as a hint to disable the +# DWIM completion logic +# 3) If GIT_COMPLETION_CHECKOUT_NO_GUESS is set, disable the DWIM completion +# logic, as requested by the user. +# 4) 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 + if [ -n "$(__git_find_on_cmdline "--no-track")" ]; 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=}" ;; --*) - __gitcomp " - --quiet --ours --theirs --track --no-track --merge - --conflict= --orphan --patch --detach --ignore-skip-worktree-bits - --recurse-submodules --no-recurse-submodules - " + __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 () -{ - __git_complete_refs -} +__git_sequencer_inprogress_options="--continue --quit --abort --skip" + +__git_cherry_pick_inprogress_options=$__git_sequencer_inprogress_options _git_cherry_pick () { __git_find_repo_path if [ -f "$__git_repo_path"/CHERRY_PICK_HEAD ]; then - __gitcomp "--continue --quit --abort" + __gitcomp "$__git_cherry_pick_inprogress_options" return fi + + __git_complete_strategy && return + case "$cur" in --*) - __gitcomp "--edit --no-commit --signoff --strategy= --mainline" + __gitcomp_builtin cherry-pick "" \ + "$__git_cherry_pick_inprogress_options" ;; *) __git_complete_refs @@ -1292,7 +1703,7 @@ _git_clean () { case "$cur" in --*) - __gitcomp "--dry-run --quiet" + __gitcomp_builtin clean return ;; esac @@ -1303,28 +1714,20 @@ _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 " - --local - --no-hardlinks - --shared - --reference - --quiet - --no-checkout - --bare - --mirror - --origin - --upload-pack - --template= - --depth - --single-branch - --no-tags - --branch - --recurse-submodules - --no-single-branch - --shallow-submodules - " + __gitcomp_builtin clone return ;; esac @@ -1357,16 +1760,7 @@ _git_commit () return ;; --*) - __gitcomp " - --all --author= --signoff --verify --no-verify - --edit --no-edit - --amend --include --only --interactive - --dry-run --reuse-message= --reedit-message= - --reset-author --file= --message= --template= - --cleanup= --untracked-files --untracked-files= - --verbose --quiet --fixup= --squash= - --patch --short --date --allow-empty - " + __gitcomp_builtin commit return esac @@ -1382,11 +1776,7 @@ _git_describe () { case "$cur" in --*) - __gitcomp " - --all --tags --contains --abbrev= --candidates= - --exact-match --debug --long --match --always --first-parent - --exclude --dirty --broken - " + __gitcomp_builtin describe return esac __git_complete_refs @@ -1396,9 +1786,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 @@ -1411,7 +1808,9 @@ __git_diff_common_options="--stat --numstat --shortstat --summary --dirstat --dirstat= --dirstat-by-file --dirstat-by-file= --cumulative --diff-algorithm= - --submodule --submodule= + --submodule --submodule= --ignore-submodules + --indent-heuristic --no-indent-heuristic + --textconv --no-textconv " _git_diff () @@ -1427,6 +1826,14 @@ _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 @@ -1439,7 +1846,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 () @@ -1452,11 +1860,11 @@ _git_difftool () return ;; --*) - __gitcomp "--cached --staged --pickaxe-all --pickaxe-regex - --base --ours --theirs - --no-renames --diff-filter= --find-copies-harder - --relative --ignore-submodules - --tool=" + __gitcomp_builtin difftool "$__git_diff_common_options + --base --cached --ours --theirs + --pickaxe-all --pickaxe-regex + --relative --staged + " return ;; esac @@ -1465,12 +1873,6 @@ _git_difftool () __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 --recurse-submodules= - --unshallow --update-shallow -" - _git_fetch () { case "$cur" in @@ -1478,21 +1880,21 @@ _git_fetch () __gitcomp "$__git_fetch_recurse_submodules" "" "${cur##--recurse-submodules=}" return ;; + --filter=*) + __gitcomp "blob:none blob:limit= sparse:oid=" "" "${cur##--filter=}" + return + ;; --*) - __gitcomp "$__git_fetch_options" + __gitcomp_builtin fetch return ;; esac __git_complete_remote_or_refspec } -__git_format_patch_options=" - --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_extra_options=" + --full-index --not --all --no-prefix --src-prefix= + --dst-prefix= --notes " _git_format_patch () @@ -1504,32 +1906,23 @@ _git_format_patch () " "" "${cur##--thread=}" return ;; - --*) - __gitcomp "$__git_format_patch_options" + --base=*|--interdiff=*|--range-diff=*) + __git_complete_refs --cur="${cur#--*=}" return ;; - esac - __git_complete_revlist -} - -_git_fsck () -{ - case "$cur" in --*) - __gitcomp " - --tags --root --unreachable --cache --no-reflogs --full - --strict --verbose --lost-found --name-objects - " + __gitcomp_builtin format-patch "$__git_format_patch_extra_options" return ;; esac + __git_complete_revlist } -_git_gc () +_git_fsck () { case "$cur" in --*) - __gitcomp "--prune --aggressive" + __gitcomp_builtin fsck return ;; esac @@ -1585,21 +1978,7 @@ _git_grep () case "$cur" in --*) - __gitcomp " - --cached - --text --ignore-case --word-regexp --invert-match - --full-name --line-number - --extended-regexp --basic-regexp --fixed-strings - --perl-regexp - --threads - --files-with-matches --name-only - --files-without-match - --max-depth - --count - --and --or --not --all-match - --break --heading --show-function --function-context - --untracked --no-index - " + __gitcomp_builtin grep return ;; esac @@ -1617,17 +1996,16 @@ _git_help () { case "$cur" in --*) - __gitcomp "--all --guides --info --man --web" + __gitcomp_builtin help return ;; esac - __git_compute_all_commands - __gitcomp "$__git_all_commands $(__git_aliases) - attributes cli core-tutorial cvs-migration - diffcore everyday gitk glossary hooks ignore modules - namespaces repository-layout revisions tutorial tutorial-2 - workflows - " + if test -n "$GIT_TESTING_ALL_COMMAND_LIST" + then + __gitcomp "$GIT_TESTING_ALL_COMMAND_LIST $(__git --list-cmds=alias,list-guide) gitk" + else + __gitcomp "$(__git --list-cmds=main,nohelpers,alias,list-guide) gitk" + fi } _git_init () @@ -1640,7 +2018,7 @@ _git_init () return ;; --*) - __gitcomp "--quiet --bare --template= --shared --shared=" + __gitcomp_builtin init return ;; esac @@ -1650,13 +2028,7 @@ _git_ls_files () { case "$cur" in --*) - __gitcomp "--cached --deleted --modified --others --ignored - --stage --directory --no-empty-directory --unmerged - --killed --exclude= --exclude-from= - --exclude-per-directory= --exclude-standard - --error-unmatch --with-tree= --full-name - --abbrev --ignored --exclude-per-directory - " + __gitcomp_builtin ls-files return ;; esac @@ -1670,7 +2042,7 @@ _git_ls_remote () { case "$cur" in --*) - __gitcomp "--heads --tags --refs --get-url --symref" + __gitcomp_builtin ls-remote return ;; esac @@ -1679,6 +2051,13 @@ _git_ls_remote () _git_ls_tree () { + case "$cur" in + --*) + __gitcomp_builtin ls-tree + return + ;; + esac + __git_complete_file } @@ -1705,8 +2084,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 () { @@ -1752,6 +2131,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 @@ -1759,19 +2142,23 @@ _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 + --patch $merge $__git_diff_common_options --pickaxe-all --pickaxe-regex + --patch --no-patch " return ;; @@ -1794,22 +2181,13 @@ _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 () { __git_complete_strategy && return case "$cur" in --*) - __gitcomp "$__git_merge_options - --rerere-autoupdate --no-rerere-autoupdate --abort --continue" + __gitcomp_builtin merge return esac __git_complete_refs @@ -1823,7 +2201,7 @@ _git_mergetool () return ;; --*) - __gitcomp "--tool= --prompt --no-prompt" + __gitcomp "--tool= --prompt --no-prompt --gui --no-gui" return ;; esac @@ -1833,7 +2211,7 @@ _git_merge_base () { case "$cur" in --*) - __gitcomp "--octopus --independent --is-ancestor --fork-point" + __gitcomp_builtin merge-base return ;; esac @@ -1844,7 +2222,7 @@ _git_mv () { case "$cur" in --*) - __gitcomp "--dry-run" + __gitcomp_builtin mv return ;; esac @@ -1858,19 +2236,14 @@ _git_mv () fi } -_git_name_rev () -{ - __gitcomp "--tags --all --stdin" -} - _git_notes () { - local subcommands='add append copy edit list prune remove show' + local subcommands='add append copy edit get-ref list merge prune remove show' local subcommand="$(__git_find_on_cmdline "$subcommands")" case "$subcommand,$cur" in ,--*) - __gitcomp '--ref' + __gitcomp_builtin notes ;; ,*) case "$prev" in @@ -1882,21 +2255,14 @@ _git_notes () ;; esac ;; - add,--reuse-message=*|append,--reuse-message=*|\ - add,--reedit-message=*|append,--reedit-message=*) + *,--reuse-message=*|*,--reedit-message=*) __git_complete_refs --cur="${cur#*=}" ;; - add,--*|append,--*) - __gitcomp '--file= --message= --reedit-message= - --reuse-message=' - ;; - copy,--*) - __gitcomp '--stdin' + *,--*) + __gitcomp_builtin notes_$subcommand ;; - prune,--*) - __gitcomp '--dry-run --verbose' - ;; - prune,*) + prune,*|get-ref,*) + # this command does not take a ref, do not complete it ;; *) case "$prev" in @@ -1920,12 +2286,8 @@ _git_pull () return ;; --*) - __gitcomp " - --rebase --no-rebase - --autostash --no-autostash - $__git_merge_options - $__git_fetch_options - " + __gitcomp_builtin pull + return ;; esac @@ -1976,27 +2338,39 @@ _git_push () return ;; --*) + __gitcomp_builtin push + return + ;; + esac + __git_complete_remote_or_refspec +} + +_git_range_diff () +{ + case "$cur" in + --*) __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= + --creation-factor= --no-dual-color + $__git_diff_common_options " return ;; esac - __git_complete_remote_or_refspec + __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" + __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" + __gitcomp "$__git_rebase_inprogress_options" return fi __git_complete_strategy && return @@ -2005,19 +2379,13 @@ _git_rebase () __gitcomp "$__git_whitespacelist" "" "${cur##--whitespace=}" return ;; + --onto=*) + __git_complete_refs --cur="${cur##--onto=}" + return + ;; --*) - __gitcomp " - --onto --merge --strategy --interactive - --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 - --exec - " + __gitcomp_builtin rebase "" \ + "$__git_rebase_interactive_inprogress_options" return esac @@ -2077,16 +2445,16 @@ _git_send_email () return ;; --*) - __gitcomp "--annotate --bcc --cc --cc-cmd --chain-reply-to + __gitcomp_builtin send-email "--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 + --no-suppress-from --no-thread --quiet --reply-to --signed-off-by-cc --smtp-pass --smtp-server --smtp-server-port --smtp-encryption= --smtp-user --subject --suppress-cc= --suppress-from --thread --to --validate --no-validate - $__git_format_patch_options" + $__git_format_patch_extra_options" return ;; esac @@ -2119,11 +2487,7 @@ _git_status () return ;; --*) - __gitcomp " - --short --branch --porcelain --long --verbose - --untracked-files= --ignore-submodules= --ignored - --column= --no-column - " + __gitcomp_builtin status return ;; esac @@ -2148,6 +2512,57 @@ _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 @@ -2170,496 +2585,287 @@ __git_config_get_set_variables () __git config $config_file --name-only --list } -_git_config () +__git_config_vars= +__git_compute_config_vars () { - case "$prev" in + test -n "$__git_config_vars" || + __git_config_vars="$(git help --config-for-completion | sort -u)" +} + +# 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="$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="${varname,,}" + else + 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 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.aliasesfiletype) - __gitcomp "mutt mailrc pine elm gnus" + sendemail.aliasfiletype) + __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 " - --system --global --local --file= - --list --replace-all - --get --get-all --get-regexp - --add --unset --unset-all - --remove-section --rename-section - --name-only - " - 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_" + argPrompt cmd confirm needsFile noConsole noRescan + prompt revPrompt revUnmerged title + " "$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_" + receivepack uploadpack tagOpt pushurl + " "$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_" - return - ;; - esac - __gitcomp " - add.ignoreErrors - advice.amWorkDir - advice.commitBeforeMerge - advice.detachedHead - advice.implicitIdentity - advice.pushAlreadyExists - advice.pushFetchFirst - advice.pushNeedsForce - advice.pushNonFFCurrent - advice.pushNonFFMatching - advice.pushUpdateRejected - advice.resolveConflict - advice.rmHints - advice.statusHints - advice.statusUoption - advice.ignoredHook - alias. - am.keepcr - am.threeWay - apply.ignorewhitespace - apply.whitespace - branch.autosetupmerge - branch.autosetuprebase - browser. - clean.requireForce - color.branch - color.branch.current - color.branch.local - color.branch.plain - color.branch.remote - color.decorate.HEAD - color.decorate.branch - color.decorate.remoteBranch - color.decorate.stash - color.decorate.tag - color.diff - color.diff.commit - color.diff.frag - color.diff.func - color.diff.meta - color.diff.new - color.diff.old - color.diff.plain - color.diff.whitespace - color.grep - color.grep.context - color.grep.filename - color.grep.function - color.grep.linenumber - color.grep.match - color.grep.selected - color.grep.separator - color.interactive - color.interactive.error - color.interactive.header - color.interactive.help - color.interactive.prompt - color.pager - color.showbranch - color.status - color.status.added - color.status.changed - color.status.header - color.status.localBranch - color.status.nobranch - color.status.remoteBranch - color.status.unmerged - color.status.untracked - color.status.updated - color.ui - commit.cleanup - commit.gpgSign - commit.status - commit.template - commit.verbose - core.abbrev - core.askpass - core.attributesfile - core.autocrlf - core.bare - core.bigFileThreshold - core.checkStat - core.commentChar - core.compression - core.createObject - core.deltaBaseCacheLimit - core.editor - core.eol - core.excludesfile - core.fileMode - core.fsyncobjectfiles - core.gitProxy - core.hideDotFiles - core.hooksPath - core.ignoreStat - core.ignorecase - core.logAllRefUpdates - core.loosecompression - core.notesRef - core.packedGitLimit - core.packedGitWindowSize - core.packedRefsTimeout - core.pager - core.precomposeUnicode - core.preferSymlinkRefs - core.preloadindex - core.protectHFS - core.protectNTFS - core.quotepath - core.repositoryFormatVersion - core.safecrlf - core.sharedRepository - core.sparseCheckout - core.splitIndex - core.sshCommand - core.symlinks - core.trustctime - core.untrackedCache - core.warnAmbiguousRefs - core.whitespace - core.worktree - credential.helper - credential.useHttpPath - credential.username - credentialCache.ignoreSIGHUP - diff.autorefreshindex - 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.from - format.headers - format.numbered - format.pretty - format.signature - format.signoff - format.subjectprefix - format.suffix - format.thread - format.to - gc. - gc.aggressiveDepth - gc.aggressiveWindow - gc.auto - gc.autoDetach - gc.autopacklimit - gc.logExpiry - gc.packrefs - gc.pruneexpire - gc.reflogexpire - gc.reflogexpireunreachable - gc.rerereresolved - gc.rerereunresolved - gc.worktreePruneExpire - gitcvs.allbinary - gitcvs.commitmsgannotation - gitcvs.dbTableNamePrefix - gitcvs.dbdriver - gitcvs.dbname - gitcvs.dbpass - gitcvs.dbuser - gitcvs.enabled - gitcvs.logfile - gitcvs.usecrlfattr - guitool. - gui.blamehistoryctx - gui.commitmsgwidth - gui.copyblamethreshold - gui.diffcontext - gui.encoding - gui.fastcopyblame - gui.matchtrackingbranch - gui.newbranchtemplate - gui.pruneduringfetch - gui.spellingdictionary - gui.trustmtime - help.autocorrect - help.browser - help.format - http.lowSpeedLimit - http.lowSpeedTime - http.maxRequests - http.minSessions - http.noEPSV - http.postBuffer - http.proxy - http.sslCipherList - http.sslVersion - http.sslCAInfo - http.sslCAPath - http.sslCert - http.sslCertPasswordProtected - http.sslKey - http.sslVerify - http.useragent - i18n.commitEncoding - i18n.logOutputEncoding - imap.authMethod - imap.folder - imap.host - imap.pass - imap.port - imap.preformattedHTML - imap.sslverify - imap.tunnel - imap.user - init.templatedir - instaweb.browser - instaweb.httpd - instaweb.local - instaweb.modulepath - instaweb.port - interactive.singlekey - log.date - log.decorate - log.showroot - mailmap.file - man. - man.viewer - merge. - merge.conflictstyle - merge.log - merge.renameLimit - merge.renormalize - merge.stat - merge.tool - merge.verbosity - mergetool. - mergetool.keepBackup - mergetool.keepTemporaries - mergetool.prompt - notes.displayRef - notes.rewrite. - notes.rewrite.amend - notes.rewrite.rebase - notes.rewriteMode - notes.rewriteRef - pack.compression - pack.deltaCacheLimit - pack.deltaCacheSize - pack.depth - pack.indexVersion - pack.packSizeLimit - pack.threads - pack.window - pack.windowMemory - pager. - pretty. - pull.octopus - pull.twohead - push.default - push.followTags - rebase.autosquash - rebase.stat - receive.autogc - receive.denyCurrentBranch - receive.denyDeleteCurrent - receive.denyDeletes - receive.denyNonFastForwards - receive.fsckObjects - receive.unpackLimit - receive.updateserverinfo - remote.pushdefault - remotes. - repack.usedeltabaseoffset - rerere.autoupdate - rerere.enabled - sendemail. - sendemail.aliasesfile - sendemail.aliasfiletype - sendemail.bcc - sendemail.cc - sendemail.cccmd - sendemail.chainreplyto - sendemail.confirm - sendemail.envelopesender - sendemail.from - sendemail.identity - sendemail.multiedit - sendemail.signedoffbycc - sendemail.smtpdomain - sendemail.smtpencryption - sendemail.smtppass - sendemail.smtpserver - sendemail.smtpserveroption - sendemail.smtpserverport - sendemail.smtpuser - sendemail.suppresscc - sendemail.suppressfrom - sendemail.thread - sendemail.to - sendemail.tocmd - sendemail.validate - sendemail.smtpbatchsize - sendemail.smtprelogindelay - showbranch.default - status.relativePaths - status.showUntrackedFiles - status.submodulesummary - submodule. - tar.umask - transfer.unpackLimit - url. - user.email - user.name - user.signingkey - web.browser - branch. remote. - " + local pfx="${cur_%.*}." + cur_="${cur_##*.}" + __gitcomp "insteadOf pushInsteadOf" "$pfx" "$cur_" "$sfx" + return + ;; + *.*) + __git_compute_config_vars + __gitcomp "$__git_config_vars" "" "$cur_" "$sfx" + ;; + *) + __git_compute_config_vars + __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 } _git_remote () @@ -2672,7 +2878,7 @@ _git_remote () if [ -z "$subcommand" ]; then case "$cur" in --*) - __gitcomp "--verbose" + __gitcomp_builtin remote ;; *) __gitcomp "$subcommands" @@ -2683,33 +2889,33 @@ _git_remote () case "$subcommand,$cur" in add,--*) - __gitcomp "--track --master --fetch --tags --no-tags --mirror=" + __gitcomp_builtin remote_add ;; add,*) ;; set-head,--*) - __gitcomp "--auto --delete" + __gitcomp_builtin remote_set-head ;; set-branches,--*) - __gitcomp "--add" + __gitcomp_builtin remote_set-branches ;; set-head,*|set-branches,*) __git_complete_remote_or_refspec ;; update,--*) - __gitcomp "--prune" + __gitcomp_builtin remote_update ;; update,*) - __gitcomp "$(__git_get_config_variables "remotes")" + __gitcomp "$(__git_remotes) $(__git_get_config_variables "remotes")" ;; set-url,--*) - __gitcomp "--push --add --delete" + __gitcomp_builtin remote_set-url ;; get-url,--*) - __gitcomp "--push --all" + __gitcomp_builtin remote_get-url ;; prune,--*) - __gitcomp "--dry-run" + __gitcomp_builtin remote_prune ;; *) __gitcomp_nl "$(__git_remotes)" @@ -2720,8 +2926,12 @@ _git_remote () _git_replace () { case "$cur" in + --format=*) + __gitcomp "short medium long" "" "${cur##--format=}" + return + ;; --*) - __gitcomp "--edit --graft --format= --list --delete" + __gitcomp_builtin replace return ;; esac @@ -2745,26 +2955,49 @@ _git_reset () case "$cur" in --*) - __gitcomp "--merge --mixed --hard --soft --patch --keep" + __gitcomp_builtin reset return ;; esac __git_complete_refs } +_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 () { __git_find_repo_path if [ -f "$__git_repo_path"/REVERT_HEAD ]; then - __gitcomp "--continue --quit --abort" + __gitcomp "$__git_revert_inprogress_options" return fi + __git_complete_strategy && return case "$cur" in --*) - __gitcomp " - --edit --mainline --no-edit --no-commit --signoff - --strategy= --strategy-option= - " + __gitcomp_builtin revert "" \ + "$__git_revert_inprogress_options" return ;; esac @@ -2775,7 +3008,7 @@ _git_rm () { case "$cur" in --*) - __gitcomp "--cached --dry-run --ignore-unmatch --quiet" + __gitcomp_builtin rm return ;; esac @@ -2818,9 +3051,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 --patch + --expand-tabs --expand-tabs= --no-expand-tabs $__git_diff_common_options " return @@ -2833,28 +3075,52 @@ _git_show_branch () { case "$cur" in --*) - __gitcomp " - --all --remotes --topo-order --date-order --current --more= - --list --independent --merge-base --no-name - --color --no-color - --sha1-name --sparse --topics --reflog - " + __gitcomp_builtin show-branch return ;; esac __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 save list show apply clear drop pop create branch' - local subcommand="$(__git_find_on_cmdline "$subcommands")" + local subcommands='push list show apply clear drop pop create branch' + local subcommand="$(__git_find_on_cmdline "$subcommands save")" + if [ -z "$subcommand" -a -n "$(__git_find_on_cmdline "-p")" ]; then + subcommand="push" + fi if [ -z "$subcommand" ]; then case "$cur" in --*) __gitcomp "$save_opts" ;; + sa*) + if [ -z "$(__git_find_on_cmdline "$save_opts")" ]; then + __gitcomp "save" + fi + ;; *) if [ -z "$(__git_find_on_cmdline "$save_opts")" ]; then __gitcomp "$subcommands" @@ -2875,6 +3141,9 @@ _git_stash () drop,--*) __gitcomp "--quiet" ;; + list,--*) + __gitcomp "--name-status --oneline --patch-with-stat" + ;; show,--*|branch,--*) ;; branch,*) @@ -2899,7 +3168,7 @@ _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 @@ -2930,6 +3199,9 @@ _git_submodule () --force --rebase --merge --reference --depth --recursive --jobs " ;; + set-branch,--*) + __gitcomp "--default --branch" + ;; summary,--*) __gitcomp "--cached --files --summary-limit" ;; @@ -2960,6 +3232,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=" @@ -3045,7 +3318,7 @@ _git_tag () while [ $c -lt $cword ]; do i="${words[c]}" case "$i" in - -d|-v) + -d|--delete|-v|--verify) __gitcomp_direct "$(__git_tags "" "$cur" " ")" return ;; @@ -3071,11 +3344,7 @@ _git_tag () case "$cur" in --*) - __gitcomp " - --list --delete --verify --annotate --message --file - --sign --cleanup --local-user --force --column --sort= - --contains --no-contains --points-at --merged --no-merged --create-reflog - " + __gitcomp_builtin tag ;; esac } @@ -3085,29 +3354,128 @@ _git_whatchanged () _git_log } +__git_complete_worktree_paths () +{ + local IFS=$'\n' + __gitcomp_nl "$(git worktree list --porcelain | + # Skip the first entry: it's the path of the main worktree, + # which can't be moved, removed, locked, etc. + sed -n -e '2,$ s/^worktree //p')" +} + _git_worktree () { - local subcommands="add list lock prune unlock" - local subcommand="$(__git_find_on_cmdline "$subcommands")" - if [ -z "$subcommand" ]; then + local subcommands="add list lock move prune remove unlock" + 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 "--detach" - ;; - list,--*) - __gitcomp "--porcelain" - ;; - lock,--*) - __gitcomp "--reason" + ;; + *,--*) + __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 ;; - prune,--*) - __gitcomp "--dry-run --expire --verbose" + -*) # 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 + ;; + 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 () { + local command="$1" + + case "$cur" in + --*) + __gitcomp_builtin "$command" + ;; + esac +} + +__git_cmds_with_parseopt_helper= +__git_support_parseopt_helper () { + test -n "$__git_cmds_with_parseopt_helper" || + __git_cmds_with_parseopt_helper="$(__git --list-cmds=parseopt)" + + case " $__git_cmds_with_parseopt_helper " in + *" $1 "*) + return 0 + ;; + *) + return 1 + ;; + esac +} + +__git_complete_command () { + local command="$1" + local completion_func="_git_${command//-/_}" + if ! declare -f $completion_func >/dev/null 2>/dev/null && + declare -f _completion_loader >/dev/null 2>/dev/null + then + _completion_loader "git-$command" + fi + if declare -f $completion_func >/dev/null 2>/dev/null + then + $completion_func + return 0 + elif __git_support_parseopt_helper "$command" + then + __git_complete_common "$command" + return 0 + else + return 1 fi } @@ -3134,14 +3502,18 @@ __git_main () ((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 ;; @@ -3164,20 +3536,24 @@ __git_main () --help " ;; - *) __git_compute_porcelain_commands - __gitcomp "$__git_porcelain_commands $(__git_aliases)" ;; + *) + if test -n "${GIT_TESTING_PORCELAIN_COMMAND_LIST-}" + then + __gitcomp "$GIT_TESTING_PORCELAIN_COMMAND_LIST" + else + __gitcomp "$(__git --list-cmds=list-mainporcelain,others,nohelpers,alias,list-complete,config)" + fi + ;; esac return fi - local completion_func="_git_${command//-/_}" - declare -f $completion_func >/dev/null 2>/dev/null && $completion_func && return + __git_complete_command "$command" && return local expansion=$(__git_aliased_command "$command") if [ -n "$expansion" ]; then words[1]=$expansion - completion_func="_git_${expansion//-/_}" - declare -f $completion_func >/dev/null 2>/dev/null && $completion_func + __git_complete_command "$expansion" fi } @@ -3205,76 +3581,8 @@ __gitk_main () __git_complete_revlist } -if [[ -n ${ZSH_VERSION-} ]]; 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 () - { - 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 @@ -3296,17 +3604,42 @@ __git_complete () || complete -o default -o nospace -F $wrapper $1 } -# wrapper for backwards compatibility -_git () -{ - __git_wrap__git_main -} +if ! git --list-cmds=main >/dev/null 2>&1; then -# wrapper for backwards compatibility -_gitk () -{ - __git_wrap__gitk_main -} + declare -A __git_cmds + __git_cmds[list-complete]="apply blame cherry config difftool fsck help instaweb mergetool prune reflog remote repack replace request-pull send-email show-branch stage whatchanged" + __git_cmds[list-guide]="attributes cli core-tutorial credentials cvs-migration diffcore everyday faq glossary hooks ignore modules namespaces remote-helpers repository-layout revisions submodules tutorial-2 tutorial workflows" + __git_cmds[list-mainporcelain]="add am archive bisect branch bundle checkout cherry-pick citool clean clone commit describe diff fetch format-patch gc grep gui init gitk log maintenance merge mv notes pull push range-diff rebase reset restore revert rm shortlog show sparse-checkout stash status submodule switch tag worktree" + __git_cmds[main]="add add--interactive am annotate apply archimport archive bisect bisect--helper blame branch bugreport bundle cat-file check-attr check-ignore check-mailmap check-ref-format checkout checkout-index cherry cherry-pick citool clean clone column commit commit-graph commit-tree config count-objects credential credential-cache credential-cache--daemon credential-gnome-keyring credential-libsecret credential-store cvsexportcommit cvsimport cvsserver daemon describe diff diff-files diff-index diff-tree difftool difftool--helper env--helper fast-export fast-import fetch fetch-pack filter-branch fmt-merge-msg for-each-ref format-patch fsck fsck-objects gc get-tar-commit-id grep gui gui--askpass hash-object help http-backend http-fetch http-push imap-send index-pack init init-db instaweb interpret-trailers log ls-files ls-remote ls-tree mailinfo mailsplit maintenance merge merge-base merge-file merge-index merge-octopus merge-one-file merge-ours merge-recursive merge-recursive-ours merge-recursive-theirs merge-resolve merge-subtree merge-tree mergetool mktag mktree multi-pack-index mv mw name-rev notes p4 pack-objects pack-redundant pack-refs patch-id pickaxe prune prune-packed pull push quiltimport range-diff read-tree rebase rebase--interactive receive-pack reflog remote remote-ext remote-fd remote-ftp remote-ftps remote-http remote-https remote-mediawiki repack replace request-pull rerere reset restore rev-list rev-parse revert rm send-email send-pack sh-i18n--envsubst shell shortlog show show-branch show-index show-ref sparse-checkout stage stash status stripspace submodule submodule--helper subtree svn switch symbolic-ref tag unpack-file unpack-objects update-index update-ref update-server-info upload-archive upload-archive--writer upload-pack var verify-commit verify-pack verify-tag version web--browse whatchanged worktree write-tree" + __git_cmds[others]="compare reintegrate related remote-hg remote-sync send-series smartlist" + __git_cmds[parseopt]="add am apply archive bisect--helper blame branch bugreport cat-file check-attr check-ignore check-mailmap checkout checkout-index cherry cherry-pick clean clone column commit commit-graph config count-objects credential-cache credential-cache--daemon credential-store describe difftool env--helper fast-export fetch fmt-merge-msg for-each-ref format-patch fsck fsck-objects gc grep hash-object help init init-db interpret-trailers log ls-files ls-remote ls-tree merge merge-base merge-file mktree multi-pack-index mv name-rev notes pack-objects pack-refs pickaxe prune prune-packed pull push range-diff read-tree rebase rebase--interactive receive-pack reflog remote repack replace rerere reset restore revert rm send-pack shortlog show show-branch show-index show-ref sparse-checkout stage stash status stripspace switch symbolic-ref tag update-index update-ref update-server-info upload-pack verify-commit verify-pack verify-tag version whatchanged write-tree " + + # Override __git + __git () + { + case "$1" in + --list-cmds=*) + while read -r -d ',' x; do + case "$x" in + nohelpers) + ;; + alias) + ;; + config) + ;; + *) + echo ${__git_cmds[$x]} + ;; + esac + done <<< "${1##--list-cmds=}," + return + ;; + esac + git ${__git_C_args:+"${__git_C_args[@]}"} \ + ${__git_dir:+--git-dir="$__git_dir"} "$@" 2>/dev/null + } + +fi __git_complete git __git_main __git_complete gitk __gitk_main @@ -3315,6 +3648,6 @@ __git_complete gitk __gitk_main # when the user has tab-completed the executable name and consequently # included the '.exe' suffix. # -if [[ "$OSTYPE" = cygwin* ]]; then -__git_complete git.exe __git_main +if [ "$OSTYPE" = cygwin ]; then + __git_complete git.exe __git_main fi diff --git a/plugins/gitfast/git-prompt.sh b/plugins/gitfast/git-prompt.sh index fd2b049db..54e123d63 100644 --- a/plugins/gitfast/git-prompt.sh +++ b/plugins/gitfast/git-prompt.sh @@ -70,6 +70,15 @@ # state symbols by setting GIT_PS1_STATESEPARATOR. The default separator # is SP. # +# When there is an in-progress operation such as a merge, rebase, +# revert, cherry-pick, or bisect, the prompt will include information +# related to the operation, often in the form "|<OPERATION-NAME>". +# +# When the repository has a sparse-checkout, a notification of the form +# "|SPARSE" will be included in the prompt. This can be shortened to a +# single '?' character by setting GIT_PS1_COMPRESSSPARSESTATE, or omitted +# by setting GIT_PS1_OMITSPARSESTATE. +# # By default, __git_ps1 will compare HEAD to your SVN upstream if it can # find one, or @{upstream} otherwise. Once you have set # GIT_PS1_SHOWUPSTREAM, you can override it on a per-repository basis by @@ -88,7 +97,8 @@ # If you would like a colored hint about the current dirty state, set # GIT_PS1_SHOWCOLORHINTS to a nonempty value. The colors are based on # the colored output of "git status -sb" and are available only when -# using __git_ps1 for PROMPT_COMMAND or precmd. +# using __git_ps1 for PROMPT_COMMAND or precmd in Bash, +# but always available in Zsh. # # If you would like __git_ps1 to do nothing in the case when the current # directory is set up to be ignored by git, then set @@ -286,6 +296,37 @@ __git_eread () test -r "$1" && IFS=$'\r\n' read "$2" <"$1" } +# see if a cherry-pick or revert is in progress, if the user has committed a +# conflict resolution with 'git commit' in the middle of a sequence of picks or +# reverts then CHERRY_PICK_HEAD/REVERT_HEAD will not exist so we have to read +# the todo file. +__git_sequencer_status () +{ + local todo + if test -f "$g/CHERRY_PICK_HEAD" + then + r="|CHERRY-PICKING" + return 0; + elif test -f "$g/REVERT_HEAD" + then + r="|REVERTING" + return 0; + elif __git_eread "$g/sequencer/todo" todo + then + case "$todo" in + p[\ \ ]|pick[\ \ ]*) + r="|CHERRY-PICKING" + return 0 + ;; + revert[\ \ ]*) + r="|REVERTING" + return 0 + ;; + esac + fi + return 1 +} + # __git_ps1 accepts 0 or 1 arguments (i.e., format string) # when called from PS1 using command substitution # in this mode it prints text to add to bash PS1 prompt (includes branch name) @@ -390,6 +431,13 @@ __git_ps1 () return $exit fi + local sparse="" + if [ -z "${GIT_PS1_COMPRESSSPARSESTATE}" ] && + [ -z "${GIT_PS1_OMITSPARSESTATE}" ] && + [ "$(git config --bool core.sparseCheckout)" = "true" ]; then + sparse="|SPARSE" + fi + local r="" local b="" local step="" @@ -398,11 +446,7 @@ __git_ps1 () __git_eread "$g/rebase-merge/head-name" b __git_eread "$g/rebase-merge/msgnum" step __git_eread "$g/rebase-merge/end" total - if [ -f "$g/rebase-merge/interactive" ]; then - r="|REBASE-i" - else - r="|REBASE-m" - fi + r="|REBASE" else if [ -d "$g/rebase-apply" ]; then __git_eread "$g/rebase-apply/next" step @@ -417,10 +461,8 @@ __git_ps1 () fi elif [ -f "$g/MERGE_HEAD" ]; then r="|MERGING" - elif [ -f "$g/CHERRY_PICK_HEAD" ]; then - r="|CHERRY-PICKING" - elif [ -f "$g/REVERT_HEAD" ]; then - r="|REVERTING" + elif __git_sequencer_status; then + : elif [ -f "$g/BISECT_LOG" ]; then r="|BISECTING" fi @@ -467,6 +509,7 @@ __git_ps1 () local i="" local s="" local u="" + local h="" local c="" local p="" @@ -499,6 +542,11 @@ __git_ps1 () u="%${ZSH_VERSION+%}" fi + if [ -n "${GIT_PS1_COMPRESSSPARSESTATE}" ] && + [ "$(git config --bool core.sparseCheckout)" = "true" ]; then + h="?" + fi + if [ -n "${GIT_PS1_SHOWUPSTREAM-}" ]; then __git_ps1_show_upstream fi @@ -519,8 +567,8 @@ __git_ps1 () b="\${__git_ps1_branch_name}" fi - local f="$w$i$s$u" - local gitstring="$c$b${f:+$z$f}$r$p" + local f="$h$w$i$s$u" + local gitstring="$c$b${f:+$z$f}${sparse}$r$p" if [ $pcmode = yes ]; then if [ "${__git_printf_supports_v-}" != yes ]; then diff --git a/plugins/gitfast/update b/plugins/gitfast/update index 05054245f..5311065a1 100755 --- a/plugins/gitfast/update +++ b/plugins/gitfast/update @@ -1,9 +1,8 @@ #!/bin/sh -url="https://git.kernel.org/pub/scm/git/git.git/plain/contrib/completion" -version="2.16.0" +url="https://raw.githubusercontent.com/felipec/git-completion" +version="1.2" -curl -s -o _git "${url}/git-completion.zsh?h=v${version}" && -curl -s -o git-completion.bash "${url}/git-completion.bash?h=v${version}" && -curl -s -o git-prompt.sh "${url}/git-prompt.sh?h=v${version}" && -git apply updates.patch +curl -s -o _git "${url}/v${version}/git-completion.zsh" && +curl -s -o git-completion.bash "${url}/v${version}/git-completion.bash" && +curl -s -o git-prompt.sh "${url}/v${version}/git-prompt.sh" diff --git a/plugins/gitfast/updates.patch b/plugins/gitfast/updates.patch deleted file mode 100644 index 28a31f859..000000000 --- a/plugins/gitfast/updates.patch +++ /dev/null @@ -1,56 +0,0 @@ -diff --git b/plugins/gitfast/_git a/plugins/gitfast/_git -index e2554130..a2e3bef5 100644 ---- b/plugins/gitfast/_git -+++ a/plugins/gitfast/_git -@@ -30,7 +30,7 @@ if [ -z "$script" ]; then - local -a locations - local e - locations=( -- $(dirname ${funcsourcetrace[1]%:*})/git-completion.bash -+ "$(dirname ${funcsourcetrace[1]%:*})/git-completion.bash" - '/etc/bash_completion.d/git' # fedora, old debian - '/usr/share/bash-completion/completions/git' # arch, ubuntu, new debian - '/usr/share/bash-completion/git' # gentoo -@@ -214,8 +214,10 @@ _git () - - if (( $+functions[__${service}_zsh_main] )); then - __${service}_zsh_main -- else -+ elif (( $+functions[__${service}_main] )); then - emulate ksh -c __${service}_main -+ elif (( $+functions[_${service}] )); then -+ emulate ksh -c _${service} - fi - - let _ret && _default && _ret=0 -diff --git b/plugins/gitfast/git-completion.bash a/plugins/gitfast/git-completion.bash -index 9c8f7380..14012cab 100644 ---- b/plugins/gitfast/git-completion.bash -+++ a/plugins/gitfast/git-completion.bash -@@ -2915,6 +2915,6 @@ __git_complete gitk __gitk_main - # when the user has tab-completed the executable name and consequently - # included the '.exe' suffix. - # --if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then -+if [[ "$OSTYPE" = cygwin* ]]; then - __git_complete git.exe __git_main - fi -diff --git b/plugins/gitfast/git-prompt.sh a/plugins/gitfast/git-prompt.sh -index 97eacd78..c1de34eb 100644 ---- b/plugins/gitfast/git-prompt.sh -+++ a/plugins/gitfast/git-prompt.sh -@@ -502,9 +502,11 @@ __git_ps1 () - - local z="${GIT_PS1_STATESEPARATOR-" "}" - -- # NO color option unless in PROMPT_COMMAND mode -- if [ $pcmode = yes ] && [ -n "${GIT_PS1_SHOWCOLORHINTS-}" ]; then -- __git_ps1_colorize_gitstring -+ # NO color option unless in PROMPT_COMMAND mode or it's Zsh -+ if [ -n "${GIT_PS1_SHOWCOLORHINTS-}" ]; then -+ if [ $pcmode = yes ] || [ -n "${ZSH_VERSION-}" ]; then -+ __git_ps1_colorize_gitstring -+ fi - fi - - b=${b##refs/heads/} diff --git a/plugins/shell-proxy/proxy.py b/plugins/shell-proxy/proxy.py index 2b62f6cb5..d2e5361cb 100755 --- a/plugins/shell-proxy/proxy.py +++ b/plugins/shell-proxy/proxy.py @@ -30,7 +30,7 @@ def merge(mapping: dict): class CommandSet: proxies = make_proxies(get_http_proxy()) aliases = { - _: "env NAME=%s %s" % (_, ssh_agent) + _: "env __SSH_PROGRAM_NAME__=%s %s" % (_, ssh_agent) for _ in ("ssh", "sftp", "scp", "slogin", "ssh-copy-id") } diff --git a/plugins/shell-proxy/ssh-agent.py b/plugins/shell-proxy/ssh-agent.py index 61cf84c0b..4ee24b755 100755 --- a/plugins/shell-proxy/ssh-agent.py +++ b/plugins/shell-proxy/ssh-agent.py @@ -6,7 +6,7 @@ import sys ssh_proxy = os.path.join(os.path.dirname(__file__), "ssh-proxy.py") argv = [ - os.environ.get("NAME", "ssh"), + os.environ.get("__SSH_PROGRAM_NAME__", "ssh"), "-o", "ProxyCommand={} %h %p".format(ssh_proxy), "-o", diff --git a/plugins/sudo/sudo.plugin.zsh b/plugins/sudo/sudo.plugin.zsh index b3749eff7..f2445a762 100644 --- a/plugins/sudo/sudo.plugin.zsh +++ b/plugins/sudo/sudo.plugin.zsh @@ -9,40 +9,48 @@ # ------- # # * Dongweiming <ciici123@gmail.com> +# * Subhaditya Nath <github.com/subnut> +# * Marc Cornellà <github.com/mcornella> # # ------------------------------------------------------------------------------ +__sudo-replace-buffer() { + local old=$1 new=$2 space=${2:+ } + if [[ ${#LBUFFER} -le ${#old} ]]; then + RBUFFER="${space}${BUFFER#$old }" + LBUFFER="${new}" + else + LBUFFER="${new}${space}${LBUFFER#$old }" + fi +} + sudo-command-line() { [[ -z $BUFFER ]] && LBUFFER="$(fc -ln -1)" # Save beginning space local WHITESPACE="" - if [[ ${LBUFFER:0:1} == " " ]] ; then + if [[ ${LBUFFER:0:1} = " " ]]; then WHITESPACE=" " LBUFFER="${LBUFFER:1}" fi - if [[ -n $EDITOR && $BUFFER == $EDITOR\ * ]]; then - if [[ ${#LBUFFER} -le ${#EDITOR} ]]; then - RBUFFER=" ${BUFFER#$EDITOR }" - LBUFFER="sudoedit" - else - LBUFFER="sudoedit ${LBUFFER#$EDITOR }" - fi - elif [[ $BUFFER == sudoedit\ * ]]; then - if [[ ${#LBUFFER} -le 8 ]]; then - RBUFFER=" ${BUFFER#sudoedit }" - LBUFFER="$EDITOR" - else - LBUFFER="$EDITOR ${LBUFFER#sudoedit }" - fi - elif [[ $BUFFER == sudo\ * ]]; then - if [[ ${#LBUFFER} -le 4 ]]; then - RBUFFER="${BUFFER#sudo }" - LBUFFER="" - else - LBUFFER="${LBUFFER#sudo }" + # Get the first part of the typed command and check if it's an alias to $EDITOR + # If so, locally change $EDITOR to the alias so that it matches below + if [[ -n "$EDITOR" ]]; then + local cmd="${${(Az)BUFFER}[1]}" + if [[ "${aliases[$cmd]} " = (\$EDITOR|$EDITOR)\ * ]]; then + local EDITOR="$cmd" fi + fi + + if [[ -n $EDITOR && $BUFFER = $EDITOR\ * ]]; then + __sudo-replace-buffer "$EDITOR" "sudoedit" + elif [[ -n $EDITOR && $BUFFER = \$EDITOR\ * ]]; then + __sudo-replace-buffer "\$EDITOR" "sudoedit" + elif [[ $BUFFER = sudoedit\ * ]]; then + __sudo-replace-buffer "sudoedit" "$EDITOR" + elif [[ $BUFFER = sudo\ * ]]; then + __sudo-replace-buffer "sudo" "" else LBUFFER="sudo $LBUFFER" fi @@ -50,7 +58,9 @@ sudo-command-line() { # Preserve beginning space LBUFFER="${WHITESPACE}${LBUFFER}" } + zle -N sudo-command-line + # Defined shortcut keys: [Esc] [Esc] bindkey -M emacs '\e\e' sudo-command-line bindkey -M vicmd '\e\e' sudo-command-line diff --git a/plugins/terraform/_terraform b/plugins/terraform/_terraform index e4298f2ab..a19e50670 100644 --- a/plugins/terraform/_terraform +++ b/plugins/terraform/_terraform @@ -6,14 +6,16 @@ _terraform_cmds=( 'console:Interactive console for Terraform interpolations' 'destroy:Destroy Terraform-managed infrastructure' 'fmt:Rewrites config files to canonical format' + 'force-unlock:Manually unlock the terraform state' 'get:Download and install modules for the configuration' 'graph:Create a visual graph of Terraform resources' 'import:Import existing infrastructure into Terraform' 'init:Initialize a Terraform working directory' + 'login:Obtain and save credentials for a remote host' + 'logout:Remove locally-stored credentials for a remote host' 'output:Read an output from a state file' 'plan:Generate and show an execution plan' 'providers:Prints a tree of the providers used in the configuration' - 'push:Upload this Terraform module to Atlas to run' 'refresh:Update local state file against real resources' 'show:Inspect Terraform state or plan' 'state:Advanced state management' @@ -23,6 +25,7 @@ _terraform_cmds=( 'version:Prints the Terraform version' 'workspace:Workspace management' '0.12upgrade:Rewrites pre-0.12 module source code for v0.12' + '0.13upgrade:Rewrites pre-0.13 module source code for v0.13' ) __012upgrade() { @@ -31,45 +34,51 @@ __012upgrade() { '-force[ Override the heuristic that attempts to detect if a configuration is already written for v0.12 or later. Some of the transformations made by this command are not idempotent, so re-running against the same module may change the meanings expressions in the module.]' } +__013upgrade() { + _arguments \ + '-yes[Skip the initial introduction messages and interactive confirmation. This can be used to run this command in batch from a script.]' +} + __apply() { _arguments \ - '-backup=[(path) Path to backup the existing state file before modifying. Defaults to the "-state-out" path with ".backup" extension. Set to "-" to disable backup.]' \ '-auto-approve[Skip interactive approval of plan before applying.]' \ - '-lock=[(true) Lock the state file when locking is supported.]' \ + '-backup=[(path) Path to backup the existing state file before modifying. Defaults to the "-state-out" path with ".backup" extension. Set to "-" to disable backup.]:backupfile:_files -g "*.backup"' \ + '-compact-warnings[If Terraform produces any warnings that are not accompanied by errors, show them in a more compact form that includes only the summary messages.]' \ + '-lock=[(true) Lock the state file when locking is supported.]:lock:(true false)' \ '-lock-timeout=[(0s) Duration to retry a state lock.]' \ '-input=[(true) Ask for input for variables if not directly set.]' \ '-no-color[If specified, output wil be colorless.]' \ '-parallelism=[(10) Limit the number of parallel resource operations.]' \ '-refresh=[(true) Update state prior to checking for differences. This has no effect if a plan file is given to apply.]' \ - '-state=[(terraform.tfstate) Path to read and save state (unless state-out is specified).]' \ - '-state-out=[(path) Path to write state to that is different than "-state". This can be used to preserve the old state.]' \ - '-target=[(resource) Resource to target. Operation will be limited to this resource and its dependencies. This flag can be used multiple times.]:target:__statelist' \ - '-var[("foo=bar") Set a variable in the Terraform configuration. This flag can be set multiple times.]' \ - '-var-file=[(foo) Set variables in the Terraform configuration from a file. If "terraform.tfvars" or any ".auto.tfvars" files are present, they will be automatically loaded.]' + '-state=[(terraform.tfstate) Path to read and save state (unless state-out is specified).]:statefile:_files -g "*.tfstate"' \ + '-state-out=[(path) Path to write state to that is different than "-state". This can be used to preserve the old state.]:statefile:_files -g "*.tfstate"' \ + '*-target=[(resource) Resource to target. Operation will be limited to this resource and its dependencies. This flag can be used multiple times.]:target:__statelist' \ + '*-var[("foo=bar") Set a variable in the Terraform configuration. This flag can be set multiple times.]' \ + '*-var-file=[(foo) Set variables in the Terraform configuration from a file. If "terraform.tfvars" or any ".auto.tfvars" files are present, they will be automatically loaded.]:file:_files -g "*.tfvars{,.json}"' } __console() { _arguments \ '-state=[(terraform.tfstate) Path to read state.]' \ - '-var[("foo=bar") Set a variable in the Terraform configuration. This flag can be set multiple times.]' \ - '-var-file=[(foo) Set variables in the Terraform configuration from a file. If "terraform.tfvars" or any ".auto.tfvars" files are present, they will be automatically loaded.]' + '*-var[("foo=bar") Set a variable in the Terraform configuration. This flag can be set multiple times.]' \ + '*-var-file=[(foo) Set variables in the Terraform configuration from a file. If "terraform.tfvars" or any ".auto.tfvars" files are present, they will be automatically loaded.]:file:_files -g "*.tfvars{,.json}"' } __destroy() { _arguments \ - '-backup=[(path) Path to backup the existing state file before modifying. Defaults to the "-state-out" path with ".backup" extension. Set to "-" to disable backup.]' \ + '-backup=[(path) Path to backup the existing state file before modifying. Defaults to the "-state-out" path with ".backup" extension. Set to "-" to disable backup.]:backupfile:_files -g "*.backup"' \ '-auto-approve[Skip interactive approval before destroying.]' \ '-force[Deprecated: same as auto-approve.]' \ - '-lock=[(true) Lock the state file when locking is supported.]' \ + '-lock=[(true) Lock the state file when locking is supported.]:lock:(true false)' \ '-lock-timeout=[(0s) Duration to retry a state lock.]' \ '-no-color[If specified, output will contain no color.]' \ '-parallelism=[(10) Limit the number of concurrent operations.]' \ '-refresh=[(true) Update state prior to checking for differences. This has no effect if a plan file is given to apply.]' \ - '-state=[(terraform.tfstate) Path to read and save state (unless state-out is specified).]' \ - '-state-out=[(path) Path to write state to that is different than "-state". This can be used to preserve the old state.]' \ - '-target=[(resource) Resource to target. Operation will be limited to this resource and its dependencies. This flag can be used multiple times.]:target:__statelist' \ - '-var[("foo=bar") Set a variable in the Terraform configuration. This flag can be set multiple times.]' \ - '-var-file=[(foo) Set variables in the Terraform configuration from a file. If "terraform.tfvars" or any ".auto.tfvars" files are present, they will be automatically loaded.]' + '-state=[(terraform.tfstate) Path to read and save state (unless state-out is specified).]:statefile:_files -g "*.tfstate"' \ + '-state-out=[(path) Path to write state to that is different than "-state". This can be used to preserve the old state.]:statefile:_files -g "*.tfstate"' \ + '*-target=[(resource) Resource to target. Operation will be limited to this resource and its dependencies. This flag can be used multiple times.]:target:__statelist' \ + '*-var[("foo=bar") Set a variable in the Terraform configuration. This flag can be set multiple times.]' \ + '*-var-file=[(foo) Set variables in the Terraform configuration from a file. If "terraform.tfvars" or any ".auto.tfvars" files are present, they will be automatically loaded.]:file:_files -g "*.tfvars{,.json}"' } __fmt() { @@ -81,33 +90,36 @@ __fmt() { '-recursive=[(false) Also process files in subdirectories. By default, only the given directory (or current directory) is processed.]' } +__force_unlock() { + _arguments \ + "-force[Don't ask for input for unlock confirmation.]" +} + __get() { _arguments \ '-update=[(false) If true, modules already downloaded will be checked for updates and updated if necessary.]' \ - '-no-color[If specified, output will contain no color.]' + '-no-color[Disable text coloring in the output.]' } __graph() { _arguments \ '-draw-cycles[Highlight any cycles in the graph with colored edges. This helps when diagnosing cycle errors.]' \ - '-no-color[If specified, output will contain no color.]' \ '-type=[(plan) Type of graph to output. Can be: plan, plan-destroy, apply, validate, input, refresh.]' } __import() { _arguments \ - '-backup=[(path) Path to backup the existing state file before modifying. Defaults to the "-state-out" path with ".backup" extension. Set to "-" to disable backup.]' \ + '-backup=[(path) Path to backup the existing state file before modifying. Defaults to the "-state-out" path with ".backup" extension. Set to "-" to disable backup.]:backupfile:_files -g "*.backup"' \ '-config=[(path) Path to a directory of Terraform configuration files to use to configure the provider. Defaults to pwd. If no config files are present, they must be provided via the input prompts or env vars.]' \ '-allow-missing-config[Allow import when no resource configuration block exists.]' \ '-input=[(true) Ask for input for variables if not directly set.]' \ - '-lock=[(true) Lock the state file when locking is supported.]' \ + '-lock=[(true) Lock the state file when locking is supported.]:lock:(true false)' \ '-lock-timeout=[(0s) Duration to retry a state lock.]' \ '-no-color[If specified, output will contain no color.]' \ - '-provider=[(provider) Specific provider to use for import. This is used for specifying aliases, such as "aws.eu". Defaults to the normal provider prefix of the resource being imported.]' \ - '-state=[(PATH) Path to the source state file. Defaults to the configured backend, or "terraform.tfstate"]' \ - '-state-out=[(PATH) Path to the destination state file to write to. If this is not specified, the source state file will be used. This can be a new or existing path.]' \ - '-var[("foo=bar") Set a variable in the Terraform configuration. This flag can be set multiple times. This is only useful with the "-config" flag.]' \ - '-var-file=[(foo) Set variables in the Terraform configuration from a file. If "terraform.tfvars" or any ".auto.tfvars" files are present, they will be automatically loaded.]' + '-state=[(PATH) Path to the source state file. Defaults to the configured backend, or "terraform.tfstate"]:statefile:_files -g "*.tfstate"' \ + '-state-out=[(PATH) Path to the destination state file to write to. If this is not specified, the source state file will be used. This can be a new or existing path.]:statefile:_files -g "*.tfstate"' \ + '*-var[("foo=bar") Set a variable in the Terraform configuration. This flag can be set multiple times. This is only useful with the "-config" flag.]' \ + '*-var-file=[(foo) Set variables in the Terraform configuration from a file. If "terraform.tfvars" or any ".auto.tfvars" files are present, they will be automatically loaded.]:file:_files -g "*.tfvars{,.json}"' } __init() { @@ -115,80 +127,94 @@ __init() { '-backend=[(true) Configure the backend for this configuration.]' \ '-backend-config=[This can be either a path to an HCL file with key/value assignments (same format as terraform.tfvars) or a 'key=value' format. This is merged with what is in the configuration file. This can be specified multiple times. The backend type must be in the configuration itself.]' \ '-force-copy[Suppress prompts about copying state data. This is equivalent to providing a "yes" to all confirmation prompts.]' \ - '-from-module=[Copy the contents of the given module into the target directory before initialization.]' \ + '-from-module=[(SOURCE) Copy the contents of the given module into the target directory before initialization.]' \ '-get=[(true) Download any modules for this configuration.]' \ '-get-plugins=[(true) Download any missing plugins for this configuration.]' \ '-input=[(true) Ask for input if necessary. If false, will error if input was required.]' \ - '-lock=[(true) Lock the state file when locking is supported.]' \ + '-lock=[(true) Lock the state file when locking is supported.]:lock:(true false)' \ '-lock-timeout=[(0s) Duration to retry a state lock.]' \ '-no-color[If specified, output will contain no color.]' \ - '-plugin-dir[Directory containing plugin binaries. This overrides all default search paths for plugins, and prevents the automatic installation of plugins. This flag can be used multiple times.]' \ + '-plugin-dir[Directory containing plugin binaries. This overrides all default search paths for plugins, and prevents the automatic installation of plugins. This flag can be used multiple times.]:plugin_dir:_files -/' \ '-reconfigure[Reconfigure the backend, ignoring any saved configuration.]' \ '-upgrade=[(false) If installing modules (-get) or plugins (-get-plugins), ignore previously-downloaded objects and install the latest version allowed within configured constraints.]' \ '-verify-plugins=[(true) Verify the authenticity and integrity of automatically downloaded plugins.]' } +__login() { + _arguments \ + +} + +__logout() { + _arguments \ + +} + __output() { _arguments \ - '-state=[(path) Path to the state file to read. Defaults to "terraform.tfstate".]' \ - '-no-color[ If specified, output will contain no color.]' \ - '-module=[(name) If specified, returns the outputs for a specific module]' \ + '-state=[(path) Path to the state file to read. Defaults to "terraform.tfstate".]:statefile:_files -g "*.tfstate"' \ + '-no-color[If specified, output will contain no color.]' \ '-json[If specified, machine readable output will be printed in JSON format]' } __plan() { _arguments \ - '-destroy[() If set, a plan will be generated to destroy all resources managed by the given configuration and state.]' \ + '-compact-warnings[If Terraform produces any warnings that are not accompanied by errors, show them in a more compact form that includes only the summary messages.]' \ + '-destroy[If set, a plan will be generated to destroy all resources managed by the given configuration and state.]' \ '-detailed-exitcode[() Return detailed exit codes when the command exits. This will change the meaning of exit codes to: 0 - Succeeded, diff is empty (no changes); 1 - Errored, 2 - Succeeded; there is a diff]' \ '-input=[(true) Ask for input for variables if not directly set.]' \ - '-lock=[(true) Lock the state file when locking is supported.]' \ + '-lock=[(true) Lock the state file when locking is supported.]:lock:(true false)' \ '-lock-timeout=[(0s) Duration to retry a state lock.]' \ - '-module-depth=[(n) Specifies the depth of modules to show in the output. This does not affect the plan itself, only the output shown. By default, this is -1, which will expand all.]' \ '-no-color[() If specified, output will contain no color.]' \ '-out=[(path) Write a plan file to the given path. This can be used as input to the "apply" command.]' \ '-parallelism=[(10) Limit the number of concurrent operations.]' \ '-refresh=[(true) Update state prior to checking for differences.]' \ - '-state=[(statefile) Path to a Terraform state file to use to look up Terraform-managed resources. By default it will use the state "terraform.tfstate" if it exists.]' \ - '-target=[(resource) Resource to target. Operation will be limited to this resource and its dependencies. This flag can be used multiple times.]:target:__statelist' \ - '-var[("foo=bar") Set a variable in the Terraform configuration. This flag can be set multiple times.]' \ - '-var-file=[(foo) Set variables in the Terraform configuration from a file. If "terraform.tfvars" or any ".auto.tfvars" files are present, they will be automatically loaded.]' \ + '-state=[(statefile) Path to a Terraform state file to use to look up Terraform-managed resources. By default it will use the state "terraform.tfstate" if it exists.]:statefile:_files -g "*.tfstate"' \ + '*-target=[(resource) Resource to target. Operation will be limited to this resource and its dependencies. This flag can be used multiple times.]:target:__statelist' \ + '*-var[("foo=bar") Set a variable in the Terraform configuration. This flag can be set multiple times.]' \ + '*-var-file=[(foo) Set variables in the Terraform configuration from a file. If "terraform.tfvars" or any ".auto.tfvars" files are present, they will be automatically loaded.]:file:_files -g "*.tfvars{,.json}"' } __providers() { - _arguments \ + local -a __providers_cmds + __providers_cmds=( + 'mirror:Mirrors the provider plugins needed for the current configuration' + 'schema:Prints the schemas of the providers used in the configuration' + ) + _describe -t providers "providers commands" __providers_cmds + +} +__providers_mirror() { + _arguments \ + '-platform=[(os_arch) Choose which target platform to build a mirror for.]' \ + "*:target_dir:_files -/" } -__push() { +__providers_schema() { _arguments \ - '-atlas-address=[(url) An alternate address to an Atlas instance. Defaults to https://atlas.hashicorp.com.]' \ - '-upload-modules=[(true) If true (default), then the modules being used are all locked at their current checkout and uploaded completely to Atlas. This prevents Atlas from running terraform get for you.]' \ - '-name=[(name) Name of the infrastructure configuration in Atlas. The format of this is: "username/name" so that you can upload configurations not just to your account but to other accounts and organizations. This setting can also be set in the configuration in the Atlas section.]' \ - '-no-color[Disables output with coloring]' \ - '-overwrite=[(foo) Marks a specific variable to be updated on Atlas. Normally, if a variable is already set in Atlas, Terraform will not send the local value (even if it is different). This forces it to send the local value to Atlas. This flag can be repeated multiple times.]' \ - '-token=[(token) Atlas API token to use to authorize the upload. If blank or unspecified, the ATLAS_TOKEN environmental variable will be used.]' \ - '-var=[("foo=bar") Set the value of a variable for the Terraform configuration.]' \ - '-var-file=[(foo) Set the value of variables using a variable file.]' \ - '-vcs=[(true) If true (default), then Terraform will detect if a VCS is in use, such as Git, and will only upload files that are committed to version control. If no version control system is detected, Terraform will upload all files in path (parameter to the command).]' + '-json[]' \ + '::' } __refresh() { _arguments \ - '-backup=[(path) Path to backup the existing state file before modifying. Defaults to the "-state-out" path with ".backup" extension. Set to "-" to disable backup.]' \ + '-backup=[(path) Path to backup the existing state file before modifying. Defaults to the "-state-out" path with ".backup" extension. Set to "-" to disable backup.]::backupfile:_files -g "*.backup"' \ + '-compact-warnings[If Terraform produces any warnings that are not accompanied by errors, show them in a more compact form that includes only the summary messages.]' \ '-input=[(true) Ask for input for variables if not directly set.]' \ - '-lock=[(true) Lock the state file when locking is supported.]' \ + '-lock=[(true) Lock the state file when locking is supported.]:lock:(true false)' \ '-lock-timeout=[(0s) Duration to retry a state lock.]' \ '-no-color[If specified, output will not contain any color.]' \ - '-state=[(path) Path to read and save state (unless state-out is specified). Defaults to "terraform.tfstate".]' \ - '-state-out=[(path) Path to write state to that is different than "-state". This can be used to preserve the old state.]' \ - '-target=[(resource) A Resource Address to target. Operation will be limited to this resource and its dependencies. This flag can be used multiple times.]:target:__statelist' \ - '-var[("foo=bar") Set a variable in the Terraform configuration. This flag can be set multiple times.]' \ - '-var-file=[(path) Set variables in the Terraform configuration from a file. If "terraform.tfvars" is present, it will be automatically loaded if this flag is not specified.]' + '-state=[(path) Path to read and save state (unless state-out is specified). Defaults to "terraform.tfstate".]:statefile:_files -g "*.tfstate"' \ + '-state-out=[(path) Path to write state to that is different than "-state". This can be used to preserve the old state.]:statefile:_files -g "*.tfstate"' \ + '*-target=[(resource) A Resource Address to target. Operation will be limited to this resource and its dependencies. This flag can be used multiple times.]:target:__statelist' \ + '*-var[("foo=bar") Set a variable in the Terraform configuration. This flag can be set multiple times.]' \ + '*-var-file=[(foo) Set variables in the Terraform configuration from a file. If "terraform.tfvars" or any ".auto.tfvars" files are present, they will be automatically loaded.]:file:_files -g "*.tfvars{,.json}"' } __show() { _arguments \ - '-module-depth=[(n) The maximum depth to expand modules. By default this is zero, which will not expand modules at all.]' \ + '-json[If specified, output the Terraform plan or state in a machine-readable form.]' \ '-no-color[If specified, output will not contain any color.]' } @@ -199,6 +225,7 @@ __state() { 'mv:Move an item in the state' 'pull:Pull current state and output to stdout' 'push:Update remote state from a local state file' + 'replace-provider:Replace provider for resources in the Terraform state' 'rm:Remove instances from the state' 'show:Show a resource in the state' ) @@ -207,7 +234,7 @@ __state() { __state_list() { _arguments \ - '-state=[(path) Path to a Terraform state file to use to look up Terraform-managed resources. By default it will use the state "terraform.tfstate" if it exists.]' \ + '-state=[(statefile) Path to a Terraform state file to use to look up Terraform-managed resources. By default, Terraform will consult the state of the currently-selected workspace.]' \ '-id=[(id) Filters the results to include only instances whose resource types have an attribute named id whose value equals the given id string.]' \ "*:address:__statelist" } @@ -215,12 +242,12 @@ __state_list() { __state_mv() { _arguments \ "-dry-run[If set, prints out what would've been moved but doesn't actually move anything.]" \ - "-backup=[(path) Path where Terraform should write the backup for the original state. This can't be disabled. If not set, Terraform will write it to the same path as the statefile with a \".backup\" extension.]:file:_files" \ - "-backup-out=[(path) Path where Terraform should write the backup for the destination state. This can't be disabled. If not set, Terraform will write it to the same path as the destination state file with a backup extension. This only needs to be specified if -state-out is set to a different path than -state.]:file:_files" \ - "-lock=[(true|false) Lock the state files when locking is supported.]:lock:(true false)" \ - "-lock-timeout=[(seconds) Duration to retry a state lock.]" \ - '-state=[(path) Path to the source state file. Defaults to the configured backend, or "terraform.tfstate"]:file:_files' \ - "-state-out=[(path) Path to the destination state file to write to. If this isn't specified, the source state file will be used. This can be a new or existing path.]:file:_files" \ + '-backup=[(PATH) Path where Terraform should write the backup for the original state. This can"t be disabled. If not set, Terraform will write it to the same path as the statefile with a ".backup" extension.]:backupfile:_files -g "*.backup"' \ + '-backup-out=[(PATH) Path where Terraform should write the backup for the destination state. This can"t be disabled. If not set, Terraform will write it to the same path as the destination state file with a backup extension. This only needs to be specified if -state-out is set to a different path than -state.]:backupfile:_files -g "*.backup"' \ + "-lock=[(true) Lock the state files when locking is supported.]:lock:(true false)" \ + "-lock-timeout=[(0s) Duration to retry a state lock.]" \ + '-state=[(path) Path to the source state file. Defaults to the configured backend, or "terraform.tfstate"]:statefile:_files -g "*.tfstate"' \ + '-state-out=[(path) Path to the destination state file to write to. If this isn"t specified, the source state file will be used. This can be a new or existing path.]:statefile:_files -g "*.tfstate"' \ "::" \ ":source:__statelist" \ ":destination: " @@ -229,26 +256,37 @@ __state_mv() { __state_push() { _arguments \ "-force[Write the state even if lineages don't match or the remote serial is higher.]" \ - '-lock=[(true|false) Lock the state file when locking is supported.]:lock:(true false)' \ - "-lock-timeout=[(seconds) Duration to retry a state lock.]" \ + '-lock=[(true) Lock the state file when locking is supported.]:lock:(true false)' \ + "-lock-timeout=[(0s) Duration to retry a state lock.]" \ "::" \ ":destination:_files" } +__state_replace_provider() { + _arguments \ + '-auto-approve[Skip interactive approval.]' \ + '-backup=[(PATH) Path where Terraform should write the backup for the state file. This can"t be disabled. If not set, Terraform will write it to the same path as the state file with a ".backup" extension.]:backupfile:_files -g "*.backup"' \ + "-lock=[(true) Lock the state files when locking is supported.]:lock:(true false)" \ + "-lock-timeout=[(0s) Duration to retry a state lock.]" \ + '-state=[(PATH) Path to the source state file. Defaults to the configured backend, or "terraform.tfstate"]:statefile:_files -g "*.tfstate"' \ + ":from_provider_fqn:" \ + ":to_provider_fqn:" +} + __state_rm() { _arguments \ "-dry-run[If set, prints out what would've been removed but doesn't actually remove anything.]" \ - "-backup=[(path) Path where Terraform should write the backup for the original state.]:file:_files" \ - "-lock=[(true|false) Lock the state files when locking is supported.]:lock:(true false)" \ - "-lock-timeout=[(seconds) Duration to retry a state lock.]" \ - '-state=[(path) Path to the state file to update. Defaults to the current workspace state.]:file:_files' \ + '-backup=[(PATH) Path where Terraform should write the backup for the original state.]::backupfile:_files -g "*.backup"' \ + "-lock=[(true) Lock the state file when locking is supported.]:lock:(true false)" \ + "-lock-timeout=[(0s) Duration to retry a state lock.]" \ + '-state=[(PATH) Path to the state file to update. Defaults to the current workspace state.]:statefile:_files -g "*.tfstate"' \ "*:address:__statelist" } __state_show() { _arguments \ - '-state=[(path) Path to a Terraform state file to use to look up Terraform-managed resources. By default it will use the state "terraform.tfstate" if it exists.]' \ + '-state=[(statefile) Path to a Terraform state file to use to look up Terraform-managed resources. By default it will use the state "terraform.tfstate" if it exists.]:statefile:_files -g "*.tfstate"' \ "*:address:__statelist" } @@ -259,34 +297,36 @@ __statelist() { __taint() { _arguments \ '-allow-missing[If specified, the command will succeed (exit code 0) even if the resource is missing.]' \ - '-backup=[(path) Path to backup the existing state file before modifying. Defaults to the "-state-out" path with ".backup" extension. Set to "-" to disable backup.]' \ - '-lock=[(true) Lock the state file when locking is supported.]' \ + '-backup=[(path) Path to backup the existing state file before modifying. Defaults to the "-state-out" path with ".backup" extension. Set to "-" to disable backup.]:backupfile:_files -g "*.backup"' \ + '-lock=[(true) Lock the state file when locking is supported.]:lock:(true false)' \ '-lock-timeout=[(0s) Duration to retry a state lock.]' \ '-module=[(path) The module path where the resource lives. By default this will be root. Child modules can be specified by names. Ex. "consul" or "consul.vpc" (nested modules).]' \ - '-no-color[If specified, output will not contain any color.]' \ - '-state=[(path) Path to read and save state (unless state-out is specified). Defaults to "terraform.tfstate".]' \ - '-state-out=[(path) Path to write updated state file. By default, the "-state" path will be used.]' \ + '-state=[(path) Path to read and save state (unless state-out is specified). Defaults to "terraform.tfstate".]:statefile:_files -g "*.tfstate"' \ + '-state-out=[(path) Path to write updated state file. By default, the "-state" path will be used.]:statefile:_files -g "*.tfstate"' \ "*:address:__statelist" } __untaint() { _arguments \ '-allow-missing[If specified, the command will succeed (exit code 0) even if the resource is missing.]' \ - '-backup=[(path) Path to backup the existing state file before modifying. Defaults to the "-state-out" path with ".backup" extension. Set to "-" to disable backup.]' \ - '-lock=[(true) Lock the state file when locking is supported.]' \ + '-backup=[(path) Path to backup the existing state file before modifying. Defaults to the "-state-out" path with ".backup" extension. Set to "-" to disable backup.]:backupfile:_files -g "*.backup"' \ + '-lock=[(true) Lock the state file when locking is supported.]:lock:(true false)' \ '-lock-timeout=[(0s) Duration to retry a state lock.]' \ '-module=[(path) The module path where the resource lives. By default this will be root. Child modules can be specified by names. Ex. "consul" or "consul.vpc" (nested modules).]' \ - '-no-color[If specified, output will not contain any color.]' \ - '-state=[(path) Path to read and save state (unless state-out is specified). Defaults to "terraform.tfstate".]' \ - '-state-out=[(path) Path to write updated state file. By default, the "-state" path will be used.]' + '-state=[(path) Path to read and save state (unless state-out is specified). Defaults to "terraform.tfstate".]:statefile:_files -g "*.tfstate"' \ + '-state-out=[(path) Path to write updated state file. By default, the "-state" path will be used.]:statefile:_files -g "*.tfstate"' } __validate() { _arguments \ - '-check-variables=[(true) If set to true (default), the command will check whether all required variables have been specified.]' \ '-no-color[If specified, output will not contain any color.]' \ - '-var[("foo=bar") Set a variable in the Terraform configuration. This flag can be set multiple times.]' \ - '-var-file=[(path) Set variables in the Terraform configuration from a file. If "terraform.tfvars" is present, it will be automatically loaded if this flag is not specified.]' + '-json[Produce output in a machine-readable JSON format, suitable for use in text editor integrations and other automated systems.]' \ + ':dir:_files -/' +} + +__version() { + _arguments \ + '-json[Output the version information as a JSON object.]' } __workspace() { @@ -312,6 +352,8 @@ local -a _command_args case "$words[1]" in 0.12upgrade) __012upgrade ;; + 0.13upgrade) + __013upgrade ;; apply) __apply ;; console) @@ -320,6 +362,8 @@ case "$words[1]" in __destroy ;; fmt) __fmt;; + force-unlock) + __force_unlock;; get) __get ;; graph) @@ -328,14 +372,19 @@ case "$words[1]" in __import;; init) __init ;; + login) + __login ;; + logout) + __logout ;; output) __output ;; plan) __plan ;; providers) - __providers ;; - push) - __push ;; + test $CURRENT -lt 3 && __providers + [[ $words[2] = "mirror" ]] && __providers_mirror + [[ $words[2] = "schema" ]] && __providers_schema + ;; refresh) __refresh ;; show) @@ -345,6 +394,7 @@ case "$words[1]" in [[ $words[2] = "list" ]] && __state_list [[ $words[2] = "mv" ]] && __state_mv [[ $words[2] = "push" ]] && __state_push + [[ $words[2] = "replace-provider" ]] && __state_replace_provider [[ $words[2] = "rm" ]] && __state_rm [[ $words[2] = "show" ]] && __state_show ;; @@ -354,6 +404,8 @@ case "$words[1]" in __untaint ;; validate) __validate ;; + version) + __version ;; workspace) test $CURRENT -lt 3 && __workspace ;; esac diff --git a/plugins/thefuck/README.md b/plugins/thefuck/README.md index bd407b316..84f7255ce 100644 --- a/plugins/thefuck/README.md +++ b/plugins/thefuck/README.md @@ -2,6 +2,10 @@ [The Fuck](https://github.com/nvbn/thefuck) plugin — magnificent app which corrects your previous console command. +To use it, add thefuck to the plugins array of your zshrc file: + +plugins=(... thefuck) + ## Usage Press `ESC` twice to correct previous console command. diff --git a/tools/install.sh b/tools/install.sh index 61b21cb9e..953c68baf 100755 --- a/tools/install.sh +++ b/tools/install.sh @@ -37,6 +37,9 @@ # set -e +# Track if $ZSH was provided +custom_zsh=${ZSH:+yes} + # Default settings ZSH=${ZSH:-~/.oh-my-zsh} REPO=${REPO:-ohmyzsh/ohmyzsh} @@ -53,12 +56,16 @@ command_exists() { command -v "$@" >/dev/null 2>&1 } -error() { - echo ${RED}"Error: $@"${RESET} >&2 +fmt_error() { + echo ${RED}"Error: $@"${RESET} >&2 +} + +fmt_underline() { + echo "$(printf '\033[4m')$@$(printf '\033[24m')" } -underline() { - echo "$(printf '\033[4m')$@$(printf '\033[24m')" +fmt_code() { + echo "\`$(printf '\033[38;5;247m')$@${RESET}\`" } setup_color() { @@ -81,71 +88,71 @@ setup_color() { } setup_ohmyzsh() { - # Prevent the cloned repository from having insecure permissions. Failing to do - # so causes compinit() calls to fail with "command not found: compdef" errors - # for users with insecure umasks (e.g., "002", allowing group writability). Note - # that this will be ignored under Cygwin by default, as Windows ACLs take - # precedence over umasks except for filesystems mounted with option "noacl". - umask g-w,o-w - - echo "${BLUE}Cloning Oh My Zsh...${RESET}" - - command_exists git || { - error "git is not installed" - exit 1 - } - - if [ "$OSTYPE" = cygwin ] && git --version | grep -q msysgit; then - error "Windows/MSYS Git is not supported on Cygwin" - error "Make sure the Cygwin git package is installed and is first on the \$PATH" - exit 1 - fi - - git clone -c core.eol=lf -c core.autocrlf=false \ - -c fsck.zeroPaddedFilemode=ignore \ - -c fetch.fsck.zeroPaddedFilemode=ignore \ - -c receive.fsck.zeroPaddedFilemode=ignore \ - --depth=1 --branch "$BRANCH" "$REMOTE" "$ZSH" || { - error "git clone of oh-my-zsh repo failed" - exit 1 - } - - echo + # Prevent the cloned repository from having insecure permissions. Failing to do + # so causes compinit() calls to fail with "command not found: compdef" errors + # for users with insecure umasks (e.g., "002", allowing group writability). Note + # that this will be ignored under Cygwin by default, as Windows ACLs take + # precedence over umasks except for filesystems mounted with option "noacl". + umask g-w,o-w + + echo "${BLUE}Cloning Oh My Zsh...${RESET}" + + command_exists git || { + fmt_error "git is not installed" + exit 1 + } + + if [ "$OSTYPE" = cygwin ] && git --version | grep -q msysgit; then + fmt_error "Windows/MSYS Git is not supported on Cygwin" + fmt_error "Make sure the Cygwin git package is installed and is first on the \$PATH" + exit 1 + fi + + git clone -c core.eol=lf -c core.autocrlf=false \ + -c fsck.zeroPaddedFilemode=ignore \ + -c fetch.fsck.zeroPaddedFilemode=ignore \ + -c receive.fsck.zeroPaddedFilemode=ignore \ + --depth=1 --branch "$BRANCH" "$REMOTE" "$ZSH" || { + fmt_error "git clone of oh-my-zsh repo failed" + exit 1 + } + + echo } setup_zshrc() { - # Keep most recent old .zshrc at .zshrc.pre-oh-my-zsh, and older ones - # with datestamp of installation that moved them aside, so we never actually - # destroy a user's original zshrc - echo "${BLUE}Looking for an existing zsh config...${RESET}" - - # Must use this exact name so uninstall.sh can find it - OLD_ZSHRC=~/.zshrc.pre-oh-my-zsh - if [ -f ~/.zshrc ] || [ -h ~/.zshrc ]; then - # Skip this if the user doesn't want to replace an existing .zshrc - if [ $KEEP_ZSHRC = yes ]; then - echo "${YELLOW}Found ~/.zshrc.${RESET} ${GREEN}Keeping...${RESET}" - return - fi - if [ -e "$OLD_ZSHRC" ]; then - OLD_OLD_ZSHRC="${OLD_ZSHRC}-$(date +%Y-%m-%d_%H-%M-%S)" - if [ -e "$OLD_OLD_ZSHRC" ]; then - error "$OLD_OLD_ZSHRC exists. Can't back up ${OLD_ZSHRC}" - error "re-run the installer again in a couple of seconds" - exit 1 - fi - mv "$OLD_ZSHRC" "${OLD_OLD_ZSHRC}" - - echo "${YELLOW}Found old ~/.zshrc.pre-oh-my-zsh." \ - "${GREEN}Backing up to ${OLD_OLD_ZSHRC}${RESET}" - fi - echo "${YELLOW}Found ~/.zshrc.${RESET} ${GREEN}Backing up to ${OLD_ZSHRC}${RESET}" - mv ~/.zshrc "$OLD_ZSHRC" - fi - - echo "${GREEN}Using the Oh My Zsh template file and adding it to ~/.zshrc.${RESET}" - - sed "/^export ZSH=/ c\\ + # Keep most recent old .zshrc at .zshrc.pre-oh-my-zsh, and older ones + # with datestamp of installation that moved them aside, so we never actually + # destroy a user's original zshrc + echo "${BLUE}Looking for an existing zsh config...${RESET}" + + # Must use this exact name so uninstall.sh can find it + OLD_ZSHRC=~/.zshrc.pre-oh-my-zsh + if [ -f ~/.zshrc ] || [ -h ~/.zshrc ]; then + # Skip this if the user doesn't want to replace an existing .zshrc + if [ $KEEP_ZSHRC = yes ]; then + echo "${YELLOW}Found ~/.zshrc.${RESET} ${GREEN}Keeping...${RESET}" + return + fi + if [ -e "$OLD_ZSHRC" ]; then + OLD_OLD_ZSHRC="${OLD_ZSHRC}-$(date +%Y-%m-%d_%H-%M-%S)" + if [ -e "$OLD_OLD_ZSHRC" ]; then + fmt_error "$OLD_OLD_ZSHRC exists. Can't back up ${OLD_ZSHRC}" + fmt_error "re-run the installer again in a couple of seconds" + exit 1 + fi + mv "$OLD_ZSHRC" "${OLD_OLD_ZSHRC}" + + echo "${YELLOW}Found old ~/.zshrc.pre-oh-my-zsh." \ + "${GREEN}Backing up to ${OLD_OLD_ZSHRC}${RESET}" + fi + echo "${YELLOW}Found ~/.zshrc.${RESET} ${GREEN}Backing up to ${OLD_ZSHRC}${RESET}" + mv ~/.zshrc "$OLD_ZSHRC" + fi + + echo "${GREEN}Using the Oh My Zsh template file and adding it to ~/.zshrc.${RESET}" + + sed "/^export ZSH=/ c\\ export ZSH=\"$ZSH\" " "$ZSH/templates/zshrc.zsh-template" > ~/.zshrc-omztemp mv -f ~/.zshrc-omztemp ~/.zshrc @@ -154,146 +161,160 @@ export ZSH=\"$ZSH\" } setup_shell() { - # Skip setup if the user wants or stdin is closed (not running interactively). - if [ $CHSH = no ]; then - return - fi - - # If this user's login shell is already "zsh", do not attempt to switch. - if [ "$(basename "$SHELL")" = "zsh" ]; then - return - fi - - # If this platform doesn't provide a "chsh" command, bail out. - if ! command_exists chsh; then - cat <<-EOF - I can't change your shell automatically because this system does not have chsh. - ${BLUE}Please manually change your default shell to zsh${RESET} - EOF - return - fi - - echo "${BLUE}Time to change your default shell to zsh:${RESET}" - - # Prompt for user choice on changing the default login shell - printf "${YELLOW}Do you want to change your default shell to zsh? [Y/n]${RESET} " - read opt - case $opt in - y*|Y*|"") echo "Changing the shell..." ;; - n*|N*) echo "Shell change skipped."; return ;; - *) echo "Invalid choice. Shell change skipped."; return ;; - esac - - # Check if we're running on Termux - case "$PREFIX" in - *com.termux*) termux=true; zsh=zsh ;; - *) termux=false ;; - esac - - if [ "$termux" != true ]; then - # Test for the right location of the "shells" file - if [ -f /etc/shells ]; then - shells_file=/etc/shells - elif [ -f /usr/share/defaults/etc/shells ]; then # Solus OS - shells_file=/usr/share/defaults/etc/shells - else - error "could not find /etc/shells file. Change your default shell manually." - return - fi - - # Get the path to the right zsh binary - # 1. Use the most preceding one based on $PATH, then check that it's in the shells file - # 2. If that fails, get a zsh path from the shells file, then check it actually exists - if ! zsh=$(which zsh) || ! grep -qx "$zsh" "$shells_file"; then - if ! zsh=$(grep '^/.*/zsh$' "$shells_file" | tail -1) || [ ! -f "$zsh" ]; then - error "no zsh binary found or not present in '$shells_file'" - error "change your default shell manually." - return - fi - fi - fi - - # We're going to change the default shell, so back up the current one - if [ -n "$SHELL" ]; then - echo $SHELL > ~/.shell.pre-oh-my-zsh - else - grep "^$USER:" /etc/passwd | awk -F: '{print $7}' > ~/.shell.pre-oh-my-zsh - fi - - # Actually change the default shell to zsh - if ! chsh -s "$zsh"; then - error "chsh command unsuccessful. Change your default shell manually." - else - export SHELL="$zsh" - echo "${GREEN}Shell successfully changed to '$zsh'.${RESET}" - fi - - echo + # Skip setup if the user wants or stdin is closed (not running interactively). + if [ $CHSH = no ]; then + return + fi + + # If this user's login shell is already "zsh", do not attempt to switch. + if [ "$(basename -- "$SHELL")" = "zsh" ]; then + return + fi + + # If this platform doesn't provide a "chsh" command, bail out. + if ! command_exists chsh; then + cat <<EOF +I can't change your shell automatically because this system does not have chsh. +${BLUE}Please manually change your default shell to zsh${RESET} +EOF + return + fi + + echo "${BLUE}Time to change your default shell to zsh:${RESET}" + + # Prompt for user choice on changing the default login shell + printf "${YELLOW}Do you want to change your default shell to zsh? [Y/n]${RESET} " + read opt + case $opt in + y*|Y*|"") echo "Changing the shell..." ;; + n*|N*) echo "Shell change skipped."; return ;; + *) echo "Invalid choice. Shell change skipped."; return ;; + esac + + # Check if we're running on Termux + case "$PREFIX" in + *com.termux*) termux=true; zsh=zsh ;; + *) termux=false ;; + esac + + if [ "$termux" != true ]; then + # Test for the right location of the "shells" file + if [ -f /etc/shells ]; then + shells_file=/etc/shells + elif [ -f /usr/share/defaults/etc/shells ]; then # Solus OS + shells_file=/usr/share/defaults/etc/shells + else + fmt_error "could not find /etc/shells file. Change your default shell manually." + return + fi + + # Get the path to the right zsh binary + # 1. Use the most preceding one based on $PATH, then check that it's in the shells file + # 2. If that fails, get a zsh path from the shells file, then check it actually exists + if ! zsh=$(which zsh) || ! grep -qx "$zsh" "$shells_file"; then + if ! zsh=$(grep '^/.*/zsh$' "$shells_file" | tail -1) || [ ! -f "$zsh" ]; then + fmt_error "no zsh binary found or not present in '$shells_file'" + fmt_error "change your default shell manually." + return + fi + fi + fi + + # We're going to change the default shell, so back up the current one + if [ -n "$SHELL" ]; then + echo $SHELL > ~/.shell.pre-oh-my-zsh + else + grep "^$USER:" /etc/passwd | awk -F: '{print $7}' > ~/.shell.pre-oh-my-zsh + fi + + # Actually change the default shell to zsh + if ! chsh -s "$zsh"; then + fmt_error "chsh command unsuccessful. Change your default shell manually." + else + export SHELL="$zsh" + echo "${GREEN}Shell successfully changed to '$zsh'.${RESET}" + fi + + echo } main() { - # Run as unattended if stdin is closed - if [ ! -t 0 ]; then - RUNZSH=no - CHSH=no - fi - - # Parse arguments - while [ $# -gt 0 ]; do - case $1 in - --unattended) RUNZSH=no; CHSH=no ;; - --skip-chsh) CHSH=no ;; - --keep-zshrc) KEEP_ZSHRC=yes ;; - esac - shift - done - - setup_color - - if ! command_exists zsh; then - echo "${YELLOW}Zsh is not installed.${RESET} Please install zsh first." - exit 1 - fi - - if [ -d "$ZSH" ]; then - cat <<-EOF - ${YELLOW}You already have Oh My Zsh installed.${RESET} - You'll need to remove '$ZSH' if you want to reinstall. - EOF - exit 1 - fi - - setup_ohmyzsh - setup_zshrc - setup_shell - - printf "$GREEN" - cat <<-'EOF' - __ __ - ____ / /_ ____ ___ __ __ ____ _____/ /_ - / __ \/ __ \ / __ `__ \/ / / / /_ / / ___/ __ \ - / /_/ / / / / / / / / / / /_/ / / /_(__ ) / / / - \____/_/ /_/ /_/ /_/ /_/\__, / /___/____/_/ /_/ - /____/ ....is now installed! - - - EOF - cat <<-EOF - Before you scream Oh My Zsh! please look over the ~/.zshrc file to select plugins, themes, and options. - - • Follow us on Twitter: $(underline https://twitter.com/ohmyzsh) - • Join our Discord server: $(underline https://discord.gg/ohmyzsh) - • Get stickers, shirts, coffee mugs and other swag: $(underline https://shop.planetargon.com/collections/oh-my-zsh) - - EOF - printf "$RESET" - - if [ $RUNZSH = no ]; then - echo "${YELLOW}Run zsh to try it out.${RESET}" - exit - fi - - exec zsh -l + # Run as unattended if stdin is not a tty + if [ ! -t 0 ]; then + RUNZSH=no + CHSH=no + fi + + # Parse arguments + while [ $# -gt 0 ]; do + case $1 in + --unattended) RUNZSH=no; CHSH=no ;; + --skip-chsh) CHSH=no ;; + --keep-zshrc) KEEP_ZSHRC=yes ;; + esac + shift + done + + setup_color + + if ! command_exists zsh; then + echo "${YELLOW}Zsh is not installed.${RESET} Please install zsh first." + exit 1 + fi + + if [ -d "$ZSH" ]; then + echo "${YELLOW}The \$ZSH folder already exists ($ZSH).${RESET}" + if [ "$custom_zsh" = yes ]; then + cat <<EOF + +You ran the installer with the \$ZSH setting or the \$ZSH variable is +exported. You have 3 options: + +1. Unset the ZSH variable when calling the installer: + $(fmt_code "ZSH= sh install.sh") +2. Install Oh My Zsh to a directory that doesn't exist yet: + $(fmt_code "ZSH=path/to/new/ohmyzsh/folder sh install.sh") +3. (Caution) If the folder doesn't contain important information, + you can just remove it with $(fmt_code "rm -r $ZSH") + +EOF + else + echo "You'll need to remove it if you want to reinstall." + fi + exit 1 + fi + + setup_ohmyzsh + setup_zshrc + setup_shell + + printf "$GREEN" + cat <<'EOF' + __ __ + ____ / /_ ____ ___ __ __ ____ _____/ /_ + / __ \/ __ \ / __ `__ \/ / / / /_ / / ___/ __ \ +/ /_/ / / / / / / / / / / /_/ / / /_(__ ) / / / +\____/_/ /_/ /_/ /_/ /_/\__, / /___/____/_/ /_/ + /____/ ....is now installed! + + +EOF + cat <<EOF +Before you scream Oh My Zsh! please look over the ~/.zshrc file to select plugins, themes, and options. + +• Follow us on Twitter: $(fmt_underline https://twitter.com/ohmyzsh) +• Join our Discord server: $(fmt_underline https://discord.gg/ohmyzsh) +• Get stickers, shirts, coffee mugs and other swag: $(fmt_underline https://shop.planetargon.com/collections/oh-my-zsh) + +EOF + printf "$RESET" + + if [ $RUNZSH = no ]; then + echo "${YELLOW}Run zsh to try it out.${RESET}" + exit + fi + + exec zsh -l } main "$@" |