Reputation: 85
I have git aliases in .bash_profile and would like to enable auto completion when I use these commands (eg: auto complete branch names). This works for me if I use the full git commands but not when I use aliases.
I've heard that auto completion is possible if you use aliases configured through .gitconfig but those aliases force the use of git whereas the aliases I've configured in .bash_profile have "git" substituted as "g" which is more convenient.
Upvotes: 3
Views: 1605
Reputation: 2145
The bash completions for various commands usually come in a bash_completions
package or are provided with the individual commands. Where they are installed is somewhat OS dependent, in Ubuntu they are collected in various places, starting at /etc/bash_completion
. This script is sourced from the global bash.bashrc
and ultimately pulls in various other scripts that define completions for various commands. Ultimately they all call the complete
command to define when to invoke what completion.
The completions for git
(again in Ubuntu, locations may vary) are installed to /usr/share/bash-completion/completions/git
. This file comes from the git source repository and defines functions for completions of the individual commands and ultimately a __git_complete
function that takes a command and a completion function and installs the completion for that command:
__git_complete git __git_main
The above adds completions for git
to call __git_main
(also defined in that file). As all of the completions are sourced, the used functions are all available in your shell and you can therefore use the same call to add git completions to your alias:
__git_complete g __git_main
If your alias wraps a certain git subcommand instead, use one of the various subcommand completions defined in that file. If you had an alias ga
to mean git add
you would use _git_add
completions instead:
__git_complete ga _git_add
Note that the __git_complete
function (as presumably all of the functions in that file) are private and not meant to be used directly. The function is labeled with a corresponding warning:
This is NOT a public function; use at your own risk.
Which basically means, updating your git
and/or bash_completions
may change that completions file and how the above functions are named and work may change, which would then break your setup. You should therefore only use the above if you are prepared to redo that setup every so often.
Note also that you might have to explicitly source that git completions file from your .bashrc
or .profile
for the functions to become available:
source /usr/share/bash-completion/completions/git
Some of the git completions actually inspect the current command line to decide what to complete next. Examples of that are anything that complete remotes or refspecs, i.e. fetch, pull, remote and push. In such cases it isn't enough to complete the alias with the corresponding function, as the alias will hide the actual git command and the completion will therefore not know how to proceed. As an example from the comments:
$ alias gpr='git pull --rebase'
$ __git_complete gpr _git_pull
$ gpr o[TAB]
$ gpr origin m[TAB]
$ gpr origin m
In this case the remote (origin in this case) can still be completed, but the refspec fails to complete. This is due to the missing "pull" command on the command line.
This case cannot fully be solved, but a workaround that requires an extra tab can be built. Instead of directly hooking into the git completion, a wrapper function is defined that first expands the alias with the wanted command line and then passes on for further completions:
function _gpr {
if [ $COMP_CWORD = 1 ]
then
COMPREPLY=('pull --rebase ')
return 0
fi
__git_func_wrap __git_main
}
The wrapper function checks if the current word is the first one and in that case sets the COMPREPLY
variable to the desired command line expansion (see the documentation for programmable completion for the exact details). Note the extra space at the end that is needed because the function will be installed with the nospace
option that prevents automatic insertion of a space.
The wrapper function is then installed as a completion for the desired alias:
complete -o bashdefault -o default -o nospace -F _gpr gpr
The -F _gpr
arguments specify the wrapper function to be called and the last argument (gpr
) the command to be completed (see the full documentation of the complete builtin for the other options).
With the wrapper completion in place, the above session now becomes:
$ gpr [TAB]
$ gpr pull --rebase o[TAB]
$ gpr pull --rebase origin m[TAB]
$ gpr pull --rebase origin master
An extra tab is needed right after the alias to first expand to the desired command line that was previously fully hidden in the alias. From there on, the completions work as expected.
Note that, since the desired command line is now part of the actual command line, the alias should only point at git
and not at git pull --rebase
. This is also the reason why the wrapper function always passes the completion on to __git_main
and not the individual subcommand completion. The alias definition becomes just:
alias gpr=git
A nice alternative to the above special case is to combine shell and git aliases. The git completions expand git aliases (i.e. aliases created with git config alias.<name> '<command>'
) for the purpose of completion. Configuring a git alias for the actual command:
$ git config --global alias.pr 'pull --rebase'
And then installing a shorthand shell alias for git:
$ alias g=git
And hooking that up to __git_main
completion using the original method:
$ __git_complete g __git_main
Allows for this session:
$ g pr o[TAB]
$ g pr origin m[TAB]
$ g pr origin master
Upvotes: 8