覆盖git clone

时间:2018-08-22 17:46:16

标签: bash git bash-completion

内置完成

默认的completion for git clone(如下所示)为--*选项提供了制表符补全:

_git_clone ()
{
    case "$cur" in
    --*)
        __gitcomp_builtin clone
        return
        ;;
    esac
}

bash-completion 1.x(旧bash)

(对于具体实例,macos high sierra + brew安装了bash-completion / git)

在bash完成1.x的世界中,要覆盖此问题,我将(在.bashrc / .bash_profile中定义我自己的_git_clone完成函数:

# https://github.com/scop/bash-completion/blob/d2f14a7/bash_completion#L498
__ltrim_colon_completions() {
    if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then
        # Remove colon-word prefix from COMPREPLY items
        local colon_word=${1%"${1##*:}"}
        local i=${#COMPREPLY[*]}
        while [[ $((--i)) -ge 0 ]]; do
            COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"}
        done
    fi
}


_git_clone() {
    case "$cur" in
    --*)
        __gitcomp_builtin clone
        return
        ;;
    *)
        argc=0
        for word in "${words[@]}"; do
            case "$word" in
            git|clone|--*)
                continue
                ;;
            *)
                argc=$((argc + 1))
                ;;
            esac
        done

        if [ $argc -le 1 ]; then
            __gitcomp "https://github.com/git/git https://github.com/python/cpython"
            __ltrim_colon_completions "$cur"
        fi
        ;;
    esac
}

这很棒:

(我在这里键入的顺序是git clone h<tab><tab>g<tab>

$ git clone https://github.com/
//github.com/git/git          //github.com/python/cpython 
$ git clone https://github.com/git/git 

bash完成2.x

(例如:股票仿生仿生(18.04))

在bash-completion 2.x中,模型翻转为动态加载的配置。这意味着git制表符完成时,__load_completion会触发,在其完成安装的路径中找到git完成并获取源。

_git_clone / .bashrc中定义自己的.bash_profile完成函数现在已无用,因为它会被动态来源的完成文件所破坏。

我可以在this directory中定义自己的git补全:

local -a dirs=( ${BASH_COMPLETION_USER_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion}/completions )

(例如~/.local/share/bash-completion/completions/git.bash)。但这会关闭所有其他git完成!

如何在此模型下使自定义clone制表符补全工作(并且默认补全继续工作)?

不可接受的解决方案:

  • 修改系统打包文件:/usr/share/bash-completion/completions/git。该文件由apt管理。

3 个答案:

答案 0 :(得分:9)

bash完成的official FAQ包含非常有趣的信息。

首先,如果您100%确定$BASH_COMPLETION_USER_DIR$XDG_DATA_HOME环境变量为空,则在原始问题中指定的位置是添加自己的bash完成脚本的好地方:

~/.local/share/bash-completion/completions/git

要注意,.bash扩展名不是必需的。

事实是,由于/etc/profile.d/bash_completion.sh文件,bash补全脚本得以加载。

如果您在.bashrc文件中执行某些操作,则会以某种方式破坏加载链中的某些内容。

但是,如果您覆盖现有的完成功能,则仍然需要确保加载顺序正确。 因此必须加载第一个bash-completion脚本以确保一切成功结束。 您可以轻松地执行此操作,在~/.local/share/bash-completion/completions/git文件的开头添加以下初始说明:

# Ensures git bash-completion is loaded before overriding any function (be careful to avoid endless loop).
! complete -p git &> /dev/null && [ ${ENDLESS_LOOP_SAFEGUARD:-0} -eq 0 ] && ENDLESS_LOOP_SAFEGUARD=1 BASH_COMPLETION_USER_DIR=/dev/null  _completion_loader git

首先,它检查git bash-completion是否已经加载,如果不是这种情况,则所有bash-completion git定义都已加载。 编辑:ENDLESS_LOOP_SAFEGUARD技巧可以避免bash完成第一次加载git part时的无限循环。

如果需要,您可以获取用法:

complete --help
  

完成:完成[-abcdefgjksuv] [-pr] [-DE] [-o   选项] [-A操作] [-G globpat] [-W单词表] [-F功能] [-C   命令] [-X filterpat] [-P前缀] [-S后缀] [名称...]      指定Readline如何完成参数。

     

对于每个NAME,请指定参数的填写方式。如果没有选择       提供,现有的完成规格以以下方式打印:       允许将它们重新用作输入。

     

选项:

     

-p以可重用格式打印现有完成规范      -r删除每个NAME的完成规范,或者,如果没有,             提供名称,所有完成规格      -D将完成和动作作为命令的默认值             没有定义任何特定的完成      -E将补全和操作应用于“空”命令-             在空白行尝试完成

     

尝试完成时,将按照以下顺序应用操作:       上面列出了大写字母选项。 -D选项需要       优先于-E。

     

退出状态:       除非提供了无效的选项或发生错误,否则返回成功。

然后,只有那时,您才能定义所需的内容,包括覆盖git clone bash完成的旧方法:

# Ensures git bash-completion is loaded before overriding any function (be careful to avoid endless loop).
! complete -p git &> /dev/null && [ ${ENDLESS_LOOP_SAFEGUARD:-0} -eq 0 ] && ENDLESS_LOOP_SAFEGUARD=1 BASH_COMPLETION_USER_DIR=/dev/null  _completion_loader git

# https://github.com/scop/bash-completion/blob/d2f14a7/bash_completion#L498
__ltrim_colon_completions() {
    if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then
        # Remove colon-word prefix from COMPREPLY items
        local colon_word=${1%"${1##*:}"}
        local i=${#COMPREPLY[*]}
        while [[ $((--i)) -ge 0 ]]; do
            COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"}
        done
    fi
}


_git_clone() {
    case "$cur" in
    --*)
        __gitcomp_builtin clone
        return
        ;;
    *)
        argc=0
        for word in "${words[@]}"; do
            case "$word" in
            git|clone|--*)
                continue
                ;;
            *)
                argc=$((argc + 1))
                ;;
            esac
        done

        if [ $argc -le 1 ]; then
            __gitcomp "https://github.com/git/git https://github.com/python/cpython"
            __ltrim_colon_completions "$cur"
        fi
        ;;
    esac
}

每次执行更改并要检查结果时,您只需要为git请求bash-completion重新加载即可:

_completion_loader git

这样,您将永远不会丢失所做的更改,因为您无需更改软件包文件。并且仍然可以使用您自己的功能来增强任何现有的bash补全功能。

编辑:

关于您对_completion_loader函数=>的担心,但是在检查了源代码之后,此函数自2015-07-15 20:53:05的提交cad3abfc7开始存在,因此我认为应该将其保留在后面,兼容,但没有保证是正确的。我将编辑答案以提出一些替代方案

作为替代,这应该是获取自己的git完成定义的另一种方法(放在您自己的脚本的开头):

# Ensures git bash-completion is loaded before overriding any function
# Be careful to avoid endless loop with dedicated $ENDLESS_LOOP_SAFEGUARD environment variable.
if ! complete -p git &> /dev/null && [ ${ENDLESS_LOOP_SAFEGUARD:-0} -eq 0 ]; then
    # Trick: define $BASH_COMPLETION_USER_DIR environment variable here to ensure bash-completion rule are loaded once.
    export BASH_COMPLETION_USER_DIR=/usr/share
    complete -D git

    unset BASH_COMPLETION_USER_DIR
    ENDLESS_LOOP_SAFEGUARD=1 complete -D git
fi

答案 1 :(得分:0)

注意:有时,您不能“配置”,但必须提出补丁。
例如,Git 2.33(2021 年第 3 季度)修复了 git clone --rec* 的完成(如“--recurse-submodules”或“--recursive”)

请参阅 commit ca2d62bPhilippe Blain (phil-blain)(2021 年 7 月 16 日)。
(2021 年 7 月 28 日于 Junio C Hamano -- gitster --commit fa8b225 合并)

<块引用>

parse-options:默认不填写选项别名

签字人:Philippe Blain

<块引用>

由于 'OPT_ALIAS' 是在 5c38742 中创建的(parse-options:不发出,2019-04-29,Git v2.22.0-rc1 -- merge ) (parse-options: 不要为别名发出“歧义选项”,2019-04-29), 'git clone'(man) --git-completion-helper ,Bash 完成脚本使用它来列出克隆接受的选项(通过 '__gitcomp_builtin'),列出了 '--recurse-submodules' 和它的别名 '--recursive',这不是之前的情况,因为 '-- recursive' 设置了 PARSE_OPT_HIDDEN 标志,带有这个标志的选项被 'parse-options.c::show_gitcomp' 跳过,它实现了 git --git-completion-helper。

这意味着输入 'git clone --recurs<TAB>' 将同时产生 '--recurse-submodules' 和 '--recursive',这并不理想,因为两者都做同样的事情,并且所以补全应该直接补全规范选项。

在“show_gitcomp”中调用“parse_options_step”时,“preprocess_options”已经在“parse_options”中调用,因此所有别名现在都是副本带有修改过的帮助文本的原始选项,表明它们是别名。

很有帮助,因为 64cc539 ("parse-options: don't leak alias help messages", 2021-03-21, Git v2.32.0-rc0 -- merge 列在 { {3}}) 这些副本设置了 PARSE_OPT_FROM_ALIAS 标志,因此请尽早在“show_gitcomp”中检查该标志并且不要打印它们,除非用户明确要求all显示完成(通过设置“GIT_COMPLETION_SHOW_ALL”)。
毕竟,如果我们想鼓励使用 '--recurse-submodules' 而不是 '--recursive',我们最好只建议使用前者。

唯一的其他选项别名是“log”和朋友“--mailmap”,它是“--use-mailmap”的别名,但这些命令的 Bash 补全助手没有使用“__gitcomp_builtin”,因此不受此更改的影响。

在 t9902-completion.sh 中测试新行为。
作为副作用,这也测试了之前未测试过的 GIT_COMPLETION_SHOW_ALL, 的正确行为。
请注意,由于“__gitcomp_builtin”缓存了它显示的选项,因此我们需要重新获取完成脚本的源代码以清除该缓存以进行第二次测试。

答案 2 :(得分:-1)

在您的.bashrc / .bash_profile中,您可以在重新定义git的完成之前,强制加载git clone的默认完成:

if ! complete -p git &> /dev/null
then
    # Instead of hardcoding the name of the dynamic completion loader
    # function, you can obtain it by parsing the output of 'complete -p -D'
    _completion_loader git
fi

_git_clone() {
    COMPREPLY=("My own completion for 'git clone'")
}

编辑

上述方法的延迟加载版本(不会急于加载git的默认bash补全):

if ! complete -p git &> /dev/null
then
    _my_git_clone()
    {
        COMPREPLY=("My own completion for 'git clone'")
    }

    # A placeholder for git completion that will load the real
    # completion script on first use     
    _my_git_comp_stub()
    {
        # Remove the old completion handler (which should be this very function)
        complete -r git

        # and load the git bash completion
        _completion_loader git

        # Rebind _git_clone to our own version
        eval 'function _git_clone() { _my_git_clone "$@"; }'

        # Tell the completion machinery to retry the completion attempt
        # using the updated completion handler
        return 124
    }

    # Install a lazy loading handler for git completion    
    complete -o bashdefault -o default -o nospace -F _my_git_comp_stub git
fi
相关问题