Git bash-completion 支持文件名吗?

发布于 2024-11-08 00:21:28 字数 481 浏览 0 评论 0 原文

是否有支持文件名完成的 bash-completion 脚本?我主要使用 Mercurial,我可以输入:

hg diff test/test_<tab>

它将显示/完成所有修改的测试文件。它适用于大多数子命令,即 hg add 将仅列出未跟踪的文件。真的很方便。

git contrib 的 bash 脚本似乎不支持这一点。有没有其他选择,或者如何在命令行上使用 git?

Edit 2015

git-completion.bash~1.8.2

is there a bash-completion script that supports filename completion? I use mostly mercurial and there I can type:

hg diff test/test_<tab>

and it will show/complete all modified test files. It works for most subcommands, i.e. hg add <tab><tab> will only list untracked files. It is really handy.

The bash script from git contrib seams to not support this. Are there any alternatives, or how do you work with git on the command line?

Edit 2015

git-completion.bash supports full filename completion since ~1.8.2

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

心安伴我暖 2024-11-15 00:21:28

那么,让我们看看 Mercurial bash 完成脚本如何执行此操作。

这是重要部分

_hg_status()
{
    local files="$(_hg_cmd status -n$1 .)"
    local IFS=

它被称为此处

_hg_command_specific()
{
    case "$cmd" in 
    [...]
    diff)
        _hg_status "mar"
    ;;
    [...]
    esac
    return 0
}

因此,它只是对 hg 的调用status -nmar,并使用输出作为完成的文件列表。

我认为将类似的内容修补到 git 补全脚本 - 我们必须在这里修改 __git_diff ,以不执行纯文件名+分支补全,而是调用 git status反而。


命令

git status --porcelain | grep '^.[^ ?]' | cut -b 4-

(对于git diff --cached)和

git status --porcelain | grep '^[^ ?]' | cut -b 4-

(对于git diff)似乎输出了正确的内容(如果没有重命名)。

不过,当与 HEAD 以外的任何东西进行比较时,它们都没有用。

更通用的方法是使用

git diff --relative --name-only [--cached] [commit1] [commit2]]

来自已给定 diff 命令行的 commit1commit2 (也许还有 --cached)。


我在 bash 中实现了上面概述的想法,并修补到 git-completion.bash 中。如果您不想更改 git-completion.bash,请将这两个函数添加到某个 bash 文件中,并在原始 git-completion.bash 之后获取它。它现在应该可以使用像

git diff -- <tab>
git diff --cached -- <tab>
git diff HEAD^^ -- <tab>
git diff origin/master master -- <tab>

提交此 作为 git 邮件列表的补丁,让我们看看这会产生什么结果。 (当我在那里得到反馈时,我会更新这个答案。)

# 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 输出可能为空时完成)。如果链接到某些配置变量(默认为当前行为),则可能会接受它。此外,缩进应适应标准(请参阅

我可能会再试一次,但不能保证在不久的将来会如此。如果其他人想做,请随意拿走我的代码,更改它并再次提交。

\n' COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$files' -- "$cur")) }

它被称为此处


因此,它只是对 hg 的调用status -nmar,并使用输出作为完成的文件列表。

我认为将类似的内容修补到 git 补全脚本 - 我们必须在这里修改 __git_diff ,以不执行纯文件名+分支补全,而是调用 git status反而。


命令


(对于git diff --cached)和


(对于git diff)似乎输出了正确的内容(如果没有重命名)。

不过,当与 HEAD 以外的任何东西进行比较时,它们都没有用。

更通用的方法是使用


来自已给定 diff 命令行的 commit1commit2 (也许还有 --cached)。


我在 bash 中实现了上面概述的想法,并修补到 git-completion.bash 中。如果您不想更改 git-completion.bash,请将这两个函数添加到某个 bash 文件中,并在原始 git-completion.bash 之后获取它。它现在应该可以使用像


提交此 作为 git 邮件列表的补丁,让我们看看这会产生什么结果。 (当我在那里得到反馈时,我会更新这个答案。)



更新:看起来这个形式不需要这个补丁,因为当前完成文件的方式对于想要检查的人来说更有用某些子目录是否有更改(例如,当 diff 输出可能为空时完成)。如果链接到某些配置变量(默认为当前行为),则可能会接受它。此外,缩进应适应标准(请参阅

我可能会再试一次,但不能保证在不久的将来会如此。如果其他人想做,请随意拿走我的代码,更改它并再次提交。

So, let's see how the Mercurial bash completion script does this.

This is the important part:

_hg_status()
{
    local files="$(_hg_cmd status -n$1 .)"
    local IFS=

It gets called here:

_hg_command_specific()
{
    case "$cmd" in 
    [...]
    diff)
        _hg_status "mar"
    ;;
    [...]
    esac
    return 0
}

Thus, it is simply a call of hg status -nmar, and using the output as a list of files for completion.

I think it would not be too hard to patch something similar into the git completion script - we would have to modify __git_diff here to not do a plain filename + branch completion, but calling git status instead.


The commands

git status --porcelain | grep '^.[^ ?]' | cut -b 4-

(for git diff --cached) and

git status --porcelain | grep '^[^ ?]' | cut -b 4-

(for git diff) seem to output the right thing (if there are no renames).

They both are not useful when diffing to anything other than HEAD, though.

A more general way would be to use

git diff --relative --name-only [--cached] [commit1] [commit2]]

where commit1 and commit2 (and maybe --cached) come from the already given diff command line.


I implemented the idea outlined above in bash, and patched into git-completion.bash. If you don't want to change your git-completion.bash, add these two functions to some bash file and source it after the original git-completion.bash. It should now work with commands like

git diff -- <tab>
git diff --cached -- <tab>
git diff HEAD^^ -- <tab>
git diff origin/master master -- <tab>

I submitted this as a patch to the git mailing list, let's see what results from this. (I'll update this answer as I get feedback there.)

# 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
}

Update: Looks like this patch is not wanted in this form, as the current way to complete files is more useful for people which want to check whether there are changes in some subdirectory (e.g. completing when the diff output could be empty). It might be accepted if linked to some configuration variable (with the default being the current behavior). Also, the indenting should be adapted to the standard (see the answer from Junio C Hamano).

I might take another go on it, but can't guarantee this for the near future. If someone else want to do, feel free to take my code, change it and submit it again.

\n' COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$files' -- "$cur")) }

It gets called here:


Thus, it is simply a call of hg status -nmar, and using the output as a list of files for completion.

I think it would not be too hard to patch something similar into the git completion script - we would have to modify __git_diff here to not do a plain filename + branch completion, but calling git status instead.


The commands


(for git diff --cached) and


(for git diff) seem to output the right thing (if there are no renames).

They both are not useful when diffing to anything other than HEAD, though.

A more general way would be to use


where commit1 and commit2 (and maybe --cached) come from the already given diff command line.


I implemented the idea outlined above in bash, and patched into git-completion.bash. If you don't want to change your git-completion.bash, add these two functions to some bash file and source it after the original git-completion.bash. It should now work with commands like


I submitted this as a patch to the git mailing list, let's see what results from this. (I'll update this answer as I get feedback there.)



Update: Looks like this patch is not wanted in this form, as the current way to complete files is more useful for people which want to check whether there are changes in some subdirectory (e.g. completing when the diff output could be empty). It might be accepted if linked to some configuration variable (with the default being the current behavior). Also, the indenting should be adapted to the standard (see the answer from Junio C Hamano).

I might take another go on it, but can't guarantee this for the near future. If someone else want to do, feel free to take my code, change it and submit it again.

像你 2024-11-15 00:21:28

这为我解决了 git diff 问题,将以下内容放入 .bashrc 中:

alias gid='git diff'
__gdiff () {
    local cur prev opts
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"
    opts=$(git status --porcelain | grep '^.[^ ?]' | cut -b 4-)

    case "${prev}" in
        gid)
            COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
            ;;
    esac
}
complete -F __gdiff gid

然后改为执行 gid git diff 。它可能被简化了,但似乎作为一种快速解决方案效果很好。

This solves the git diff <tab> problem for me, put the following in .bashrc:

alias gid='git diff'
__gdiff () {
    local cur prev opts
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"
    opts=$(git status --porcelain | grep '^.[^ ?]' | cut -b 4-)

    case "${prev}" in
        gid)
            COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
            ;;
    esac
}
complete -F __gdiff gid

And then do gid <tab> instead of git diff <tab>. It may be simplified, but seems to work well as a quick fix.

少女净妖师 2024-11-15 00:21:28

这并不是您真正想要的答案,但我想让您知道,很快 Fish(友好的交互式 shell)将为您提供开箱即用的 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

如果您的状态如下:

$ git status
modified: ../README.md
$ git add <tab>
:/README.md 

在此处输入图像描述

您也可以只输入 README 并点击 Tab,如果它是唯一匹配的,它会为您插入它。真他妈的好!

Not really your desired answer but I wanted to let you know that soon fish (friendly interactive shell) will give you git filename completion support out of the box. It is currently in master with a 2.3.0 release coming soon.

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

If you have a status like this:

$ git status
modified: ../README.md
$ git add <tab>
:/README.md 

enter image description here

You can also just type README and hit tab and it will insert it for you if it is the only match. Friggin nice!

三生一梦 2024-11-15 00:21:28

自 2011 年以来,正如 OP 所评论的,Git 从 ~1.8.2 开始支持完整文件名完成。

但在 Git 2.18(2018 年第 2 季度)中,提供路径列表的 shell 补全(在 contrib/ 中)已得到一定程度的优化。

请参阅 提交 78a2d21(2018 年 4 月 4 日),作者:Clemens Buchacher (drizzd)
(由 Junio C Hamano -- gitster -- 合并于 提交 3a940e9,2018 年 4 月 25 日)

completion:提高ls-files过滤器性能

ls-files的输出中,我们删除除最左边路径之外的所有路径
组件,然后我们消除重复项。我们在 while 循环中执行此操作,
当迭代次数较多时,这是一个性能瓶颈
(例如,linux.git 中的 60000 个文件)。

$ COMP_WORDS=(git status -- ar) COMP_CWORD=3;时间_git

真实0米11.876秒
用户0m4.685s
系统0m6.808s

用 cut 命令替换循环可提高性能
显着:

$ COMP_WORDS=(git status -- ar) COMP_CWORD=3;时间_git

真实0m1.372s
用户0m0.263s
系统0m0.167s

测量是使用 Msys2 bash 完成的,Git 将其用于
Windows。

在过滤 ls-files 输出时,我们注意不要触及绝对值
路径。这是多余的,因为 ls-files 永远不会输出绝对值
路径。删除不必要的操作。

该问题最初报告于 Git for Windows 问题 1533


目录遍历代码具有冗余递归调用,这使得其性能特征相对于树的深度呈指数级增长,这一点已在 Git 2.27(2020 年第二季度)中得到纠正。

请参阅提交c0af173提交 95c11ec, 提交 7f45ab2, 提交 1684644, 提交 8d92fb2, 提交 2df179d, 提交 0126d14, 提交 cd129ee, 提交 446f46d, 提交 7260c7b, 提交 ce5c61a(2020 年 4 月 1 日),作者:以利亚纽伦 (newren)
请参阅 提交 0bbd0e8(2020 年 4 月 1 日),作者:Derrick Stolee (derrickstolee)
(由 Junio C Hamano -- gitster -- 合并于 提交 6eacc39,2020 年 4 月 29 日)

完成:修复'git在未跟踪目录下的路径上添加'

签字人:伊利亚·纽伦

据 git 邮件列表报道,自 git-2.25 起,

git add untracked-dir/

已制表符补全至

git add untracked-dir/./

造成这种情况的原因是 commit b9670c1f5e ("dir :修复对公共前缀目录的检查”, 2019-12-19,Git v2.25.0-rc0 -- 合并),< /p>

git ls-files -o --directory untracked-dir/

(或等效的git -C untracked-dir ls-files -o --directory)开始报告

untracked-dir/

而不是列出该目录下的路径。

还值得注意的是,有问题的真正命令是

git -C untracked-dir ls-files -o --directory '*'

这相当于:

git ls-files -o --directory 'untracked-dir/*'

对于此问题的目的,其行为相同(“*”可以匹配空字符串),但与建议的修复相关。

首先,根据该报告,我决定尝试将其视为一种回归,并尝试找到一种方法来恢复旧行为而不破坏其他内容,或者至少尽可能少地破坏。
然而,最终我找不到一种方法,既不会导致比解决的问题更多的问题。

旧的行为是一个错误:

  • 尽管旧版 git 会避免使用 git clean -f .git 清理任何内容,但它会使用 git clean -f .git/ 清除该目录下的所有内容。< br>
    尽管使用的命令有所不同,但这是相关的,因为修复 clean 的完全相同的更改改变了 ls 文件的行为。
  • 旧版 git 会仅根据命令 git ls-files -o --directory $SUBDIR 中是否存在 $SUBDIR 的尾部斜杠来报告不同的结果.
  • 旧版 git 违反了记录的行为,即在指定 --directory 时不递归到与路径规范匹配的目录。
  • 毕竟,提交 b9670c1f5e (dir:修复对公共前缀目录的检查, 2019-12-19,Git v2.25.0-rc0)并没有忽视这个问题;它明确指出该命令的行为正在更改以使其与文档保持一致。

(另外,如果有帮助的话,尽管该提交在 2.25 系列期间合并,但在 2.25 周期期间没有报告此错误,甚至在 2.26 周期的大部分时间里也没有报告 - 它是在 2.26 发布前一天报告的。

因此,更改的影响至少有点小。)

不要依赖 ls-files 的错误来报告错误的内容,而是更改 git-completion 使用的 ls-files 的调用,使其抓取更深一层的路径。

通过将“$DIR/*”(匹配 $DIR/ 加 0 个或多个字符)更改为“$DIR/?*'(匹配 $DIR/ 加 1 个或多个字符)。

请注意,尝试完成文件名时不应添加 '?' 字符(例如 'git ls-files -o --directory merge.c?*"' 会没有正确返回"[merge.c](https://github.com/git/git/blob/c0af173a136785b3cfad4bd414b2fb10a130760a/merge.c)" 当这样的文件存在时),所以我们必须确保添加仅当当前指定的路径是目录时才使用“?`”字符。


警告:Git 2.29 (2020 年第四季度)修复了 2.27 周期期间引入的回归。

请参阅提交 cada730(7 月 20 日 ) 2020)由 Martin Ågren(
(由 Junio C Hamano -- gitster -- 合并于 提交 82fafc7,2020 年 7 月 30 日)

dir:返回path_excluded

报告人:Andreas Schwab
审阅者:Elijah Newren
签字人:Martin Ågren

95c11ecc73中(“修复容易出错的fill_directory()< /code> API;使其仅返回匹配项”, 2020-04-01,Git v2.27.0-rc0 -- 合并 <一个href="https://github.com/git/git/commit/d61d20c9b413225793f8a0b491bbbec61c184e26" rel="nofollow noreferrer">batch #5),我们教授了 fill_directory() 或更多特别是 treat_path(),检查任何路径规范,以便我们可以简化来电者。

但在这样做的过程中,我们为“排除”案例添加了一个稍微过早的回报。我们最终没有检查路径规范,这意味着我们可能应该返回 path_none 时返回 path_excluded。因此, git status --ignored -- 路径规范(man) 可能会显示实际上与“pathspec”不匹配的路径。

将“排除”检查向下移动到我们检查了任何路径规范之后。


Git 2.38(2022 年第 3 季度)修复了 2.27 周期中引入的另一个回归,这可能会影响 git-bash 的完成。

在非裸存储库中,当 core.worktree 配置变量指向以存储库作为其子目录的目录时,Git 的行为在 Git 2.27 天中回归。

请参阅 提交 d6c9a71提交 2712899(2022 年 6 月 16 日),作者:高斯·格佩特 (ggossdev)
(由 Junio C Hamano -- gitster -- 合并于 提交 dc6315e,2022 年 7 月 14 日)

dir:遍历到存储库

签署人:Goss Geppert
审阅者:Elijah Newren

8d92fb2(“dir:替换指数线性算法”,2020-04-01,Git v2.27.0-rc0 -- 合并batch #5) 当遍历开始于存储库的标准位置之外时,遍历到存储库的目录树失败,因为遇到的存储库被识别为嵌套的外部存储库。

在此提交之前,在以下任一条件下(可能还有其他条件),从用户的角度来看,无法遍历到存储库的默认工作树位置:

  1. core.worktree位置设置为默认工作树的父目录;或
  2. 当工作目录位于外部时使用 --git_dir 选项
    存储库的默认工作树位置

在这两种情况下,无法遍历存储库的默认工作树位置的症状包括无法将文件添加到索引或无法通过 ls-files 获取未跟踪文件的列表。< /p>

此提交添加了一项检查,以确定递归路径中遇到的嵌套存储库是否实际上是 the_repository
如果是这样,我们只需将该目录视为不包含嵌套存储库即可。

Since 2011, as the OP comments, Git supports full filename completion since ~1.8.2.

But with Git 2.18 (Q2 2018), the shell completion (in contrib/) that gives list of paths have been optimized somewhat.

See commit 78a2d21 (04 Apr 2018) by Clemens Buchacher (drizzd).
(Merged by Junio C Hamano -- gitster -- in commit 3a940e9, 25 Apr 2018)

completion: improve ls-files filter performance

From the output of ls-files, we remove all but the leftmost path
component and then we eliminate duplicates. We do this in a while loop,
which is a performance bottleneck when the number of iterations is large
(e.g. for 60000 files in linux.git).

$ COMP_WORDS=(git status -- ar) COMP_CWORD=3; time _git

real    0m11.876s
user    0m4.685s
sys     0m6.808s

Replacing the loop with the cut command improves performance
significantly:

$ COMP_WORDS=(git status -- ar) COMP_CWORD=3; time _git

real    0m1.372s
user    0m0.263s
sys     0m0.167s

The measurements were done with Msys2 bash, which is used by Git for
Windows.

When filtering the ls-files output we take care not to touch absolute
paths. This is redundant, because ls-files will never output absolute
paths. Remove the unnecessary operations.

The issue was originally reported Git for Windows issue 1533.


The directory traversal code had redundant recursive calls which made its performance characteristics exponential with respect to the depth of the tree, which was corrected with Git 2.27 (Q2 2020).

See commit c0af173, commit 95c11ec, commit 7f45ab2, commit 1684644, commit 8d92fb2, commit 2df179d, commit 0126d14, commit cd129ee, commit 446f46d, commit 7260c7b, commit ce5c61a (01 Apr 2020) by Elijah Newren (newren).
See commit 0bbd0e8 (01 Apr 2020) by Derrick Stolee (derrickstolee).
(Merged by Junio C Hamano -- gitster -- in commit 6eacc39, 29 Apr 2020)

completion: fix 'git add' on paths under an untracked directory

Signed-off-by: Elijah Newren

As reported on the git mailing list, since git-2.25,

git add untracked-dir/

has been tab completing to

git add untracked-dir/./

The cause for this was that with commit b9670c1f5e ("dir: fix checks on common prefix directory", 2019-12-19, Git v2.25.0-rc0 -- merge),

git ls-files -o --directory untracked-dir/

(or the equivalent git -C untracked-dir ls-files -o --directory) began reporting

untracked-dir/

instead of listing paths underneath that directory.

It may also be worth noting that the real command in question was

git -C untracked-dir ls-files -o --directory '*'

which is equivalent to:

git ls-files -o --directory 'untracked-dir/*'

which behaves the same for the purposes of this issue (the '*' can match the empty string), but becomes relevant for the proposed fix.

At first, based on the report, I decided to try to view this as a regression and tried to find a way to recover the old behavior without breaking other stuff, or at least breaking as little as possible.
However, in the end, I couldn't figure out a way to do it that wouldn't just cause lots more problems than it solved.

The old behavior was a bug:

  • Although older git would avoid cleaning anything with git clean -f .git, it would wipe out everything under that directory with git clean -f .git/.
    Despite the difference in command used, this is relevant because the exact same change that fixed clean changed the behavior of ls-files.
  • Older git would report different results based solely on presence or absence of a trailing slash for $SUBDIR in the command git ls-files -o --directory $SUBDIR.
  • Older git violated the documented behavior of not recursing into directories that matched the pathspec when --directory was specified.
  • And, after all, commit b9670c1f5e (dir: fix checks on common prefix directory, 2019-12-19, Git v2.25.0-rc0) didn't overlook this issue; it explicitly stated that the behavior of the command was being changed to bring it inline with the docs.

(Also, if it helps, despite that commit being merged during the 2.25 series, this bug was not reported during the 2.25 cycle, nor even during most of the 2.26 cycle -- it was reported a day before 2.26 was released.

So the impact of the change is at least somewhat small.)

Instead of relying on a bug of ls-files in reporting the wrong content, change the invocation of ls-files used by git-completion to make it grab paths one depth deeper.

Do this by changing '$DIR/*' (match $DIR/ plus 0 or more characters) into '$DIR/?*' (match $DIR/ plus 1 or more characters).

Note that the '?' character should not be added when trying to complete a filename (e.g. '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 '?`' character only in cases where the path specified so far is a directory.


Warning: Git 2.29 (Q4 2020) fixes a regression introduced during 2.27 cycle.

See commit cada730 (20 Jul 2020) by Martin Ågren (none).
(Merged by Junio C Hamano -- gitster -- in commit 82fafc7, 30 Jul 2020)

dir: check pathspecs before returning path_excluded

Reported-by: Andreas Schwab
Reviewed-by: Elijah Newren
Signed-off-by: Martin Ågren

In 95c11ecc73 ("Fix error-prone fill_directory() API; make it only return matches", 2020-04-01, Git v2.27.0-rc0 -- merge listed in batch #5), we taught fill_directory(), or more specifically treat_path(), to check against any pathspecs so that we could simplify the callers.

But in doing so, we added a slightly-too-early return for the "excluded" case. We end up not checking the pathspecs, meaning we return path_excluded when maybe we should return path_none. As a result, git status --ignored -- pathspec(man) might show paths that don't actually match "pathspec".

Move the "excluded" check down to after we've checked any pathspecs.


Git 2.38 (Q3 2022) fixes another regression introduced during 2.27 cycle, which can impact git-bash completion.

In a non-bare repository, the behavior of Git when the core.worktree configuration variable points at a directory that has a repository as its subdirectory, regressed in Git 2.27 days.

See commit d6c9a71, commit 2712899 (16 Jun 2022) by Goss Geppert (ggossdev).
(Merged by Junio C Hamano -- gitster -- in commit dc6315e, 14 Jul 2022)

dir: traverse into repository

Signed-off-by: Goss Geppert
Reviewed-by: Elijah Newren

Since 8d92fb2 ("dir: replace exponential algorithm with a linear one", 2020-04-01, Git v2.27.0-rc0 -- merge listed in batch #5) traversing into a repository's directory tree when the traversal began outside the repository's standard location has failed because the encountered repository was identified as a nested foreign repository.

Prior to this commit, the failure to traverse into a repository's default worktree location was observable from a user's perspective under either of the following conditions (there may be others):

  1. Set the core.worktree location to a parent directory of the default worktree; or
  2. Use the --git_dir option while the working directory is outside
    the repository's default worktree location

Under either of these conditions, symptoms of the failure to traverse into the repository's default worktree location include the inability to add files to the index or get a list of untracked files via ls-files.

This commit adds a check to determine whether a nested repository that is encountered in recursing a path is actually the_repository.
If so, we simply treat the directory as if it doesn't contain a nested repository.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文