如果在.bash_profile中使用别名,如何启用git命令的自动完成?

时间:2017-12-28 18:24:42

标签: git

我在.bash_profile中有git别名,并希望在使用这些命令时启用自动完成(例如:自动完成分支名称)。如果我使用完整的git命令,这对我有用,但在使用别名时则不适用。

我听说如果你使用通过.gitconfig配置的别名可以自动完成,但是这些别名强制使用git,而我在.bash_profile中配置的别名将“git”替换为“g”,这是更多方便。

1 个答案:

答案 0 :(得分:4)

各种命令的bash完成通常在bash_completions包中提供,或者随各个命令提供。它们的安装位置在某种程度上取决于操作系统,在Ubuntu中它们被收集在各个地方,从/etc/bash_completion开始。此脚本源自全局bash.bashrc,并最终提取各种其他脚本,用于定义各种命令的完成。最终,他们都调用complete命令来定义何时调用完成。

git的完成(在Ubuntu中,位置可能会有所不同)安装到/usr/share/bash-completion/completions/git。此文件来自the git source repository,并定义了各个命令的完成函数,并最终定义了一个__git_complete函数,该函数接受命令和完成函数并安装该命令的完成:

__git_complete git __git_main

上面添加了git的完成次数来调用__git_main(也在该文件中定义)。由于所有的完成都是来源的,因此所使用的函数都可以在shell中使用,因此您可以使用相同的调用将git完成添加到别名中:

__git_complete g __git_main

如果您的别名包含某个git子命令,请使用该文件中定义的各种子命令完成之一。如果您的别名ga表示git add,则可以使用_git_add完成代码:

__git_complete ga _git_add

请注意,__git_complete函数(可能是该文件中的所有函数)都是私有的,并不打算直接使用。该功能标有相应的警告:

  

这不是公共职能;使用风险自负。

这基本上意味着,更新git和/或bash_completions可能会更改完成文件以及上述功能的命名方式以及工作可能会发生变化,这会破坏您的设置。因此,如果您准备经常重做该设置,则应仅使用上述内容。

另请注意,您可能必须明确从.bashrc.profile获取该git完成文件才能使这些功能可用:

source /usr/share/bash-completion/completions/git

特殊情况

一些git完成实际上检查当前命令行以决定接下来要完成什么。其中的例子是任何完成遥控器或refspecs的东西,即fetch,pull,remote和push。在这种情况下,使用相应的函数完成别名是不够的,因为别名将隐藏实际的git命令,因此完成将不知道如何继续。作为评论的一个例子:

$ alias gpr='git pull --rebase'
$ __git_complete gpr _git_pull
$ gpr o[TAB]
$ gpr origin m[TAB]
$ gpr origin m

在这种情况下,遥控器(在这种情况下为原点)仍然可以完成,但refspec无法完成。这是由于命令行上缺少“pull”命令。

此案例无法完全解决,但可以构建需要额外标签的变通方法。而不是直接挂钩到git完成,定义了一个包装器函数,它首先使用所需的命令行扩展别名,然后传递以进一步完成:

function _gpr {
    if [ $COMP_CWORD = 1 ]
    then
        COMPREPLY=('pull --rebase ')
        return 0
    fi

    __git_func_wrap __git_main
}

包装器函数检查当前单词是否是第一个单词,并且在这种情况下将COMPREPLY变量设置为所需的命令行扩展(有关详细信息,请参阅the documentation for programmable completion)。请注意最后需要的额外空间,因为该函数将使用nospace选项安装,以防止自动插入空格。

然后安装包装函数作为所需别名的完成:

complete -o bashdefault -o default -o nospace -F _gpr gpr

-F _gpr参数指定要调用的包装函数和最后一个参数(gpr)要完成的命令(有关其他选项,请参阅the full documentation of the complete builtin)。

随着包装器的完成,上面的会话变为:

$ gpr [TAB]
$ gpr pull --rebase o[TAB]
$ gpr pull --rebase origin m[TAB]
$ gpr pull --rebase origin master

在别名之后需要一个额外的选项卡,以首先扩展到之前完全隐藏在别名中的所需命令行。从那时起,完井工作按预期进行。

请注意,由于所需的命令行现在是实际命令行的一部分,因此别名应仅指向git而不是git pull --rebase。这也是包装函数始终将完成传递到__git_main而不是单个子命令完成的原因。别名定义只是:

alias gpr=git

组合shell和git别名

上述特殊情况的一个很好的替代方案是组合shell和git别名。为完成目的,git完成扩展git别名(即用git config alias.<name> '<command>'创建的别名)。为实际命令配置git别名:

$ git config --global alias.pr 'pull --rebase'

然后为git安装一个速记shell别名:

$ alias g=git

使用原始方法将其挂起__git_main

$ __git_complete g __git_main

允许此会话:

$ g pr o[TAB]
$ g pr origin m[TAB]
$ g pr origin master