为 git 克隆覆盖 bash 完成
Override bash completion for git clone
内置补全
默认 completion for git clone(转载如下)为 --*
选项提供制表符补全:
_git_clone ()
{
case "$cur" in
--*)
__gitcomp_builtin clone
return
;;
esac
}
bash-完成1.x(旧bash)
(具体实例,macos high sierra + brew 安装bash-完成/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 [[ "" == *:* && "$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
(具体实例:stock ubuntu bionic (18.04))
在 bash-完成 2.x 中,模型被翻转为 动态 加载配置。这意味着当 git
完成制表符时,__load_completion
触发,在安装路径找到 git 完成并获取它。
在 .bashrc
/ .bash_profile
中定义我自己的 _git_clone
完成函数现在没有用了,因为它被动态来源的完成文件破坏了。
我可以在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
. 管理
在您的 .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
bash-完成的official FAQ包含了非常有趣的信息。
首先,如果您 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-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 部分时避免无限循环。
如有需要,可获取用法:
complete --help
complete: complete [-abcdefgjksuv] [-pr] [-DE] [-o
option] [-A action] [-G globpat] [-W wordlist] [-F function] [-C
command] [-X filterpat] [-P prefix] [-S suffix] [name ...]
Specify how arguments are to be completed by Readline.
For each NAME, specify how arguments are to be completed. If no options
are supplied, existing completion specifications are printed in a way that
allows them to be reused as input.
Options:
-p print existing completion specifications in a reusable format
-r remove a completion specification for each NAME, or, if no
NAMEs are supplied, all completion specifications
-D apply the completions and actions as the default for commands
without any specific completion defined
-E apply the completions and actions to "empty" commands --
completion attempted on a blank line
When completion is attempted, the actions are applied in the order the
uppercase-letter options are listed above. The -D option takes
precedence over -E.
Exit Status:
Returns success unless an invalid option is supplied or an error occurs.
然后,也只有那时,您可以定义任何您想要的,包括您覆盖 git clone bash completion:
的旧方法
# 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 [[ "" == *:* && "$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
}
每次执行更改并想查看结果时,只需请求bash-完成重载git:
_completion_loader git
这样一来,您就永远不会丢失零钱,因为您让包文件保持原样;并且仍然可以使用您自己的功能增强任何现有的 bash-completion。
编辑:
关于你对 _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
注意:有时,您不能“配置”,而必须提出补丁。
例如,Git 2.33(2021 年第 3 季度)修复了 git clone --rec*
的完成(如“--recurse-submodules
”或“--recursive
”)
参见 commit ca2d62b (16 Jul 2021) by Philippe Blain (phil-blain
)。
(由 Junio C Hamano -- gitster
-- in commit fa8b225 合并,2021 年 7 月 28 日)
parse-options
: don't complete option aliases by default
Signed-off-by: Philippe Blain
Since 'OPT_ALIAS
' was created in 5c38742 (parse-options
: don't emit , 2019-04-29, Git v2.22.0-rc1 -- merge) (parse-options: don't emit "ambiguous option" for aliases, 2019-04-29), 'git clone
'(man) --git-completion-helper
, which is used by the Bash completion script to list options accepted by clone (via '__gitcomp_builtin'), lists both '--recurse-submodules' and its alias '--recursive', which was not the case before since '--recursive' had the PARSE_OPT_HIDDEN
flag set, and options with this flag are skipped by 'parse-options.c
::show_gitcomp', which implements git --git-completion-helper.
This means that typing 'git clone
--recurs<TAB>
' will yield both '--recurse-submodules
' and '--recursive
', which is not ideal since both do the same thing, and so the completion should directly complete the canonical option.
At the point where 'show_gitcomp
' is called in 'parse_options_step
', 'preprocess_options
' was already called in 'parse_options
', so any aliases are now copies of the original options with a modified help text indicating they are aliases.
Helpfully, since 64cc539 ("parse-options
: don't leak alias help messages", 2021-03-21, Git v2.32.0-rc0 -- merge listed in batch #7) these copies have the PARSE_OPT_FROM_ALIAS
flag set, so check that flag early in 'show_gitcomp
' and do not print them, unless the user explicitly requested that all completion be shown (by setting 'GIT_COMPLETION_SHOW_ALL
').
After all, if we want to encourage the use of '--recurse-submodules
' over '--recursive
', we'd better just suggest the former.
The only other options alias is 'log
' and friends' '--mailmap
', which is an alias for '--use-mailmap
', but the Bash completion helpers for these commands do not use '__gitcomp_builtin
', and thus are unaffected by this change.
Test the new behavior in t9902-completion.sh.
As a side effect, this also tests the correct behavior of GIT_COMPLETION_SHOW_ALL,
which was not tested before.
Note that since '__gitcomp_builtin
' caches the options it shows, we need to re-source the completion script to clear that cache for the second test.
内置补全
默认 completion for git clone(转载如下)为 --*
选项提供制表符补全:
_git_clone ()
{
case "$cur" in
--*)
__gitcomp_builtin clone
return
;;
esac
}
bash-完成1.x(旧bash)
(具体实例,macos high sierra + brew 安装bash-完成/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 [[ "" == *:* && "$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
(具体实例:stock ubuntu bionic (18.04))
在 bash-完成 2.x 中,模型被翻转为 动态 加载配置。这意味着当 git
完成制表符时,__load_completion
触发,在安装路径找到 git 完成并获取它。
在 .bashrc
/ .bash_profile
中定义我自己的 _git_clone
完成函数现在没有用了,因为它被动态来源的完成文件破坏了。
我可以在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
. 管理
在您的 .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
bash-完成的official FAQ包含了非常有趣的信息。
首先,如果您 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-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 部分时避免无限循环。
如有需要,可获取用法:
complete --help
complete: complete [-abcdefgjksuv] [-pr] [-DE] [-o option] [-A action] [-G globpat] [-W wordlist] [-F function] [-C command] [-X filterpat] [-P prefix] [-S suffix] [name ...] Specify how arguments are to be completed by Readline.
For each NAME, specify how arguments are to be completed. If no options are supplied, existing completion specifications are printed in a way that allows them to be reused as input.
Options:
-p print existing completion specifications in a reusable format -r remove a completion specification for each NAME, or, if no NAMEs are supplied, all completion specifications -D apply the completions and actions as the default for commands without any specific completion defined -E apply the completions and actions to "empty" commands -- completion attempted on a blank line
When completion is attempted, the actions are applied in the order the uppercase-letter options are listed above. The -D option takes precedence over -E.
Exit Status: Returns success unless an invalid option is supplied or an error occurs.
然后,也只有那时,您可以定义任何您想要的,包括您覆盖 git clone bash completion:
的旧方法# 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 [[ "" == *:* && "$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
}
每次执行更改并想查看结果时,只需请求bash-完成重载git:
_completion_loader git
这样一来,您就永远不会丢失零钱,因为您让包文件保持原样;并且仍然可以使用您自己的功能增强任何现有的 bash-completion。
编辑:
关于你对 _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
注意:有时,您不能“配置”,而必须提出补丁。
例如,Git 2.33(2021 年第 3 季度)修复了 git clone --rec*
的完成(如“--recurse-submodules
”或“--recursive
”)
参见 commit ca2d62b (16 Jul 2021) by Philippe Blain (phil-blain
)。
(由 Junio C Hamano -- gitster
-- in commit fa8b225 合并,2021 年 7 月 28 日)
parse-options
: don't complete option aliases by defaultSigned-off-by: Philippe Blain
Since '
OPT_ALIAS
' was created in 5c38742 (parse-options
: don't emit , 2019-04-29, Git v2.22.0-rc1 -- merge) (parse-options: don't emit "ambiguous option" for aliases, 2019-04-29), 'git clone
'(man)--git-completion-helper
, which is used by the Bash completion script to list options accepted by clone (via '__gitcomp_builtin'), lists both '--recurse-submodules' and its alias '--recursive', which was not the case before since '--recursive' had thePARSE_OPT_HIDDEN
flag set, and options with this flag are skipped by 'parse-options.c
::show_gitcomp', which implements git --git-completion-helper.This means that typing '
git clone
--recurs<TAB>
' will yield both '--recurse-submodules
' and '--recursive
', which is not ideal since both do the same thing, and so the completion should directly complete the canonical option.At the point where '
show_gitcomp
' is called in 'parse_options_step
', 'preprocess_options
' was already called in 'parse_options
', so any aliases are now copies of the original options with a modified help text indicating they are aliases.Helpfully, since 64cc539 ("
parse-options
: don't leak alias help messages", 2021-03-21, Git v2.32.0-rc0 -- merge listed in batch #7) these copies have thePARSE_OPT_FROM_ALIAS
flag set, so check that flag early in 'show_gitcomp
' and do not print them, unless the user explicitly requested that all completion be shown (by setting 'GIT_COMPLETION_SHOW_ALL
').
After all, if we want to encourage the use of '--recurse-submodules
' over '--recursive
', we'd better just suggest the former.The only other options alias is '
log
' and friends' '--mailmap
', which is an alias for '--use-mailmap
', but the Bash completion helpers for these commands do not use '__gitcomp_builtin
', and thus are unaffected by this change.Test the new behavior in t9902-completion.sh.
As a side effect, this also tests the correct behavior ofGIT_COMPLETION_SHOW_ALL,
which was not tested before.
Note that since '__gitcomp_builtin
' caches the options it shows, we need to re-source the completion script to clear that cache for the second test.