如何禁用Git Bash中远程分支的自动补全功能?

49
我正在处理一个包含数千个(远程)分支的相当大的git存储库。我习惯于在控制台(在这种情况下为Git Bash)中使用自动完成(使用[TAB]),因此我也会无意识地对git命令执行此操作。
例如,我会输入:
git checkout task[TAB]

由此导致控制台经常停顿数分钟。是否有一种方法仅限于本地分支来限制自动完成?


2
注意:自2011年以来,您可以(使用Git 2.13,Q2 2017)git checkout --no-guess ...export GIT_COMPLETION_CHECKOUT_NO_GUESS=1:这两者都会禁用分支补全。请参见我的答案 - VonC
9个回答

32

从 Git 2.13 (2017 年第二季度) 开始,您可以禁用(部分)分支自动补全功能。

git checkout --no-guess ...
# or:
export GIT_COMPLETION_CHECKOUT_NO_GUESS=1

请看 提交60e71bb (2017年4月21日) 由Jeff King (peff)完成。
(由Junio C Hamano -- gitster --合并于提交b439747, 2017年5月1日)

如现在所记录在contrib/completion/git-completion.bash中:

You can set the following environment variables to influence the behavior of the completion routines:

GIT_COMPLETION_CHECKOUT_NO_GUESS

When set to "1", do not include "DWIM" suggestions in git-checkout completion (e.g., completing "foo" when "origin/foo" exists).

注意:DWIM是 Do What I Mean 的缩写, 它指的是系统尝试预测用户想要做什么,自动纠正微小错误,而不是盲目执行用户明确但可能不正确的输入。

completion: 可选禁用 DWIM 检出

当我们为 "git checkout" 补全分支名称时,我们还会补全可能触发 DWIM 行为的远程分支名称。根据您的工作流和项目,这可能既方便又烦人。

例如,我的 gitster.git 克隆包含 74 个本地 "jk/*" 分支,但 origin 包含另外 147 个。
当我想要检出一个本地分支但无法完全记住名称时,制表符补全会显示 251 个条目。更糟糕的是,对于已被选中用于 pu 的主题,上游分支名称很可能与我的类似,导致我选择错误并意外创建新分支的概率很高。


请注意:"picked up for pu":请参阅git.git的最新进展,它以以下内容开始:
引号内前缀为“-”的提交仅在“pu(proposed updates)”分支中,而前缀为“+”的提交在“next”中。
这是Git工作流程升级过程的一部分。
“pu(proposed updates)”是一个集成分支,用于那些还不太准备好包含的东西。

This patch adds a way for the user to tell the completion code not to include DWIM suggestions for checkout.
This can already be done by typing:

git checkout --no-guess jk/<TAB>

but that's rather cumbersome.

The downside, of course, is that you no longer get completion support when you do want to invoke the DWIM behavior.
But depending on your workflow, that may not be a big loss (for instance, in git.git I am much more likely to want to detach, so I'd type "git checkout origin/jk/<TAB>" anyway).


@sehe 我已经在“pu”部分添加了参考资料和文档。 - VonC
2
遗憾的是,创建一个内部使用此选项的别名似乎不起作用。只在CLI上直接提供该选项会导致仅本地分支完成。 :( - Sascha Wolf
我认为目前最好的解决方案(似乎完全回答了OP的请求)是在使用2.23版本之后的git switch时设置GIT_COMPLETION_CHECKOUT_NO_GUESS=1。@VonC - lajarre

26

我假设你正在使用git-completion.bash脚本,并且你只关心git checkout

为了实现这一点,我只在git-completion.bash_git_checkout ()函数的定义中更改了一行:

<       __gitcomp_nl "$(__git_refs '' $track)"
---
>       __gitcomp_nl "$(__git_heads '' $track)"

我的理解是,这只影响了选项补全操作(因为它位于 switch-case 语句的 * 案例内部)。


3
步骤:输入 sudoedit /usr/share/bash-completion/completions/git,然后更改 @erik.weathers 回答中提到的那一行,重新打开 bash,完成!非常感谢。 - mschayna
1
我同意@andre.orvalho的观点,这绝对是现在更好的解决方案。 - Joel Pearson
3
我增加了一个新的方法:__git_rco()。RCO(或远程检出)使用旧的refs行为。__git_checkout()现在只用于heads。我创建了一个别名rco = checkout。这样,如果你想要旧的行为,你仍然可以得到它。 - Matt Hughes
我添加了一个额外的情况,其中"")使用__git_heads,但*)使用__git_refs。这样它只建议头部,但如果您已经输入了某些内容(即t),它将给出任何匹配的引用(即tag/sometag)。如果这仍然太多,它也可以匹配tag/-> tag/sometags等。 - arcyqwerty
如果你通过brew安装了git-completion,它的位置在这里:/usr/local/etc/bash_completion.d/git-completion.bash - Toland Hon

11
如果您通过Homebrew安装了git-completion,它位于这里:/usr/local/etc/bash_completion.d/git-completion.bash 按照上面erik.weathers的答案所述,我做了以下更改,以便根据当前前缀自动完成本地和远程工作。默认情况下,它只会搜索本地,但如果我指定origin/…,它就会知道我想要搜索远程分支。
_git_checkout ()方法中,更改
    __gitcomp_nl "$(__git_refs '' $track)"

到:

    # only search local branches instead of remote branches if origin isn't specified
    if [[ $cur == "origin/"* ]]; then
        __gitcomp_nl "$(__git_refs '' $track)"
    else
        __gitcomp_nl "$(__git_heads '' $track)"
    fi

当然,你可以将origin更改为其他内容,或者如果有多个远程前缀,则可以让它搜索远程前缀列表。

我认为这是最完整的答案。 - WBC

10

你可以黑掉 /etc/bash_completion.d/git

你需要编辑 __git_refs ()

请注意,此更改的行为将在任何地方应用(因此即使在使用 git push/pull 时,您可能不希望它这样做)。 当然,你可以复制该函数或传递额外的参数,但我将其留给你决定。


5
是的,事实证明标签和远程分支都很慢。我将refs="refs/tags refs/heads refs/remotes"更改为只有refs="refs/heads",并注释掉整个“尝试查找与完成单词匹配的远程分支”的部分。这样做不太美观,但可以正常工作 :-) - Manuela Hutter
嗯,你不会碰巧是在使用网络上的git存储库吧?我从未见过git的那种可悲性能(但是:在Windows中,git add众所周知非常慢) - sehe
不,我只是在本地使用git。我想这只是一个非常庞大的repo。git for-each-ref | wc -l 返回11000... - Manuela Hutter
但是,是的,在Windows上Git相比于*nix/Mac而言明显较慢。 - Manuela Hutter
有没有想法如何在zsh中实现这个? - Galder Zamarreño
@GalderZamarreño 我通过修改 /usr/share/zsh/4.3.11/functions/_git 已经让它工作了。 - c.apolzon

4
你可能会认为你只需要使用别名co来切换本地分支,而使用完整命令checkout来切换所有分支。

你可以执行以下操作,在你的.bashrc文件中重新定义_git_checkout()函数。你让这个函数保持不变,除了结尾部分。
if [ $command -eq "co" ]; then
  __gitcomp "$(__git_refs_local '' $track)"
else
  __gitcomp "$(__git_refs '' $track)"
fi

然后,您只需定义一个新函数__git_refs_local,在其中删除远程内容即可。

1
"-eq" 用于比较数字,比较应该是 if [ $command = "co" ]; then - pR0Ps

3
修改 $(brew --prefix)/etc/bash_completion.d/git-completion.bash 并不是一个好主意,因为每次通过 Homebrew 更新 Git 时都会被覆盖。
综合所有答案,在导入完成文件后,我只重写了.bash_profile 中的 _git_checkout 函数。
_git_checkout ()
{
  __git_has_doubledash && return

  case "$cur" in
    --conflict=*)
      __gitcomp "diff3 merge" "" "${cur##--conflict=}"
      ;;
    --*)
      __gitcomp "
      --quiet --ours --theirs --track --no-track --merge
      --conflict= --orphan --patch
      "
      ;;
    *)
      # check if --track, --no-track, or --no-guess was specified
      # if so, disable DWIM mode
      local flags="--track --no-track --no-guess" track=1
      if [ -n "$(__git_find_on_cmdline "$flags")" ]; then
        track=''
      fi
      # only search local branches instead of remote branches if origin isn't
      # specified
      if [[ $cur == "origin/"* ]]; then
        __gitcomp_nl "$(__git_refs '' $track)"
      else
        __gitcomp_nl "$(__git_heads '' $track)"
      fi
      ;;
  esac
}

3

Carey Metcalfe写了一篇博客文章,提供了一个解决方案,还编辑了自动完成功能,但使用的代码略微更新于其他答案。他还定义了一个别名checkoutr,以保留旧的自动完成行为,以防需要。

简而言之,首先使用此命令创建checkoutr别名:

git config --global alias.checkoutr checkout

然后找到 git-completion.bash,将 _git_checkout 函数复制到您的 shell 的 RC 文件中以便重新定义它,并在该函数内部替换此行:
__git_complete_refs $track_opt

使用以下代码行:

if [ "$command" = "checkoutr" ]; then
    __git_complete_refs $track_opt
else
    __gitcomp_direct "$(__git_heads "" "$cur" " ")"
fi

请查看博客文章了解更多细节和代码的可能更新。


0

我自己没有使用Git Bash,但如果这与http://tekrat.com/2008/04/30/bash-autocompletion-git-super-lazy-goodness/中提到的相同,那么你应该能够用普通的git branch替换git branch -a

_complete_git() {
  if [ -d .git ]; then
    branches=`git branch -a | cut -c 3-`
    tags=`git tag`
    cur="${COMP_WORDS[COMP_CWORD]}"
    COMPREPLY=( $(compgen -W "${branches} ${tags}" -- ${cur}) )
  fi
}
complete -F _complete_git git checkout

(在您的.profile或类似文件中)获取您想要的内容。

嗯,这个函数将成为标准Git行为的补充(至少我在任何地方都没有这个函数)。我的目标是比默认情况下做得更少。编辑__git_refs()似乎可以解决问题。 - Manuela Hutter

0

FWW 这里有一个 hack 来完成 __git_complete_refs 的技巧

__git_complete_refs ()

{ 本地远端轨道 pfx cur_="$cur" sfx=" "

while test $# != 0; do
    case "$1" in
    --remote=*) remote="${1##--remote=}" ;;
    --track)    track="yes" ;;
    --pfx=*)    pfx="${1##--pfx=}" ;;
    --cur=*)    cur_="${1##--cur=}" ;;
    --sfx=*)    sfx="${1##--sfx=}" ;;
    *)      return 1 ;;
    esac
    shift
done
echo cur_ $cur_ > a
 if [[  $GIT_COMPLETION_CHECKOUT_NO_GUESS != 1 || $cur_ == "origin"* ]]; then
    __gitcomp_direct "$(__git_refs "$remote" "$track" "$pfx" "$cur_" "$sfx")"
  else
    __gitcomp_direct "$(__git_heads  "" "$cur_")"
  fi

}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接