在 bash 别名中 Git 自动完成?

我使用 go作为 git checkout branchname的一个简单 bash 别名。我忽略了一个自动补全特性,它可以用于完整的 git checkout branchna...命令,但不能用于别名。

是否有方法指示 Bash“继承”另一个命令的自动完成“驱动程序”?

17878 次浏览

If you can find out the completion function used by the original command, you can assign it to the alias using complete -F.

For example, on my ubuntu box, the completion function used by git checkout is _git_checkout (found in /etc/bash_complete.d/git).

Example

Before running complete -F:

[me@home]$ git checkout <TAB><TAB>
HEAD            master          origin/HEAD     origin/master


[me@home]$ alias go="git checkout"


[me@home]$$ go <TAB><TAB>
.git/                precommit_config.py  README.md            SvnSentinel/
.gitignore           precommit.py         startcommit.py       tests/

After:

[me@home]$$ complete -F _git_checkout go


[me@home]$$ go <TAB><TAB>
HEAD            master          origin/HEAD     origin/master

On Linux Mint, the accepted answer didn't work for me. I was getting bash: [: 1: unary operator expected .

I found the following linked response worked quite well - with the troubleshooting section the user provided to be quite helpful. https://superuser.com/questions/436314/how-can-i-get-bash-to-perform-tab-completion-for-my-aliases

After using complete -F:

complete -F _git_checkout go

Tabbing after go may result in:

bash: [: 1: unary operator expected

Instead of complete, use __git_complete

This is git bash completion's built-in function for this purpose.

After declaring your alias, bind the correct auto-complete function to it:

# Main git completions (prior to git 2.30, you an use _git instead of __git_main)
alias g="git"
__git_complete g __git_main


alias go="git checkout"
__git_complete go _git_checkout


alias gp="git push"
__git_complete gp _git_push

In Ubuntu 16.04.3 LTS, the file I needed to source was /usr/share/bash-completion/completions/git. So in .bash_custom (or .bashrc, whatever):

[ -f /usr/share/bash-completion/completions/git ] && . /usr/share/bash-completion/completions/git
__git_complete g __git_main

On Ubuntu 18.04 (Bionic) the following works. Add something like this snippet (with your aliases) to your preferred bash configuration file e.g. .bashrc, .bash_aliases .bash_profile.

# define aliases
alias gc='git checkout'
alias gp='git pull'


# setup autocompletion
if [ -f "/usr/share/bash-completion/completions/git" ]; then
source /usr/share/bash-completion/completions/git
__git_complete gc _git_checkout
__git_complete gp _git_pull
else
echo "Error loading git completions"
fi

In general the format of the __git_complete directive is the following:

__git_complete <YOUR ALIAS> _git_<GIT COMMAND NAME>

This combines wisdom from the existing answers in a single up-to-date answer, thank you all.

To add to other excellent answers: normally you have a lot of Git aliases and it may be tedious to manually forward completions for all of them. Here's a small trick to do this automatically:

if [ -f "/usr/share/bash-completion/completions/git" ]; then
# Enable Git completions for aliases
. /usr/share/bash-completion/completions/git
for a in $(alias | sed -n 's/^alias \(g[^=]*\)=.git .*/\1/p'); do
c=$(alias $a | sed 's/^[^=]*=.git \([a-z0-9\-]\+\).*/\1/' | tr '-' '_')
if set | grep -q "^_git_$c *()"; then
eval "__git_complete $a _git_$c"
fi
done
fi

For macOS, run the following to install bash completion

 brew install bash-completion

Then add the following

[[ -r "/usr/local/etc/profile.d/bash_completion.sh" ]] && . "/usr/local/etc/profile.d/bash_completion.sh"

to your .bashrc or .bash_profile

I realize you're specifically asking about bash aliases, but for those coming here looking for autocomplete in bash for complex git aliases, see here.

In particular:

# If you use complex aliases of form '!f() { ... }; f', you can use the null
# command ':' as the first command in the function body to declare the desired
# completion style.  For example '!f() { : git commit ; ... }; f' will
# tell the completion to use commit completion.  This also works with aliases
# of form "!sh -c '...'".  For example, "!sh -c ': git commit ; ... '".

As somebody else answered you should use __git_complete, otherwise the script will fail.

alias g="git"
__git_complete g __git_main


alias g="gl"
__git_complete gl _git_log

But you shouldn't use _git for the main command, it's __git_main.

Unfortunately a lot of information about the completion is hidden, but you can find more on the README of my fork: git-completion.