内置完成
git clone的默认完成(如下所示)为--*
选项提供选项卡完成:
_git_clone ()
{
case "$cur" in
--*)
__gitcomp_builtin clone
return
;;
esac
}
bash完成1.x(旧bash)
(举个具体的例子,macos high-sierra+brew安装bash completion/git)
在bash completion 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
(具体例子:股票ubuntu仿生(18.04))
在bash completion 2.x中,模型被翻转为动态加载的配置。这意味着,当git
完成制表时,__load_completion
会触发,在安装路径上找到git完成并获取它
在.bashrc
/.bash_profile
中定义我自己的_git_clone
完成函数现在是无用的,因为它会被动态来源的完成文件所破坏。
我可以在以下目录中定义我自己的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
管理
bash完成的官方常见问题解答包含非常有趣的信息。
首先,如果您100%确定$BASH_COMPLETION_USER_DIR
和$XDG_DATA_HOME
环境变量为空,那么您在原始问题中指定的内容是添加自己的bash完成脚本的好地方:
~/.local/share/bash-completion/completions/git
需要注意的是,.bash
扩展没有必要。
事实上,bash完成脚本是通过/etc/profile.d/bash_completion.sh
文件加载的。
如果您在.bashrc
文件中执行某个操作,您会以某种方式破坏加载链中的某些内容。
尽管如此,如果您覆盖现有的完成函数,您仍然需要确保加载顺序是正确的。因此,必须加载第一个bash完成脚本,以确保一切顺利结束。您可以很容易地执行它,在~/.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完成是否已经加载,然后如果不是这样,则加载所有bash完成git定义。编辑:当bash完成第一次加载git部分时,ENDLESS_LOOP_SAFEGUARD
技巧可以避免无休止的循环。
如果需要,您可以获得用法:
complete --help
complete:complete[-abcdefgjksuv][-pr][-DE][-o选项][-A操作][-G globpat][-W单词表][-F函数][-C命令][-X filterpat][-P前缀][-S后缀][名称…]指定Readline如何完成参数。
对于每个NAME,指定参数的完成方式。如果没有选项提供,现有的竣工规范以这样的方式打印允许它们被重新用作输入。
选项:
-p以可重复使用的格式打印现有的完工规范-r删除每个NAME的填写说明,或者,如果没有提供了姓名,所有填写规范-D将完成和操作作为命令的默认值没有定义任何特定的完成-E将完成和操作应用于"空"命令--尝试在空白行上完成
当尝试完成时,操作将按上面列出了大写字母选项。-D选项采用优先于-E。
退出状态:除非提供了无效选项或出现错误,否则返回成功。
然后,也只有到那时,你才能定义你想要的任何东西,包括覆盖git克隆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_loader git
这样,你就永远不会丢失你的零钱,因为你让包文件原封不动;并且仍然可以使用自己的功能来增强任何现有的bash完成。
编辑:
关于你对_completion_loader
函数=>的恐惧,但在检查了源代码后,这个函数从2015-07-15 20:53:05
的commit 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
在.bashrc
/.bash_profile
中,在重新定义git clone
:的完成之前,可以强制加载git
的默认完成
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
注意:有时,您不能"配置";,但必须提出一个补丁
例如,Git 2.33(2021年第三季度)修复了git clone --rec*
的完成(如"--recurse-submodules
"或"--recursive
")
请参阅Philippe Blain(phil-blain
)提交的ca2d62b(2021年7月16日)
(由Junio C Hamano合并——gitster
-提交fa8b225,2021年7月28日)
parse-options
:默认情况下不完成选项别名签字人:Philippe Blain
由于'
OPT_ALIAS
'是在5c38742中创建的(parse-options
:不发射,2019-04-29,Git v2.22.0-rc1-merge)(解析选项:别名不发射"模糊选项",2019-04.29),'git clone
'(man)--git-completion-helper
,Bash完成脚本使用它来列出克隆接受的选项(通过'__gitcomp_builtin'),列出了"--recurse子模块"及其别名"--recurrent",但由于"--rec递归"设置了PARSE_OPT_HIDDEN
标志,所以以前的情况并非如此,并且实现git--git完成帮助程序的"parse-options.c
::show_gitcomp"跳过了具有此标志的选项。这意味着键入"
git clone
--recurs<TAB>
"将同时生成"--recurse-submodules
"one_answers"--recursive
",这并不理想,因为两者都做相同的事情,因此完成应该直接完成规范选项。在'
parse_options_step
'中调用'show_gitcomp
'时,'preprocess_options
'已在'parse_options
'中调用,因此任何别名现在都是原始选项的副本,并带有修改的帮助文本,指示它们是别名。有用的是,由于64cc539("
parse-options
:不要泄露别名帮助消息",2021-03-21,Git v2.32.0-rc0——在第7批中列出的merge),这些副本设置了PARSE_OPT_FROM_ALIAS
标志,因此请在"show_gitcomp
"早期检查该标志,不要打印它们,除非用户明确要求显示所有完成(通过设置"GIT_COMPLETION_SHOW_ALL
")
毕竟,如果我们想鼓励使用"--recurse-submodules
"而不是"--recursive
",我们最好建议使用前者。唯一的其他选项别名是"
log
"和朋友"--mailmap
",这是"--use-mailmap
"的别名,但这些命令的Bash完成助手不使用"__gitcomp_builtin
",因此不受此更改的影响。测试t9902 completion.sh.中的新行为。
作为副作用,这也测试了以前未测试的GIT_COMPLETION_SHOW_ALL,
的正确行为
请注意,由于'__gitcomp_builtin
'缓存了它显示的选项,因此我们需要重新获取完成脚本的源代码,以便为第二次测试清除该缓存。