7

git-completion.zshgit-completion.bash在运行时自动安装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包含在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

出于某些原因,有时当我键入git reba并按下时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
}

默认完成是:

❯ complete -p git
complete -o bashdefault -o default -o nospace -F __git_wrap_tig tig
complete _bash bash

继续检查:

❯ type __git_wrap_tig
__git_wrap_tig is a shell function from /usr/local/share/zsh/site-functions/tig-completion.bash

问题是我在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

看起来最近有一些变化:https ://github.com/jonas/tig/commit/26ab51d28133354bfaa94d064bff37d29b3c30e3

__git_wrap_tig功能在哪里?

PS:正如我上面所说,这个问题并非每次都会发生。有时,当我打开一个新选项卡并检查默认完成时,它只是:

❯ complete -p git
complete _bash bash

并按git预期完成工作。


回复@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

还有一个调用__git_complete是在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 
4

3 回答 3

8

TL;博士

这是tig完成定义的问题,而不是git完成定义的问题。

tig在中断完成时激活完成git

  • 如果tig在 之后激活git,则tig完成工作并且git完成被破坏。
  • 如果tig完成之前被激活git,那么它们都被破坏了。

减轻:

安装旧版本的完成脚本。

取消链接 _tig 和 tig-completion.bash/usr/local/share/zsh/site-functions并替换为这些旧版本。重命名tig-completion.zsh_tig下载时。

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:tig 的文件问题。这是在jonas/tig#960中实现的新完成脚本的回归

状态:

我从 git tab 完成工作开始,然后在某些时候外壳“变坏了”。我实际上有三个状态

  1. 初始状态。在职的。 complete没有定义的。
    % which complete
    
  2. 在第一个选项卡完成后仍在工作,它创建了一个定义 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
    }
    
  3. 不工作。 complete函数定义引用 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
        }
    

研究

完成()函数:

取消设置完整功能unset -f complete并不能神奇地修复它。我认为这可能会让我无法完成 git?

虚拟环境

我跳进跳出虚拟环境,并认为这是相关的,但是跳进和跳出并手动设置 VIRTUAL_ENV 等的受控示例并没有溢出并影响完成系统。

分心,不相关

局部变量

进一步挖掘我发现在第三种情况下设置了很多局部变量,“坏壳”。

我删除了这些局部变量中的每一个,但没有任何积极影响:

% unset REPLY
% unset __git_repo_path
% unset __tig_commands
% unset __tig_options
% unset _ack_raw_types
% unset $_cmd_variant
% unset _cmd_variant

泰格

进步!我可以通过在 tig 上调用完成从状态 1 移动到状态 2:

% 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

通过先完成 tig 来完成相关的破碎状态:

% 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

fpath 和 tig 完成

% 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*

为 git、tig 准备站点功能的源代码

  • 来自 tig 版本 2.5.1 的 tig 补全
  • 来自 git 版本 2.28.0 的 git 完成
% 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

Tig 完成/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 <felipe.contreras@gmail.com>
    #
    # 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
    
    
于 2020-10-02T00:46:49.250 回答
2

__git_complete函数包含以下内容:

__git_complete ()
{
    local wrapper="__git_wrap${2}"
    eval "$wrapper () { __git_func_wrap $2 ; }"
}

tig 的新代码这样称呼它:

__git_complete tig _tig 

此代码有效地创建了一个名为__git_wrap_tig.

__git_wrap_tig { __git_func_wrap _tig }

但是,这些函数并不打算由 Zsh 补全使用。

Zsh 补全(此处为最新版本)旨在自行获取 bash 补全,然后__tig_main直接调用,绕过任何包装器。

问题是必须调用主要的 tig 函数__tig_main,而不是_tig. 我已经发送了一个补丁来修复这个和其他与官方 git 完成的差异。

如果您安装所有最新的相关文件:

  1. git-completion.bash
  2. git-completion.zsh(作为_git)
  3. tig-completion.bash
  4. tig -completion.zsh(作为_tig)

进入您的fpath(例如~/.zsh/),它应该可以正常工作。

于 2020-11-03T21:31:33.027 回答
0

在您的评论中,您写道,您已经从https://github.com/git/git/blob/master/contrib/completion/git-completion.bash安装了完成系统,但是对于 zsh,正确的来源应该是https://github.com/git/git/blob/master/contrib/completion/git-completion.zsh _

根据安装说明:git-completion.zsh 必须来自 .zshrc。bash 版本必须可以从中访问。

于 2020-07-07T08:48:30.570 回答