Extending git's bash completion for your own commands
Posted on Sun 08 November 2015 in Extending git
Git has pretty decent bash completion, and you can easily extend it for your own git scripts. It also provides some useful bash functions for making writing your bash completions easy. In this article we'll write a bash completion function for the fictional git-frotz command, which behaves like this:
$ git fritz -h
Fix your repos that are on the fritz
Usage:
git fritz [--branch=HEAD] [--noop] (wibble|wobble)
git fritz shimmy <file>
The basics
To integrate your command with git, its name should start with git-, it should be on your $PATH and it should have a manpage.
$ mv git-fritz ~/bin
$ exqport PATH="~/bin:$PATH"
$ PAGER= git fritz --help
GIT-FRITZ(1) Git Manual GIT-FRITZ(1)
NAME
git-fritz - Fix your repos that are on the fritz
SYNOPSIS
git fritz [--branch=HEAD] [--noop] (wibble|wobble)
git fritz shimmy <file>
DESCRIPTION
A description would go here
Showing the manpage when you ask for --help is one of the things git does for commands that integrate. No need to write code yourself. Now let's write our completion.
You don't actually have to write a full completion function, git's own
completion functions will call the shell function _git_fritz
automatically
when completing a git fritz
command. So all you need to do is create such a
function and make sure it's loaded when your shell starts. So we'll put it in a
file that we'll load from ~/.bashrc.
_git_fritz() {
# Actual code goes here
...
# And we'll make bash not use its default filename completion
compopt +o default
}
The compopt call is to prevent bash from doing filename completion if your completion function returns nothing.
Handling options
So now let's do something: we'll handle our options using the helper functions from git's bash completion.
_git_fritz() {
case "${cur}" in
--branch=*)
__gitcomp_nl "$(__git_heads)"
;;
--*)
__gitcomp "--branch= --noop --help"
;;
esac
# And we'll make bash not use its default filename completion
compopt +o default
}
Here you see three of the utility functions git's bash completion provides:
- __gitcomp makes it easy to specify possible completions in a string
- __gitcomp_nl does the same for strings where options are separated by newlines, which means you can easily use outputs of other git commands
- __git_heads gives you all branches, in a format usable by the bash completion.
Handling positional arguments
Handling positional arguments is not much harder than options. So let's complete our bash completion.
_git_fritz() {
if [ -n "$(__git_find_on_cmdline shimmy)" ]; then
_filedir
return
fi
case "${cur}" in
--branch=*)
__gitcomp_nl "$(__git_heads)"
;;
--*)
__gitcomp "--branch= --noop --help"
;;
*)
__gitcomp "wibble wobble shimmy"
esac
# And we'll make bash not use its default filename completion
compopt +o default
}
If the user has already provided shimmy as an argument, we'll let bash do its filename completion. Otherwise we'll the options completion we wrote before, but also complete the wibble/wobble/shimmy argument.
That's it! Our bash completion for git-fritz is now complete. For more examples of git completions, look at git's own bash completion, where you can also find all the helper functions I used and more.
For a more complicated third party command with bash-completion integration, you can look at git-spindle, which provides integration with GitHub, GitLab and BitBucket.