git-completion.zsh
and git-completion.bash
are installed automatically when running brew install git
:
❯ ls -l /usr/local/share/zsh/site-functions/_git
lrwxr-xr-x 56 quanta 7 Jul 18:54 /usr/local/share/zsh/site-functions/_git -> ../../../Cellar/git/2.27.0/share/zsh/site-functions/_git
❯ ls -l /usr/local/share/zsh/site-functions/git-completion.bash
lrwxr-xr-x 71 quanta 7 Jul 18:54 /usr/local/share/zsh/site-functions/git-completion.bash -> ../../../Cellar/git/2.27.0/share/zsh/site-functions/git-completion.bash
/usr/local/share/zsh/site-functions
is included in fpath
:
❯ echo $fpath
/usr/local/share/zsh-completions
/usr/local/share/zsh/site-functions
/usr/share/zsh/site-functions
/usr/share/zsh/5.7.1/functions
For some reasons, sometimes when I type git reba
and press tab:
❯ git reba
__git_func_wrap:3: : not found
__git_func_wrap:3: : not found
❯ type __git_func_wrap
__git_func_wrap is a shell function from /usr/local/share/zsh/site-functions/git-completion.bash
https://github.com/git/git/blob/master/contrib/completion/git-completion.bash#L3517-L3522
❯ grep -A5 '^__git_func_wrap' /usr/local/share/zsh/site-functions/git-completion.bash
__git_func_wrap ()
{
local cur words cword prev
_get_comp_words_by_ref -n =: cur words cword prev
$1
}
What the default completion is:
❯ complete -p git
complete -o bashdefault -o default -o nospace -F __git_wrap_tig tig
complete _bash bash
Continue inspect:
❯ type __git_wrap_tig
__git_wrap_tig is a shell function from /usr/local/share/zsh/site-functions/tig-completion.bash
The thing is I cannot find this function in tig-completion.bash
tig: stable 2.5.1 (bottled), HEAD
Text interface for Git repositories
https://jonas.github.io/tig/
/usr/local/Cellar/tig/2.5.1 (15 files, 875.9KB) *
Poured from bottle on 2020-07-06 at 16:01:38
From: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/tig.rb
==> Dependencies
Required: readline ✔
==> Options
--HEAD
Install HEAD version
==> Caveats
A sample of the default configuration has been installed to:
/usr/local/opt/tig/share/tig/examples/tigrc
to override the system-wide default configuration, copy the sample to:
/usr/local/etc/tigrc
Bash completion has been installed to:
/usr/local/etc/bash_completion.d
zsh completions and functions have been installed to:
/usr/local/share/zsh/site-functions
Looks like there is some changed recently: https://github.com/jonas/tig/commit/26ab51d28133354bfaa94d064bff37d29b3c30e3
but where is __git_wrap_tig
function?
PS: As I said above, this problem is not happen every time. Sometimes, when I opened a new tab and check the default completion and it is just:
❯ complete -p git
complete _bash bash
and git
completion worked as expected.
Reply to @user1934428:
❯ grep '__git_complete ' /usr/local/share/zsh/site-functions/git-completion.bash
__git_complete ()
__git_complete git __git_main
__git_complete gitk __gitk_main
__git_complete git.exe __git_main
and one more invocation of __git_complete
is in tig-completion.bash:
# we use internal git-completion functions, so wrap _tig for all necessary
# variables (like cword and prev) to be defined
__git_complete tig _tig
fpath : The git-completion. zsh is a function file, not designed to be sourced like the bash script. This command appends the ~/. zsh directory onto the shell's function lookup list.
zsh-autocomplete adds real-time type-ahead autocompletion to Zsh. Find as you type, then press Tab to insert the top completion, Shift Tab to insert the bottom one, or ↓ / PgDn to select another completion.
for ZSH usersZsh can handle bash completions functions. The latest development version of zsh has a function bashcompinit, that when run will allow zsh to read bash completion specifications and functions.
This is a problem with the tig
completion definitions, and not with the git
completion definitions.
Activating completion on tig
breaks completion for git
.
tig
is activated after git
, then tig
completion works and git
completion is broken.tig
completion is activated before git
, then they are both broken.Install the old versions of the completion scripts.
Unlink _tig and tig-completion.bash in /usr/local/share/zsh/site-functions
and replace with these older versions. Rename tig-completion.zsh
as _tig
when downloading.
cd /usr/local/share/zsh/site-functions && \
rm -f _tig tig-completion.bash && \
wget -O _tig https://raw.githubusercontent.com/jonas/tig/91912eb97da4f6907015dab41ef9bba315730854/contrib/tig-completion.zsh && \
wget -O tig-completion.bash https://raw.githubusercontent.com/jonas/tig/c72aa4dab21077231a97dcca8e3821d7b35fe7db/contrib/tig-completion.bash
TODO: File issue with tig. This is a regression with the new completion script as implemented in jonas/tig#960
I start with git tab completion working, and then at some point the shell "goes bad." I actually have three states
complete
not defined.
% which complete
complete
% git <TAB>
add -- add file contents to the index
bisect -- find by binary search the change that introduced a bug
...
% which complete
complete () {
return 0
}
complete
function defined referencing bash
complete () {
emulate -L zsh
local args void cmd print remove
args=("$@")
zparseopts -D -a void o: A: G: W: C: F: P: S: X: a b c d e f g j k u v p=print r=remove
if [[ -n $print ]]
then
printf 'complete %2$s %1$s\n' "${(@kv)_comps[(R)_bash*]#* }"
elif [[ -n $remove ]]
then
for cmd
do
unset "_comps[$cmd]"
done
else
compdef _bash_complete\ ${(j. .)${(q)args[1,-1-$#]}} "$@"
fi
}
unsetting the complete function unset -f complete
does not magically fix it. I think this may leave me with no completion for git?
I jump in and out of virtual envs, and thought that was related, but a controlled example of jumping in and out and manually setting VIRTUAL_ENV and etc did not bleed over and affect the completion system.
distraction, not related
Digging further I found a lot of local variables set in the third case, "bad shell."
I removed each of these local variables without any positive effect:
% unset REPLY
% unset __git_repo_path
% unset __tig_commands
% unset __tig_options
% unset _ack_raw_types
% unset $_cmd_variant
% unset _cmd_variant
Progress! I can move from state 1 to state 2 by invoking completion on tig:
% git <TAB>
add -- add file contents to the index
bisect -- find by binary search the change that introduced a bug
...
% tig <TAB>
% git <TAB>
__git_func_wrap:3: : not found
related broken state by completing with tig first:
% tig <TAB>
__git_complete:5: command not found: complete
% which complete
complete () {
emulate -L zsh
local args void cmd print remove
args=("$@")
zparseopts -D -a void o: A: G: W: C: F: P: S: X: a b c d e f g j k u v p=print r=remove
if [[ -n $print ]]
then
printf 'complete %2$s %1$s\n' "${(@kv)_comps[(R)_bash*]#* }"
elif [[ -n $remove ]]
then
for cmd
do
unset "_comps[$cmd]"
done
else
compdef _bash_complete\ ${(j. .)${(q)args[1,-1-$#]}} "$@"
fi
}
% git <TAB>
__git_func_wrap:3: : not found
% echo $fpath
/usr/local/share/zsh/site-functions /usr/share/zsh/site-functions /usr/share/zsh/5.7.1/functions
% for f in $fpath; do ls $f/*tig*; done | cat
/usr/local/share/zsh/site-functions/_tig
/usr/local/share/zsh/site-functions/tig-completion.bash
zsh: no matches found: /usr/share/zsh/site-functions/*tig*
zsh: no matches found: /usr/share/zsh/5.7.1/functions/*tig*
% cd /usr/local/share/zsh/site-functions
% ls -l *tig*
_tig -> ../../../Cellar/tig/2.5.1/share/zsh/site-functions/_tig
tig-completion.bash -> ../../../Cellar/tig/2.5.1/share/zsh/site-functions/tig-completion.bash
% ls -l *git*
_git -> ../../../Cellar/git/2.28.0/share/zsh/site-functions/_git
git-completion.bash -> ../../../Cellar/git/2.28.0/share/zsh/site-functions/git-completion.bash
/usr/local/share/zsh/site-functions
_tig
#compdef tig
#
# zsh completion wrapper for tig
# ==============================
#
# You need to install this script to zsh fpath with tig-completion.bash.
#
# The recommended way to install this script is to copy this and tig-completion.bash
# to '~/.zsh/_tig' and '~/.zsh/tig-completion.bash' and
# then add following to your ~/.zshrc file:
#
# fpath=(~/.zsh $fpath)
_tig () {
local e
e=$(dirname ${funcsourcetrace[1]%:*})/git-completion.bash
if [ -f $e ]; then
GIT_SOURCING_ZSH_COMPLETION=y . $e
fi
e=$(dirname ${funcsourcetrace[1]%:*})/tig-completion.bash
if [ -f $e ]; then
. $e
fi
}
tig-completion.bash
#compdef git gitk
# zsh completion wrapper for git
#
# Copyright (c) 2012-2013 Felipe Contreras <[email protected]>
#
# You need git's bash completion script installed somewhere, by default it
# would be the location bash-completion uses.
#
# If your script is somewhere else, you can configure it on your ~/.zshrc:
#
# zstyle ':completion:*:*:git:*' script ~/.git-completion.zsh
#
# The recommended way to install this script is to make a copy of it in
# ~/.zsh/ directory as ~/.zsh/git-completion.zsh and then add the following
# to your ~/.zshrc file:
#
# fpath=(~/.zsh $fpath)
complete ()
{
# do nothing
return 0
}
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
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
)
for e in $locations; do
test -f $e && script="$e" && break
done
fi
GIT_SOURCING_ZSH_COMPLETION=y . "$script"
__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+=("$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_nl_append ()
{
emulate -L zsh
local IFS=$'\n'
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_zsh_bash_func ()
{
emulate -L ksh
local command=$1
local completion_func="_git_${command//-/_}"
declare -f $completion_func >/dev/null && $completion_func && 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
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.}%$'\n'*})
_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)
_alternative \
'alias-commands:alias:__git_zsh_cmd_alias' \
'common-commands:common:__git_zsh_cmd_common' \
'all-commands:all:__git_zsh_cmd_all' && _ret=0
;;
(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
else
emulate ksh -c __${service}_main
fi
let _ret && _default && _ret=0
return _ret
}
_git
The __git_complete
function contains this:
__git_complete ()
{
local wrapper="__git_wrap${2}"
eval "$wrapper () { __git_func_wrap $2 ; }"
}
The new code of tig calls it like this:
__git_complete tig _tig
This code is effectively creating a wrapper called __git_wrap_tig
.
__git_wrap_tig { __git_func_wrap _tig }
However, these functions are not meant to be used by the Zsh completion.
The Zsh completion (latest version here) is meant to be sourcing the bash completion by itself, and then calling __tig_main
directly, bypassing any wrappers.
The problem is that the main tig function must be called __tig_main
, not _tig
. I've sent a patch to fix this and other discrepancies with the official git completion.
If you install all the latest relevant files:
Into your fpath
(e.g. ~/.zsh/
), it should work correctly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With