Completion is function-based: on Tab zsh finds a completion function matching the command name, and it must exist. Zsh reads no --help.
Minimum setup:
autoload -Uz compinit
compinit
zstyle ':completion:*' menu select
zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}'
setopt AUTO_MENU
setopt COMPLETE_IN_WORD
compinit loads completion and builds a dump file, ~/.zcompdump. Rebuild it if changes do not appear, or zsh warns about an insecure completion directory:
rm -f ~/.zcompdump*
compinit
Git completion
Git support is zsh’s own _git function: once compinit has run, these complete on Tab with refs, remotes and branches:
git checkout <TAB>
git switch <TAB>
git merge <TAB>
git rebase <TAB>
git push origin <TAB>
Oh My Zsh enables it; not required.
Who ships the script
A tool’s completion file is named after the command — _myapp — on $fpath, usually ~/.zsh/completions/ or the package manager’s site-functions directory. Modern CLIs generate it:
myapp completion zsh > ~/.zsh/completions/_myapp
compinit
Cobra (Go), clap (Rust), yargs or oclif (Node) emit one; writing your own is the fallback for internal tools.
Completion for an alias
Shell alias for git — alias g=git — must reuse git’s completion:
alias g=git
compdef g=git
Keep completealiases off, or zsh expands the alias before completion runs and the binding never matches:
unsetopt completealiases
Git’s own aliases — co = checkout — are handled by the official script via git help --aliases-for-completion.
Git aliases that shell out — starting with ! — are opaque shell commands; attach completion to a shell function:
wa() { command git wa "$@"; }
compdef _git-checkout wa
Branch completion in your own command
autoload -Uz compinit
compinit
_mycmd() {
local -a branches
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
branches=(${(f)"$(git for-each-ref --format='%(refname:short)' refs/heads 2>/dev/null)"})
_arguments '1:branch:compadd -a branches'
else
_message 'not in a git repository'
fi
}
compdef _mycmd mycmd
Add refs/remotes to for-each-ref for remote branches; scope to one subcommand via $words[2]. A branch argument followed by more:
_arguments \
'1:branch:_git-branch' \
'*::args:_files'
Helpers by type: _git-branch/_git-switch refs, _files paths, _git-remote remotes.
compdef does nothing until compinit has run.