I have this in my .zshrc:

# command completion
autoload -Uz compinit

usually letting me tab through commands, args, and files.

But with git --git-dir=<the path> --work-tree="$HOME" (usually aliased) it only works for sub-commands (like add and push) and args, not files. I'd like it to work with files too.

I tried putting it in a script like so:

#!/usr/bin/env sh

git --git-dir=<the path> --work-tree="$HOME" "$@"

and it works for files but then sub-commands and args break.


1 Answer 1


If --git-dir is present on the command line, the completion code recognizes it and takes it into account when looking for things to complete, if your zsh is recent enough (≥5.3). It does so by passing the argument to --git-dir as the environment variable GIT_DIR. For example, to complete remote names after git fetch, zsh runs git remote. After git --git-dir=/some/where, zsh runs git remote with GIT_DIR=/some/where in the environment. So this part does work.

Unfortunately, as of the current zsh version (5.7.1+), it passes the unexpanded argument. So if you write something like git --git-dir=~/repositories/foo.git, zsh passes GIT_DIR=~/repositories/foo.git, not GIT_DIR=/home/aaa/repositories/foo.git.

The zsh completion code does not have similar support for --work-tree.

I think you can fix the expansion issue with --git-dir and add support for --git-worktree by editing the file _git and replacing the line

        (( $+opt_args[--git-dir] )) && local -x GIT_DIR=$opt_args[--git-dir]


        (( $+opt_args[--git-dir] )) && local -x GIT_DIR=${(e)~opt_args[--git-dir]}
        (( $+opt_args[--work-tree] )) && local -x GIT_WORKTREE=${(e)~opt_args[--work-tree]}

I haven't tested this.

I'm not sufficiently motivated to post to the zsh mailing list, but please do: this (or a working version of it, if it doesn't work) would be a useful patch.

If you put --git-dir or --work-tree in an alias, this is transparent: zsh expands aliases before doing completion (assuming you haven't turned off the option complete_aliases). If you put them in a function or in a script, this is opaque: zsh won't see them, and won't even know that the function or script calls git unless you tell it.

If you can't get this patch to work or you don't want to maintain your own version of _git, you can work around it by defining a custom command with a custom completion function that wraps around git.

gitx () {
  git --git-dir=/some/where --work-tree=/else/where "$@"
_gitx () {
  local -x GIT_DIR=/some/where GIT_WORKTREE=/else/where
  _git "$@"
compdef _gitx gitx

If you have multiple wrappers, you'll need a completion function for each one. Alternatively, you can add some logic to the completion function to figure out what value of GIT_DIR and GIT_WORKTREE to use. The name of the function is in $service.

  • Note that echo --git-dir=~ outputs --git-dir=~ unless you have the magic_equal_subst option enabled. IOW, that ~ is not meant to be expanded (by default) Nov 6, 2019 at 7:45
  • @StéphaneChazelas True, but if you want to treat this accurately, you have to distinguish between --git-dir=~ which refers to a directory called ~ if magic_equal_subst is off from --git-dir ~ which always refers to the home directory. Nov 6, 2019 at 7:52
  • Note that ${(e)var} turns '$0' into 'zsh' for instance, and $~var * into the list of files in the current directory. eval "GIT_DIR=$opt_args[--git-dir]" would probably be slightly better, but would still expand command substitutions for instance (which as a use I wouldn't expect here). Nov 6, 2019 at 7:56
  • @StéphaneChazelas Wouldn't eval crap out on word separators? Spaces are plausible; <>| and such are unlikely but dangerous. I'd like something close to expand-word, but I don't know if there's enough information to do this accurately inside a completion function. Nov 6, 2019 at 8:03
  • If $opt_args[--git-dir] is meant to be the expanded token following --git-dir, I don't expect it would contain unquoted blanks. Nov 6, 2019 at 8:22

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .