# Completion for the file argument for git diff.
# It completes only files actually changed. This might be useful
# as completion for other commands as well.
#
# The idea comes from the bash completion for Mercurial (hg),
# which does something similar (but more simple, only difference of
# working directory to HEAD and/or index, if I understand right).
# It (the idea) was brought to us by the question
# http://stackoverflow.com/q/6034472/600500
# from "olt".
__git_complete_changed_files()
{
#
# We use "git diff --name-only --relative" to generate the list,
# but this needs the same --cached and <commit> arguments as the
# command line being constructed.
#
# first grab arguments like --cached and any commit arguments.
local -a args=()
local finish=false
for (( i=1 ; i < cword ; i++)) do
local current_arg=${words[$i]}
# echo checking $current_arg >&2
case $current_arg in
--cached)
args+=( $current_arg )
;;
--)
# finish parsing arguments, the rest are file names
break
;;
-*)
# other options are ignored
;;
*)
if git cat-file -e $current_arg 2> /dev/null
then
case $( git cat-file -t $current_arg ) in
commit|tag)
# commits and tags are added to the command line.
args+=( $current_arg )
# echo adding $current_arg >&2
;;
*)
esac
fi
;;
esac
done
# now we can call `git diff`
COMPREPLY=( $( compgen \
-W "$( git diff --name-only --relative "${args[@]}" -- )" -- $cur ) )
}
_git_diff ()
{
if __git_has_doubledash
then
# complete for the file part: only changed files
__git_complete_changed_files
else
case "$cur" in
--*)
__gitcomp "--cached --staged --pickaxe-all --pickaxe-regex
--base --ours --theirs --no-index
$__git_diff_common_options
"
return
;;
esac
__git_complete_revlist_file
fi
}
**更新:**看起来这个补丁在这个表单中是不需要的,因为当前完成文件的方式对于想要检查某个子目录中是否有更改的人来说更有用(例如,当diff输出可能为空时完成)。如果链接到某个配置变量,它可能会被接受(默认为当前行为)。另外,缩进应该适应标准(参见Junio C Hamano的答案)。
(Also,如果有帮助的话,尽管那个提交在2.25系列中被合并,这个bug在2.25周期中没有被报告,甚至在2.26周期的大部分时间里也没有--它在2.26发布的前一天被报告。 因此,这一变化的影响至少在某种程度上是小的。) 与其依赖ls-files的bug来报告错误的内容,不如改变git-completion使用的ls文件的调用,使其获取更深一层的路径。 为此,请将"$DIR/*"(匹配$DIR/加上0个或更多字符)更改为"$DIR/?*"(匹配$DIR/加上1个或更多字符)。 请注意,在尝试完成文件名时不应添加"?"字符(例如,仅在目前指定的路径是目录的情况下才添加"git ls-files -o --directory merge.c?*"' would not correctly return "[ merge.c ](https://github.com/git/git/blob/c0af173a136785b3cfad4bd414b2fb10a130760a/merge.c)" when such a file exists), so we have to make sure to add the '?"字符)。 警告:Git 2.29(Q4 2020)修复了在2.27周期中引入的回归。 参见Martin Ågren ( none )的commit cada730(2020年7月20日)。 (由Junio C Hamano -- gitster --合并至commit 82fafc7,2020年7月30日)
4条答案
按热度按时间wa7juj8i1#
那么,让我们看看Mercurial bash完成脚本是如何完成的。
这是最重要的部分:
它在这里被称为:
因此,它只是一个
hg status -nmar
的调用,并使用输出作为要完成的文件列表。我认为将类似的东西修补到git completion script中不会太难-我们必须在这里修改
__git_diff
,使其不使用纯文件名+分支完成,而是调用git status
。命令
(for
git diff --cached
)和(for
git diff
)似乎输出了正确的内容(如果没有重命名)。但是,当与HEAD以外的任何东西进行比较时,它们都没有用。
更通用的方法是使用
其中
commit1
和commit2
(可能还有--cached
)来自已经给出的diff命令行。我在bash中实现了上面概述的想法,并打了补丁到
git-completion.bash
中。如果您不想更改您的git-completion.bash
,请将这两个函数添加到某个bash文件中,并在原始git-completion.bash
的源代码后面添加它。我把这个作为补丁提交到git邮件列表,让我们看看结果如何(我会在收到反馈后更新这个答案)。
**更新:**看起来这个补丁在这个表单中是不需要的,因为当前完成文件的方式对于想要检查某个子目录中是否有更改的人来说更有用(例如,当diff输出可能为空时完成)。如果链接到某个配置变量,它可能会被接受(默认为当前行为)。另外,缩进应该适应标准(参见Junio C Hamano的答案)。
我可能会采取另一个去,但不能保证这在不久的将来。如果别人想这样做,随时采取我的代码,改变它,并再次提交。
ztigrdn82#
这样就解决了
git diff <tab>
问题,将以下内容放入**.bashrc**中:然后执行
gid <tab>
而不是git diff <tab>
。它可能会简化,但作为快速修复似乎效果很好。gc0ot86w3#
这不是你想要的答案,但我想让你知道,很快fish(友好的交互式环境)就会给予你提供开箱即用的git文件名补全支持,它现在是master版本,2.3.0即将发布。
https://github.com/fish-shell/fish-shell/issues/901
https://github.com/fish-shell/fish-shell/pull/2364
https://github.com/fish-shell/fish-shell/commit/c5c59d4acb00674bc37198468b5978f69484c628
如果您有这样的状态:
你也可以只输入
README
并点击tab,如果它是唯一匹配的,它会为你插入它。e0bqpujr4#
自2011年起,如OP注解所示,Git从~1.8.2起支持完整的文件名补全。
但是在Git 2.18(Q2 2018)中,给出路径列表的shell补全(在
contrib/
中)得到了一些优化。参见Clemens Buchacher (
drizzd
)(2018年4月4日)。(由Junio C Hamano --
gitster
--合并至commit 3a940e9,2018年4月25日)completion
:改进ls-files
滤波器性能从
ls-files
的输出中,我们删除了最左边的路径部分,然后我们消除了重复的部分,我们在while
循环中完成这一步,当迭代次数很大时(例如linux.git
中有60000个文件),这是一个性能瓶颈。使用剪切命令替换循环可显著提高性能:
测量是使用Windows版Git使用的Msys2 bash完成的。
当过滤
ls-files
输出时,我们注意不要触及绝对路径。这是多余的,因为ls-files
永远不会输出绝对路径。删除不必要的操作。该问题最初报告为Git for Windows issue 1533。
目录遍历代码有冗余的递归调用,这使得它的性能特征与树的深度成指数关系,这在Git 2.27(Q2 2020)中得到了纠正。
请参见第一版第五版、第一版第六版、第一版第七版、第一版第八版、第一版第九版、第一版第十版、第一版第十一版、第一版第十二版、第一版第十三版、第一版第十四版、第一版第十五版(2020年4月1日)。
参见Derrick Stolee (
derrickstolee
)的commit 0bbd0e8(2020年4月1日)。(由Junio C Hamano --
gitster
--合并至commit 6eacc39,2020年4月29日)completion
:修复未跟踪目录下路径上的"git add
"签署人:伊莱贾·纽伦
正如git邮件列表中所报告的,自git-2. 25以来,
已按Tab键完成到
其原因是commit b9670c1f5e("
dir
:修复对公共前缀目录的检查",2019年12月19日,Git v2.25.0-rc0--merge),(or等价物
git -C untracked-dir ls-files -o --directory
)开始报告而不是列出该目录下的路径。
也许还值得注意的是,真正的命令是
其等效于:
其行为与此问题的目的相同("
*
"可以匹配空字符串),但与建议的修复相关。一开始,基于这份报告,我决定试着把这看作是一种倒退,并试图找到一种方法来恢复旧的行为,而不破坏其他东西,或者至少尽可能少地破坏。
然而,最后,我找不到一种方法来做这件事,而不仅仅是引起比它解决的更多的问题。
旧的行为是一个错误:
git clean -f .git
来清除任何内容,但它会使用git clean -f .git/
来清除该目录下的所有内容。尽管使用的命令不同,但这是相关的,因为与修复clean完全相同的更改更改了ls文件的行为。
git ls-files -o --directory $SUBDIR
中是否有尾部斜杠来报告不同的结果。--directory
时不递归到与路径规范匹配的目录。dir
:修复了对通用前缀目录的检查,2019年12月19日,Git v2.25.0-rc0)未忽略此问题;它明确指出,该命令的行为正在改变,以使其与文档保持一致。(Also,如果有帮助的话,尽管那个提交在2.25系列中被合并,这个bug在2.25周期中没有被报告,甚至在2.26周期的大部分时间里也没有--它在2.26发布的前一天被报告。
因此,这一变化的影响至少在某种程度上是小的。)
与其依赖
ls-files
的bug来报告错误的内容,不如改变git-completion使用的ls文件的调用,使其获取更深一层的路径。为此,请将"
$DIR/*
"(匹配$DIR/
加上0个或更多字符)更改为"$DIR/?*
"(匹配$DIR/
加上1个或更多字符)。请注意,在尝试完成文件名时不应添加"
?
"字符(例如,仅在目前指定的路径是目录的情况下才添加"git ls-files -o --directory merge.c?*"' would not correctly return "[
merge.c](https://github.com/git/git/blob/c0af173a136785b3cfad4bd414b2fb10a130760a/merge.c)" when such a file exists), so we have to make sure to add the '
?"字符)。警告:Git 2.29(Q4 2020)修复了在2.27周期中引入的回归。
参见Martin Ågren (
none
)的commit cada730(2020年7月20日)。(由Junio C Hamano --
gitster
--合并至commit 82fafc7,2020年7月30日)dir
:在返回path_excluded
之前检查路径规范报告人:安德烈亚斯·施瓦布
审核人:伊莱贾·纽伦
签署人:马丁·奥格伦
在95c11ecc73("修复容易出错的
fill_directory()
API;make it only return matches ",2020 - 04 - 01,Git v2.27.0-rc0--merge listed in batch #5),我们教fill_directory()
,或者更具体地说treat_path()
,检查任何路径规范,以便简化调用程序。但是在这样做的时候,我们为"excluded"的情况添加了一个稍早的返回,我们最终没有检查路径规范,这意味着我们在应该返回
path_none
的时候返回了path_excluded
,结果git status --ignored -- pathspec
(man)可能显示的路径实际上并不匹配"pathspec
"。将"excluded"复选框下移到检查完所有路径规范之后。
Git 2.38(Q3 2022)修复了在2.27周期中引入的 * 另一个 * 回归,这可能会影响git-bash的完成。
在一个非裸仓库中,当
core.worktree
配置变量指向一个子目录为仓库的目录时,Git的行为在Git 2.27中出现了退化。参见commit d6c9a71、commit 2712899(2022年6月16日),作者为Goss Geppert (
ggossdev
)。(由Junio C Hamano --
gitster
--合并至commit dc6315e,2022年7月14日)dir
:遍历到存储库签署人:戈斯·格佩特
审核人:伊莱贾·纽伦
由于8d92fb2("
dir
:replace exponential algorithm with a linear one ",2020 - 04 - 01,Git v2.27.0-rc0--merge listed in batch #5)当遍历开始于仓库的标准位置之外时,遍历仓库的目录树失败,因为遇到的仓库被标识为嵌套的外部仓库。在此提交之前,在以下任一条件下(可能还有其他条件),从用户的Angular 可以观察到未能遍历到存储库的默认工作树位置:
1.将
core.worktree
位置设置为默认工作树的父目录;或1.当工作目录不在存储库的默认工作树位置时,请使用
--git_dir
选项在这两种情况下,遍历存储库的默认工作树位置失败的症状包括无法将文件添加到索引或通过
ls-files
获取未跟踪文件的列表。这个提交添加了一个检查,以确定在递归路径时遇到的嵌套存储库是否实际上是
the_repository
。如果是这样,我们就简单地将目录视为不包含嵌套存储库。