~willdurand/public-inbox

dotfiles: zsh: fix/update git completion v1 APPLIED

William Durand: 1
 zsh: fix/update git completion

 4 files changed, 806 insertions(+), 222 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~willdurand/public-inbox/patches/20361/mbox | git am -3
Learn more about email & git

[PATCH dotfiles] zsh: fix/update git completion Export this patch

Fix git completion in zsh. It didn't work on Ubuntu for some reasons so
I started by updated the completion files and fixed the issue by
allowing completion of aliases in zsh.

Signed-off-by: William Durand <will+git@drnd.me>
---
 bin/install             |  20 +-
 git/git-completion.bash | 710 ++++++++++++++++++++++++++++------------
 zsh/_git                | 294 +++++++++++++++++
 zsh/zshrc               |   4 +-
 4 files changed, 806 insertions(+), 222 deletions(-)
 create mode 100644 zsh/_git

diff --git a/bin/install b/bin/install
index c39d33b..a242308 100755
--- a/bin/install
@@ -36,14 +36,25 @@ ensure_dir() {
}

setup_git() {
    link_files git/git-completion.bash ~/.git-completion.bash
    link_files git/gitattributes ~/.gitattributes
    link_files git/gitconfig ~/.gitconfig
    link_files git/gitignore_global ~/.gitignore_global
    link_files zsh/_yarn ~/.zsh/_yarn
    link_files git/git-completion.bash ~/.git-completion.bash
    link_files git/commands/npm-release /usr/local/bin/git-npm-release
}

setup_zsh() {
    ensure_dir ~/.zsh

    link_files zsh/pure/pure.zsh ~/.zsh/prompt_pure_setup
    link_files zsh/pure/async.zsh ~/.zsh/async
    link_files zsh/zshrc ~/.zshrc

    for fn in zsh/_*; do
        link_files "$fn" ~/.zsh/$(basename $fn)
    done
}

# Update submodules first
git submodule update --init

@@ -51,7 +62,6 @@ git submodule update --init
link_files tmux/tmux.conf ~/.tmux.conf

# git
ensure_dir ~/.zsh
setup_git

# vim
@@ -59,9 +69,7 @@ link_files vim ~/.vim
link_files vim/vimrc ~/.vimrc

# zsh
link_files zsh/pure/pure.zsh ~/.zsh/prompt_pure_setup
link_files zsh/pure/async.zsh ~/.zsh/async
link_files zsh/zshrc ~/.zshrc
setup_zsh

# rg
link_files ripgrep/rgignore ~/.rgignore
diff --git a/git/git-completion.bash b/git/git-completion.bash
index 00fbe6c..7dc6cd8 100644
--- a/git/git-completion.bash
+++ b/git/git-completion.bash
@@ -29,6 +29,15 @@
# tell the completion to use commit completion.  This also works with aliases
# of form "!sh -c '...'".  For example, "!sh -c ': git commit ; ... '".
#
# If you have a command that is not part of git, but you would still
# like completion, you can use __git_complete:
#
#   __git_complete gl git_log
#
# Or if it's a main command (i.e. git or gitk):
#
#   __git_complete gk gitk
#
# Compatible with bash 3.2.57.
#
# You can set the following environment variables to influence the behavior of
@@ -39,6 +48,11 @@
#     When set to "1", do not include "DWIM" suggestions in git-checkout
#     and git-switch completion (e.g., completing "foo" when "origin/foo"
#     exists).
#
#   GIT_COMPLETION_SHOW_ALL
#
#     When set to "1" suggest all options, including options which are
#     typically hidden (e.g. '--allow-empty' for 'git commit').

case "$COMP_WORDBREAKS" in
*:*) : great ;;
@@ -50,7 +64,7 @@ esac
# variable.
__git_find_repo_path ()
{
	if [ -n "$__git_repo_path" ]; then
	if [ -n "${__git_repo_path-}" ]; then
		# we already know where it is
		return
	fi
@@ -301,6 +315,19 @@ __gitcomp_direct ()
	COMPREPLY=($1)
}

# Similar to __gitcomp_direct, but appends to COMPREPLY instead.
# Callers must take care of providing only words that match the current word
# to be completed and adding any prefix and/or suffix (trailing space!), if
# necessary.
# 1: List of newline-separated matching completion words, complete with
#    prefix and suffix.
__gitcomp_direct_append ()
{
	local IFS=$'\n'

	COMPREPLY+=($1)
}

__gitcompappend ()
{
	local x i=${#COMPREPLY[@]}
@@ -373,7 +400,7 @@ __gitcomp ()
# Clear the variables caching builtins' options when (re-)sourcing
# the completion script.
if [[ -n ${ZSH_VERSION-} ]]; then
	unset $(set |sed -ne 's/^\(__gitcomp_builtin_[a-zA-Z0-9_][a-zA-Z0-9_]*\)=.*/\1/p') 2>/dev/null
	unset ${(M)${(k)parameters[@]}:#__gitcomp_builtin_*} 2>/dev/null
else
	unset $(compgen -v __gitcomp_builtin_)
fi
@@ -391,17 +418,23 @@ __gitcomp_builtin ()
	# spaces must be replaced with underscore for multi-word
	# commands, e.g. "git remote add" becomes remote_add.
	local cmd="$1"
	local incl="$2"
	local excl="$3"
	local incl="${2-}"
	local excl="${3-}"

	local var=__gitcomp_builtin_"${cmd/-/_}"
	local options
	eval "options=\$$var"
	eval "options=\${$var-}"

	if [ -z "$options" ]; then
		local completion_helper
		if [ "$GIT_COMPLETION_SHOW_ALL" = "1" ]; then
			completion_helper="--git-completion-helper-all"
		else
			completion_helper="--git-completion-helper"
		fi
		# leading and trailing spaces are significant to make
		# option removal work correctly.
		options=" $incl $(__git ${cmd/_/ } --git-completion-helper) " || return
		options=" $incl $(__git ${cmd/_/ } $completion_helper) " || return

		for i in $excl; do
			options="${options/ $i / }"
@@ -504,7 +537,7 @@ __git_index_files ()
{
	local root="$2" match="$3"

	__git_ls_files_helper "$root" "$1" "$match" |
	__git_ls_files_helper "$root" "$1" "${match:-?}" |
	awk -F / -v pfx="${2//\\/\\\\}" '{
		paths[$1] = 1
	}
@@ -550,7 +583,7 @@ __git_index_files ()
						 esc_idx, 1)
			} else if (esc == "n") {
				# Uh-oh, a newline character.
				# We cant reliably put a pathname
				# We cannot reliably put a pathname
				# containing a newline into COMPREPLY,
				# and the newline would create a mess.
				# Skip this path.
@@ -565,7 +598,7 @@ __git_index_files ()
			}
		}
		# Drop closing double quote, if there is one.
		# (There isnt any if this is a directory, as it was
		# (There is not any if this is a directory, as it was
		# already stripped with the trailing path components.)
		if (substr(p, length(p), 1) == "\"")
			out = out substr(p, 1, length(p) - 1)
@@ -611,6 +644,19 @@ __git_heads ()
			"refs/heads/$cur_*" "refs/heads/$cur_*/**"
}

# Lists branches from remote repositories.
# 1: A prefix to be added to each listed branch (optional).
# 2: List only branches matching this word (optional; list all branches if
#    unset or empty).
# 3: A suffix to be appended to each listed branch (optional).
__git_remote_heads ()
{
	local pfx="${1-}" cur_="${2-}" sfx="${3-}"

	__git for-each-ref --format="${pfx//\%/%%}%(refname:strip=2)$sfx" \
			"refs/remotes/$cur_*" "refs/remotes/$cur_*/**"
}

# Lists tags from the local repository.
# Accepts the same positional parameters as __git_heads() above.
__git_tags ()
@@ -621,6 +667,26 @@ __git_tags ()
			"refs/tags/$cur_*" "refs/tags/$cur_*/**"
}

# List unique branches from refs/remotes used for 'git checkout' and 'git
# switch' tracking DWIMery.
# 1: A prefix to be added to each listed branch (optional)
# 2: List only branches matching this word (optional; list all branches if
#    unset or empty).
# 3: A suffix to be appended to each listed branch (optional).
__git_dwim_remote_heads ()
{
	local pfx="${1-}" cur_="${2-}" sfx="${3-}"
	local fer_pfx="${pfx//\%/%%}" # "escape" for-each-ref format specifiers

	# employ the heuristic used by git checkout and git switch
	# Try to find a remote branch that cur_es the completion word
	# but only output if the branch name is unique
	__git for-each-ref --format="$fer_pfx%(refname:strip=3)$sfx" \
		--sort="refname:strip=3" \
		"refs/remotes/*/$cur_*" "refs/remotes/*/$cur_*/**" | \
	uniq -u
}

# Lists refs from the local (by default) or from a remote repository.
# It accepts 0, 1 or 2 arguments:
# 1: The remote to list refs from (optional; ignored, if set but empty).
@@ -696,13 +762,7 @@ __git_refs ()
		__git_dir="$dir" __git for-each-ref --format="$fer_pfx%($format)$sfx" \
			"${refs[@]}"
		if [ -n "$track" ]; then
			# employ the heuristic used by git checkout
			# Try to find a remote branch that matches the completion word
			# but only output if the branch name is unique
			__git for-each-ref --format="$fer_pfx%(refname:strip=3)$sfx" \
				--sort="refname:strip=3" \
				"refs/remotes/*/$match*" "refs/remotes/*/$match*/**" | \
			uniq -u
			__git_dwim_remote_heads "$pfx" "$match" "$sfx"
		fi
		return
	fi
@@ -749,29 +809,51 @@ __git_refs ()
# Usage: __git_complete_refs [<option>]...
# --remote=<remote>: The remote to list refs from, can be the name of a
#                    configured remote, a path, or a URL.
# --track: List unique remote branches for 'git checkout's tracking DWIMery.
# --dwim: List unique remote branches for 'git switch's tracking DWIMery.
# --pfx=<prefix>: A prefix to be added to each ref.
# --cur=<word>: The current ref to be completed.  Defaults to the current
#               word to be completed.
# --sfx=<suffix>: A suffix to be appended to each ref instead of the default
#                 space.
# --mode=<mode>: What set of refs to complete, one of 'refs' (the default) to
#                complete all refs, 'heads' to complete only branches, or
#                'remote-heads' to complete only remote branches. Note that
#                --remote is only compatible with --mode=refs.
__git_complete_refs ()
{
	local remote track pfx cur_="$cur" sfx=" "
	local remote= dwim= pfx= cur_="$cur" sfx=" " mode="refs"

	while test $# != 0; do
		case "$1" in
		--remote=*)	remote="${1##--remote=}" ;;
		--track)	track="yes" ;;
		--dwim)		dwim="yes" ;;
		# --track is an old spelling of --dwim
		--track)	dwim="yes" ;;
		--pfx=*)	pfx="${1##--pfx=}" ;;
		--cur=*)	cur_="${1##--cur=}" ;;
		--sfx=*)	sfx="${1##--sfx=}" ;;
		--mode=*)	mode="${1##--mode=}" ;;
		*)		return 1 ;;
		esac
		shift
	done

	__gitcomp_direct "$(__git_refs "$remote" "$track" "$pfx" "$cur_" "$sfx")"
	# complete references based on the specified mode
	case "$mode" in
		refs)
			__gitcomp_direct "$(__git_refs "$remote" "" "$pfx" "$cur_" "$sfx")" ;;
		heads)
			__gitcomp_direct "$(__git_heads "$pfx" "$cur_" "$sfx")" ;;
		remote-heads)
			__gitcomp_direct "$(__git_remote_heads "$pfx" "$cur_" "$sfx")" ;;
		*)
			return 1 ;;
	esac

	# Append DWIM remote branch names if requested
	if [ "$dwim" = "yes" ]; then
		__gitcomp_direct_append "$(__git_dwim_remote_heads "$pfx" "$cur_" "$sfx")"
	fi
}

# __git_refs2 requires 1 argument (to pass to __git_refs)
@@ -1047,37 +1129,72 @@ __git_pretty_aliases ()
# __git_aliased_command requires 1 argument
__git_aliased_command ()
{
	local word cmdline=$(__git config --get "alias.$1")
	for word in $cmdline; do
		case "$word" in
		\!gitk|gitk)
			echo "gitk"
			return
			;;
		\!*)	: shell command alias ;;
		-*)	: option ;;
		*=*)	: setting env ;;
		git)	: git itself ;;
		\(\))   : skip parens of shell function definition ;;
		{)	: skip start of shell helper function ;;
		:)	: skip null command ;;
		\'*)	: skip opening quote after sh -c ;;
		*)
			echo "$word"
	local cur=$1 last list word cmdline

	while [[ -n "$cur" ]]; do
		if [[ "$list" == *" $cur "* ]]; then
			# loop detected
			return
		esac
		fi

		cmdline=$(__git config --get "alias.$cur")
		list=" $cur $list"
		last=$cur
		cur=

		for word in $cmdline; do
			case "$word" in
			\!gitk|gitk)
				cur="gitk"
				break
				;;
			\!*)	: shell command alias ;;
			-*)	: option ;;
			*=*)	: setting env ;;
			git)	: git itself ;;
			\(\))   : skip parens of shell function definition ;;
			{)	: skip start of shell helper function ;;
			:)	: skip null command ;;
			\'*)	: skip opening quote after sh -c ;;
			*)
				cur="$word"
				break
			esac
		done
	done

	cur=$last
	if [[ "$cur" != "$1" ]]; then
		echo "$cur"
	fi
}

# __git_find_on_cmdline requires 1 argument
# Check whether one of the given words is present on the command line,
# and print the first word found.
#
# Usage: __git_find_on_cmdline [<option>]... "<wordlist>"
# --show-idx: Optionally show the index of the found word in the $words array.
__git_find_on_cmdline ()
{
	local word subcommand c=1
	local word c=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
@@ -1085,6 +1202,40 @@ __git_find_on_cmdline ()
	done
}

# Similar to __git_find_on_cmdline, except that it loops backwards and thus
# prints the *last* word found. Useful for finding which of two options that
# supersede each other came last, such as "--guess" and "--no-guess".
#
# Usage: __git_find_last_on_cmdline [<option>]... "<wordlist>"
# --show-idx: Optionally show the index of the found word in the $words array.
__git_find_last_on_cmdline ()
{
	local word c=$cword show_idx

	while test $# -gt 1; do
		case "$1" in
		--show-idx)	show_idx=y ;;
		*)		return 1 ;;
		esac
		shift
	done
	local wordlist="$1"

	while [ $c -gt 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
@@ -1180,6 +1331,7 @@ __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 ()
@@ -1198,6 +1350,10 @@ _git_am ()
		__gitcomp "$__git_patchformat" "" "${cur##--patch-format=}"
		return
		;;
	--show-current-patch=*)
		__gitcomp "$__git_showcurrentpatch" "" "${cur##--show-current-patch=}"
		return
		;;
	--*)
		__gitcomp_builtin am "" \
			"$__git_am_inprogress_options"
@@ -1291,8 +1447,10 @@ _git_branch ()
	while [ $c -lt $cword ]; do
		i="${words[c]}"
		case "$i" in
		-d|--delete|-m|--move)	only_local_ref="y" ;;
		-r|--remotes)		has_r="y" ;;
		-d|-D|--delete|-m|-M|--move|-c|-C|--copy)
			only_local_ref="y" ;;
		-r|--remotes)
			has_r="y" ;;
		esac
		((c++))
	done
@@ -1334,10 +1492,73 @@ _git_bundle ()
	esac
}

# Helper function to decide whether or not we should enable DWIM logic for
# git-switch and git-checkout.
#
# To decide between the following rules in decreasing priority order:
# - the last provided of "--guess" or "--no-guess" explicitly enable or
#   disable completion of DWIM logic respectively.
# - If checkout.guess is false, disable completion of DWIM logic.
# - If the --no-track option is provided, take this as a hint to disable the
#   DWIM completion logic
# - If GIT_COMPLETION_CHECKOUT_NO_GUESS is set, disable the DWIM completion
#   logic, as requested by the user.
# - Enable DWIM logic otherwise.
#
__git_checkout_default_dwim_mode ()
{
	local last_option dwim_opt="--dwim"

	if [ "${GIT_COMPLETION_CHECKOUT_NO_GUESS-}" = "1" ]; then
		dwim_opt=""
	fi

	# --no-track disables DWIM, but with lower priority than
	# --guess/--no-guess/checkout.guess
	if [ -n "$(__git_find_on_cmdline "--no-track")" ]; then
		dwim_opt=""
	fi

	# checkout.guess = false disables DWIM, but with lower priority than
	# --guess/--no-guess
	if [ "$(__git config --type=bool checkout.guess)" = "false" ]; then
		dwim_opt=""
	fi

	# Find the last provided --guess or --no-guess
	last_option="$(__git_find_last_on_cmdline "--guess --no-guess")"
	case "$last_option" in
		--guess)
			dwim_opt="--dwim"
			;;
		--no-guess)
			dwim_opt=""
			;;
	esac

	echo "$dwim_opt"
}

_git_checkout ()
{
	__git_has_doubledash && return

	local dwim_opt="$(__git_checkout_default_dwim_mode)"

	case "$prev" in
	-b|-B|--orphan)
		# Complete local branches (and DWIM branch
		# remote branch names) for an option argument
		# specifying a new branch name. This is for
		# convenience, assuming new branches are
		# possibly based on pre-existing branch names.
		__git_complete_refs $dwim_opt --mode="heads"
		return
		;;
	*)
		;;
	esac

	case "$cur" in
	--conflict=*)
		__gitcomp "diff3 merge" "" "${cur##--conflict=}"
@@ -1346,14 +1567,21 @@ _git_checkout ()
		__gitcomp_builtin checkout
		;;
	*)
		# check if --track, --no-track, or --no-guess was specified
		# if so, disable DWIM mode
		local flags="--track --no-track --no-guess" track_opt="--track"
		if [ "$GIT_COMPLETION_CHECKOUT_NO_GUESS" = "1" ] ||
		   [ -n "$(__git_find_on_cmdline "$flags")" ]; then
			track_opt=''
		# At this point, we've already handled special completion for
		# the arguments to -b/-B, and --orphan. There are 3 main
		# things left we can possibly complete:
		# 1) a start-point for -b/-B, -d/--detach, or --orphan
		# 2) a remote head, for --track
		# 3) an arbitrary reference, possibly including DWIM names
		#

		if [ -n "$(__git_find_on_cmdline "-b -B -d --detach --orphan")" ]; then
			__git_complete_refs --mode="refs"
		elif [ -n "$(__git_find_on_cmdline "--track")" ]; then
			__git_complete_refs --mode="remote-heads"
		else
			__git_complete_refs $dwim_opt --mode="refs"
		fi
		__git_complete_refs $track_opt
		;;
	esac
}
@@ -1470,9 +1698,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
@@ -1488,8 +1723,13 @@ __git_diff_common_options="--stat --numstat --shortstat --summary
			--submodule --submodule= --ignore-submodules
			--indent-heuristic --no-indent-heuristic
			--textconv --no-textconv
			--patch --no-patch
"

__git_diff_difftool_options="--cached --staged --pickaxe-all --pickaxe-regex
			--base --ours --theirs --no-index --relative --merge-base
			$__git_diff_common_options"

_git_diff ()
{
	__git_has_doubledash && return
@@ -1503,11 +1743,16 @@ _git_diff ()
		__gitcomp "$__git_diff_submodule_formats" "" "${cur##--submodule=}"
		return
		;;
	--color-moved=*)
		__gitcomp "$__git_color_moved_opts" "" "${cur##--color-moved=}"
		return
		;;
	--color-moved-ws=*)
		__gitcomp "$__git_color_moved_ws_opts" "" "${cur##--color-moved-ws=}"
		return
		;;
	--*)
		__gitcomp "--cached --staged --pickaxe-all --pickaxe-regex
			--base --ours --theirs --no-index
			$__git_diff_common_options
			"
		__gitcomp "$__git_diff_difftool_options"
		return
		;;
	esac
@@ -1515,8 +1760,8 @@ _git_diff ()
}

__git_mergetools_common="diffuse diffmerge ecmerge emerge kdiff3 meld opendiff
			tkdiff vimdiff gvimdiff xxdiff araxis p4merge bc
			codecompare smerge
			tkdiff vimdiff nvimdiff gvimdiff xxdiff araxis p4merge
			bc codecompare smerge
"

_git_difftool ()
@@ -1529,11 +1774,7 @@ _git_difftool ()
		return
		;;
	--*)
		__gitcomp_builtin difftool "$__git_diff_common_options
					--base --cached --ours --theirs
					--pickaxe-all --pickaxe-regex
					--relative --staged
					"
		__gitcomp_builtin difftool "$__git_diff_difftool_options"
		return
		;;
	esac
@@ -1575,6 +1816,10 @@ _git_format_patch ()
			" "" "${cur##--thread=}"
		return
		;;
	--base=*|--interdiff=*|--range-diff=*)
		__git_complete_refs --cur="${cur#--*=}"
		return
		;;
	--*)
		__gitcomp_builtin format-patch "$__git_format_patch_extra_options"
		return
@@ -1595,7 +1840,7 @@ _git_fsck ()

_git_gitk ()
{
	_gitk
	__gitk_main
}

# Lists matching symbol names from a tag (as in ctags) file.
@@ -1749,7 +1994,7 @@ __git_log_shortlog_options="
	--all-match --invert-grep
"

__git_log_pretty_formats="oneline short medium full fuller email raw format: mboxrd"
__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 ()
@@ -1819,7 +2064,6 @@ _git_log ()
			--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
@@ -2043,6 +2287,10 @@ _git_rebase ()
		__gitcomp "$__git_whitespacelist" "" "${cur##--whitespace=}"
		return
		;;
	--onto=*)
		__git_complete_refs --cur="${cur##--onto=}"
		return
		;;
	--*)
		__gitcomp_builtin rebase "" \
			"$__git_rebase_interactive_inprogress_options"
@@ -2174,6 +2422,22 @@ _git_status ()

_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=}"
@@ -2182,29 +2446,26 @@ _git_switch ()
		__gitcomp_builtin switch
		;;
	*)
		# check if --track, --no-track, or --no-guess was specified
		# if so, disable DWIM mode
		local track_opt="--track" only_local_ref=n
		if [ "$GIT_COMPLETION_CHECKOUT_NO_GUESS" = "1" ] ||
		   [ -n "$(__git_find_on_cmdline "--track --no-track --no-guess")" ]; then
			track_opt=''
		fi
		# explicit --guess enables DWIM mode regardless of
		# $GIT_COMPLETION_CHECKOUT_NO_GUESS
		if [ -n "$(__git_find_on_cmdline "--guess")" ]; then
			track_opt='--track'
		fi
		if [ -z "$(__git_find_on_cmdline "-d --detach")" ]; then
			only_local_ref=y
		else
			# --guess --detach is invalid combination, no
			# dwim will be done when --detach is specified
			track_opt=
		# 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
		if [ $only_local_ref = y -a -z "$track_opt" ]; then
			__gitcomp_direct "$(__git_heads "" "$cur" " ")"

		# 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 $track_opt
			__git_complete_refs $dwim_opt --mode="heads"
		fi
		;;
	esac
@@ -2611,6 +2872,13 @@ _git_reset ()

_git_restore ()
{
	case "$prev" in
	-s)
		__git_complete_refs
		return
		;;
	esac

	case "$cur" in
	--conflict=*)
		__gitcomp "diff3 merge" "" "${cur##--conflict=}"
@@ -2691,9 +2959,17 @@ _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 --no-abbrev-commit
			--oneline --show-signature --patch
			--oneline --show-signature
			--expand-tabs --expand-tabs= --no-expand-tabs
			$__git_diff_common_options
			"
@@ -2714,12 +2990,33 @@ _git_show_branch ()
	__git_complete_revlist
}

_git_sparse_checkout ()
{
	local subcommands="list init set disable"
	local subcommand="$(__git_find_on_cmdline "$subcommands")"
	if [ -z "$subcommand" ]; then
		__gitcomp "$subcommands"
		return
	fi

	case "$subcommand,$cur" in
	init,--*)
		__gitcomp "--cone"
		;;
	set,--*)
		__gitcomp "--stdin"
		;;
	*)
		;;
	esac
}

_git_stash ()
{
	local save_opts='--all --keep-index --no-keep-index --quiet --patch --include-untracked'
	local subcommands='push list show apply clear drop pop create branch'
	local subcommand="$(__git_find_on_cmdline "$subcommands save")"
	if [ -n "$(__git_find_on_cmdline "-p")" ]; then
	if [ -z "$subcommand" -a -n "$(__git_find_on_cmdline "-p")" ]; then
		subcommand="push"
	fi
	if [ -z "$subcommand" ]; then
@@ -2755,7 +3052,10 @@ _git_stash ()
		list,--*)
			__gitcomp "--name-status --oneline --patch-with-stat"
			;;
		show,--*|branch,--*)
		show,--*)
			__gitcomp "$__git_diff_common_options"
			;;
		branch,--*)
			;;
		branch,*)
			if [ $cword -eq 3 ]; then
@@ -2779,7 +3079,7 @@ _git_submodule ()
{
	__git_has_doubledash && return

	local subcommands="add status init deinit update set-branch summary foreach sync absorbgitdirs"
	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
@@ -2843,6 +3143,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="
@@ -2964,33 +3265,83 @@ _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 move prune remove unlock"
	local subcommand="$(__git_find_on_cmdline "$subcommands")"
	if [ -z "$subcommand" ]; then
	local subcommand subcommand_idx

	subcommand="$(__git_find_on_cmdline --show-idx "$subcommands")"
	subcommand_idx="${subcommand% *}"
	subcommand="${subcommand#* }"

	case "$subcommand,$cur" in
	,*)
		__gitcomp "$subcommands"
	else
		case "$subcommand,$cur" in
		add,--*)
			__gitcomp_builtin worktree_add
			;;
		list,--*)
			__gitcomp_builtin worktree_list
			;;
		lock,--*)
			__gitcomp_builtin worktree_lock
			;;
		prune,--*)
			__gitcomp_builtin worktree_prune
			;;
		remove,--*)
			__gitcomp "--force"
		;;
	*,--*)
		__gitcomp_builtin worktree_$subcommand
		;;
	add,*)	# usage: git worktree add [<options>] <path> [<commit-ish>]
		# Here we are not completing an --option, it's either the
		# path or a ref.
		case "$prev" in
		-b|-B)	# Complete refs for branch to be created/reseted.
			__git_complete_refs
			;;
		*)
		-*)	# The previous word is an -o|--option without an
			# unstuck argument: have to complete the path for
			# the new worktree, so don't list anything, but let
			# Bash fall back to filename completion.
			;;
		*)	# The previous word is not an --option, so it must
			# be either the 'add' subcommand, the unstuck
			# argument of an option (e.g. branch for -b|-B), or
			# the path for the new worktree.
			if [ $cword -eq $((subcommand_idx+1)) ]; then
				# Right after the 'add' subcommand: have to
				# complete the path, so fall back to Bash
				# filename completion.
				:
			else
				case "${words[cword-2]}" in
				-b|-B)	# After '-b <branch>': have to
					# complete the path, so fall back
					# to Bash filename completion.
					;;
				*)	# After the path: have to complete
					# the ref to be checked out.
					__git_complete_refs
					;;
				esac
			fi
			;;
		esac
	fi
		;;
	lock,*|remove,*|unlock,*)
		__git_complete_worktree_paths
		;;
	move,*)
		if [ $cword -eq $((subcommand_idx+1)) ]; then
			# The first parameter must be an existing working
			# tree to be moved.
			__git_complete_worktree_paths
		else
			# The second parameter is the destination: it could
			# be any path, so don't list anything, but let Bash
			# fall back to filename completion.
			:
		fi
		;;
	esac
}

__git_complete_common () {
@@ -3018,15 +3369,19 @@ __git_support_parseopt_helper () {
	esac
}

__git_have_func () {
	declare -f -- "$1" >/dev/null 2>&1
}

__git_complete_command () {
	local command="$1"
	local completion_func="_git_${command//-/_}"
	if ! declare -f $completion_func >/dev/null 2>/dev/null &&
		declare -f _completion_loader >/dev/null 2>/dev/null
	if ! __git_have_func $completion_func &&
		__git_have_func _completion_loader
	then
		_completion_loader "git-$command"
	fi
	if declare -f $completion_func >/dev/null 2>/dev/null
	if __git_have_func $completion_func
	then
		$completion_func
		return 0
@@ -3062,7 +3417,7 @@ __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
@@ -3097,7 +3452,7 @@ __git_main ()
			"
			;;
		*)
			if test -n "$GIT_TESTING_PORCELAIN_COMMAND_LIST"
			if test -n "${GIT_TESTING_PORCELAIN_COMMAND_LIST-}"
			then
				__gitcomp "$GIT_TESTING_PORCELAIN_COMMAND_LIST"
			else
@@ -3141,88 +3496,8 @@ __gitk_main ()
	__git_complete_revlist
}

if [[ -n ${ZSH_VERSION-} ]] &&
   # Don't define these functions when sourced from 'git-completion.zsh',
   # it has its own implementations.
   [[ -z ${GIT_SOURCING_ZSH_COMPLETION-} ]]; then
	echo "WARNING: this script is deprecated, please see git-completion.zsh" 1>&2

	autoload -U +X compinit && compinit

	__gitcomp ()
	{
		emulate -L zsh

		local cur_="${3-$cur}"

		case "$cur_" in
		--*=)
			;;
		*)
			local c IFS=$' \t\n'
			local -a array
			for c in ${=1}; do
				c="$c${4-}"
				case $c in
				--*=*|*.) ;;
				*) c="$c " ;;
				esac
				array[${#array[@]}+1]="$c"
			done
			compset -P '*[=:]'
			compadd -Q -S '' -p "${2-}" -a -- array && _ret=0
			;;
		esac
	}

	__gitcomp_direct ()
	{
		emulate -L zsh

		local IFS=$'\n'
		compset -P '*[=:]'
		compadd -Q -- ${=1} && _ret=0
	}

	__gitcomp_nl ()
	{
		emulate -L zsh

		local IFS=$'\n'
		compset -P '*[=:]'
		compadd -Q -S "${4- }" -p "${2-}" -- ${=1} && _ret=0
	}

	__gitcomp_file_direct ()
	{
		emulate -L zsh

		local IFS=$'\n'
		compset -P '*[=:]'
		compadd -f -- ${=1} && _ret=0
	}

	__gitcomp_file ()
	{
		emulate -L zsh

		local IFS=$'\n'
		compset -P '*[=:]'
		compadd -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

@@ -3233,10 +3508,7 @@ __git_func_wrap ()
	$1
}

# Setup completion for certain functions defined above by setting common
# variables and workarounds.
# This is NOT a public function; use at your own risk.
__git_complete ()
___git_complete ()
{
	local wrapper="__git_wrap${2}"
	eval "$wrapper () { __git_func_wrap $2 ; }"
@@ -3244,25 +3516,33 @@ __git_complete ()
		|| complete -o default -o nospace -F $wrapper $1
}

# wrapper for backwards compatibility
_git ()
# Setup the completion for git commands
# 1: command or alias
# 2: function to call (e.g. `git`, `gitk`, `git_fetch`)
__git_complete ()
{
	__git_wrap__git_main
}
	local func

# wrapper for backwards compatibility
_gitk ()
{
	__git_wrap__gitk_main
	if __git_have_func $2; then
		func=$2
	elif __git_have_func __$2_main; then
		func=__$2_main
	elif __git_have_func _$2; then
		func=_$2
	else
		echo "ERROR: could not find function '$2'" 1>&2
		return 1
	fi
	___git_complete $1 $func
}

__git_complete git __git_main
__git_complete gitk __gitk_main
___git_complete git __git_main
___git_complete gitk __gitk_main

# The following are necessary only for Cygwin, and only are needed
# when the user has tab-completed the executable name and consequently
# included the '.exe' suffix.
#
if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then
__git_complete git.exe __git_main
if [ "$OSTYPE" = cygwin ]; then
	___git_complete git.exe __git_main
fi
diff --git a/zsh/_git b/zsh/_git
new file mode 100644
index 0000000..6c56296
--- /dev/null
+++ b/zsh/_git
@@ -0,0 +1,294 @@
#compdef git gitk

# zsh completion wrapper for git
#
# Copyright (c) 2012-2020 Felipe Contreras <felipe.contreras@gmail.com>
#
# 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.
#
# For example, create a directory '~/.zsh/', copy this file to '~/.zsh/_git',
# and then add the following to your ~/.zshrc file:
#
#  fpath=(~/.zsh $fpath)
#
# 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
#

zstyle -T ':completion:*:*:git:*' tag-order && \
	zstyle ':completion:*:*:git:*' tag-order 'common-commands'

zstyle -s ":completion:*:*:git:*" script script
if [ -z "$script" ]; then
	local -a locations
	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
		"$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

local old_complete="$functions[complete]"
functions[complete]=:
GIT_SOURCING_ZSH_COMPLETION=y . "$script"
functions[complete]="$old_complete"

__gitcomp ()
{
	emulate -L zsh

	local cur_="${3-$cur}"

	case "$cur_" in
	--*=)
		;;
	--no-*)
		local c IFS=$' \t\n'
		local -a array
		for c in ${=1}; do
			if [[ $c == "--" ]]; then
				continue
			fi
			c="$c${4-}"
			case $c in
			--*=|*.) ;;
			*) c="$c " ;;
			esac
			array+=("$c")
		done
		compset -P '*[=:]'
		compadd -Q -S '' -p "${2-}" -a -- array && _ret=0
		;;
	*)
		local c IFS=$' \t\n'
		local -a array
		for c in ${=1}; do
			if [[ $c == "--" ]]; then
				c="--no-...${4-}"
				array+=("$c ")
				break
			fi
			c="$c${4-}"
			case $c in
			--*=|*.) ;;
			*) c="$c " ;;
			esac
			array+=("$c")
		done
		compset -P '*[=:]'
		compadd -Q -S '' -p "${2-}" -a -- array && _ret=0
		;;
	esac
}

__gitcomp_direct ()
{
	emulate -L zsh

	compset -P '*[=:]'
	compadd -Q -S '' -- ${(f)1} && _ret=0
}

__gitcomp_nl ()
{
	emulate -L zsh

	compset -P '*[=:]'
	compadd -Q -S "${4- }" -p "${2-}" -- ${(f)1} && _ret=0
}

__gitcomp_file ()
{
	emulate -L zsh

	compset -P '*[=:]'
	compadd -f -p "${2-}" -- ${(f)1} && _ret=0
}

__gitcomp_direct_append ()
{
	__gitcomp_direct "$@"
}

__gitcomp_nl_append ()
{
	__gitcomp_nl "$@"
}

__gitcomp_file_direct ()
{
	__gitcomp_file "$1" ""
}

_git_zsh ()
{
	__gitcomp "v1.1"
}

__git_complete_command ()
{
	emulate -L zsh

	local command="$1"
	local completion_func="_git_${command//-/_}"
	if (( $+functions[$completion_func] )); then
		emulate ksh -c $completion_func
		return 0
	else
		return 1
	fi
}

__git_zsh_bash_func ()
{
	emulate -L ksh

	local command=$1

	__git_complete_command "$command" && return

	local expansion=$(__git_aliased_command "$command")
	if [ -n "$expansion" ]; then
		words[1]=$expansion
		__git_complete_command "$expansion"
	fi
}

__git_zsh_cmd_common ()
{
	local -a list
	list=(
	add:'add file contents to the index'
	bisect:'find by binary search the change that introduced a bug'
	branch:'list, create, or delete branches'
	checkout:'checkout a branch or paths to the working tree'
	clone:'clone a repository into a new directory'
	commit:'record changes to the repository'
	diff:'show changes between commits, commit and working tree, etc'
	fetch:'download objects and refs from another repository'
	grep:'print lines matching a pattern'
	init:'create an empty Git repository or reinitialize an existing one'
	log:'show commit logs'
	merge:'join two or more development histories together'
	mv:'move or rename a file, a directory, or a symlink'
	pull:'fetch from and merge with another repository or a local branch'
	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
}

__git_zsh_cmd_alias ()
{
	local -a list
	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 ()
{
	local -a list
	emulate ksh -c __git_compute_all_commands
	list=( ${=__git_all_commands} )
	_describe -t all-commands 'all commands' list && _ret=0
}

__git_zsh_main ()
{
	local curcontext="$curcontext" state state_descr line
	typeset -A opt_args
	local -a orig_words

	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]' \
		'(- :)--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]' \
		'--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' \
		'(-): :->command' \
		'(-)*:: :->arg' && return

	case $state in
	(command)
		_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
		;;
	(arg)
		local command="${words[1]}" __git_dir

		if (( $+opt_args[--bare] )); then
			__git_dir='.'
		else
			__git_dir=${opt_args[--git-dir]}
		fi

		(( $+opt_args[--help] )) && command='help'

		words=( ${orig_words[@]} )

		__git_zsh_bash_func $command
		;;
	esac
}

_git ()
{
	local _ret=1
	local cur cword prev

	cur=${words[CURRENT]}
	prev=${words[CURRENT-1]}
	let cword=CURRENT-1

	if (( $+functions[__${service}_zsh_main] )); then
		__${service}_zsh_main
	elif (( $+functions[__${service}_main] )); then
		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
	return _ret
}

_git
diff --git a/zsh/zshrc b/zsh/zshrc
index 1a1f77d..dbc0a70 100644
--- a/zsh/zshrc
+++ b/zsh/zshrc
@@ -1,5 +1,5 @@
# Functions Autoloading
fpath+=(~/.zsh $fpath)
fpath=(~/.zsh $fpath)

# Prompt
autoload -U promptinit; promptinit
@@ -70,6 +70,8 @@ setopt nolisttypes
setopt alwaystoend
# Try to correct the spelling of commands
setopt correct
# Make auto-completion work with aliases (like `git`)
setopt completealiases

# case-insensitive (uppercase from lowercase) completion
zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}'
--
2.30.0
Applied, thanks!