I track my dotfiles using this method:

  • A bare repository resides in $HOME/repos/dotfiles.
  • All my dotfiles reside in their normal location, e.g. $HOME/.vim/vimrc, not $HOME/repos/dotfiles/vimrc.
  • I run git --git-dir=$HOME/repos/dotfiles --work-tree=$HOME ... to manage things.

(Actually, I have a function g() that expands to the above command when I'm in $HOME, and to just git otherwise.)

Everything works great, except...

The problem: Zsh git filename completion doesn't work.


% pwd
% g status                                                                                                                                         ~
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   .vim/vimrc
        modified:   .xmonad/xmonad.hs

no changes added to commit (use "git add" and/or "git commit -a")
% g add <Tab>
Completing not a git repository

(The "Completing ..." stuff is due to zstyle ':completion:*' format $'%{\e[0;31m%}Completing %B%d%b%{\e[0m%}'.)

Notably, it would not suffice to somehow tell zsh's git completion to "move to/follow" the given value of --work-tree, as if git were being called from that directory, because explicitly doing that doesn't work either:

% cd repos/dotfiles
% g status
fatal: This operation must be run in a work tree
% g add <Tab>
Completing not a git repository

The question: Is there an easy way to get extend zsh's git completion to this kind of a case?

  • 2
    This is a great question -- exactly the same problem I'm having nearly 3 years later... The below answers don't seem to be working for me... Did you ever get any joy with this one?
    – Brad
    Commented Dec 19, 2019 at 12:33
  • 3
    No, I ended up switching to GNU Stow. Under this method, all of my actual dotfiles live in ~/repos/dotfiles, organized into various top-level directories, and then I use stow to create symlinks of the dotfiles to their appropriate location (determined by the subdirectory structure of each top-level directory). It's not as simple as my original method, but (i) the dotfiles directory is a normal git repo, so zsh completion works perfectly, and (ii) I can also store configurations for things beyond just home directory dotfiles, e.g. stuff in /etc. Commented Dec 20, 2019 at 13:40

2 Answers 2


Sadly, its a bug in zsh completion for git. You can find discussion on 'zsh' mailing list here.

Daniel Shahaf did provided a patch for '_git':

diff --git a/Completion/Unix/Command/_git b/Completion/Unix/Command/_git
index 518e6d198..45a0fa622 100644
--- a/Completion/Unix/Command/_git
+++ b/Completion/Unix/Command/_git
@@ -6609,20 +6609,33 @@ __git_files_relative () {
 (( $+functions[__git_files] )) ||
 __git_files () {
   local compadd_opts opts tag description gitcdup gitprefix files expl
+  local pref

   zparseopts -D -E -a compadd_opts V: J: 1 2 n f X: M: P: S: r: R: q F:
   zparseopts -D -E -a opts -- -cached -deleted -modified -others -ignored -unmerged -killed x+: --exclude+:
   tag=$1 description=$2; shift 2

-  gitcdup=$(_call_program gitcdup git rev-parse --show-cdup 2>/dev/null)
-  __git_command_successful $pipestatus || return 1
+  case $(_call_program gitinworktree git rev-parse --is-inside-work-tree 2>/dev/null) in
+    (true)
+      gitcdup=$(_call_program gitcdup git rev-parse --show-cdup 2>/dev/null)
+      __git_command_successful $pipestatus || return 1

-  gitprefix=$(_call_program gitprefix git rev-parse --show-prefix 2>/dev/null)
-  __git_command_successful $pipestatus || return 1
+      gitprefix=$(_call_program gitprefix git rev-parse --show-prefix 2>/dev/null)
+      __git_command_successful $pipestatus || return 1
+      local pref=$gitcdup$gitprefix$PREFIX
+      ;;
+    (false)
+      local pref=
+      ;;
+    (*)
+      # XXX what to do?
+      return 1
+      ;;
+  esac

   # TODO: --directory should probably be added to $opts when --others is given.

-  local pref=$gitcdup$gitprefix$PREFIX

   # First allow ls-files to pattern-match in case of remote repository
   files=(${(0)"$(_call_program files git ls-files -z --exclude-standard ${(q)opts} -- ${(q)${pref:+$pref\\\*}} 2>/dev/null)"})
@@ -7585,7 +7598,8 @@ _git() {
-       (( $+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_WORK_TREE=${(e)opt_args[--work-tree]}
        if ! _call_function ret _git-$words[1]; then
          if zstyle -T :completion:$curcontext: use-fallback; then
            _default && ret=0

It applies clearly to zsh 5.4.1 but it didn't work form me,YMMV.

I'll update this answer as the issue is being worked on.


With the above patch it does work, but where you put add is important - it has to be at the end:

git --git-dir=$HOME/.dotfiles --work-tree=$HOME/ add

One more thing worth noticing - its somewhat slow.

  • Thanks, but unfortunately the patch isn't working for me. I still get "not a git repository", even when taking care to put add at the end. I'll keep an eye on the mailing list, though, and hope that the issue gets satisfactorily resolved. Commented Aug 12, 2017 at 7:46

The lastest (2018-04-20) official git completion works well.


To install,

mkdir -p ~/.zsh && cd $_
wget https://raw.githubusercontent.com/git/git/master/contrib/completion/git-completion.bash
wget https://raw.githubusercontent.com/git/git/master/contrib/completion/git-completion.zsh
wget https://raw.githubusercontent.com/git/git/master/contrib/completion/git-prompt.sh

# The default behavior for 'git <TAB><TAB>' is to show a 10+ lines 'common commands' list.
# I prefer a full list.
# Better way to modify?
sed -i.bak '/^__git_zsh_cmd_common/,/{/ s/{/{\n\treturn/' _git

# add zshrc settings
vi ~/.zshrc
  fpath=(~/.zsh $fpath)
  # the next two lines are not needed if using oh-my-zsh
  autoload -Uz compinit


My previous answer is about oh-my-zsh's plugin gitfast. But it has bugs, and it's very outdated.

  • 1
    To make this work, I had to rename git-completion.zsh to _git and add zstyle ':completion:*:*:git:*' script ~/.zsh/git-completion.bash, as per comment at the top of `git-completion.zsh'.
    – Boris Bukh
    Commented May 1, 2020 at 2:02
  • Also, do not use master branch. Instead, make sure you get the version of the script that matches your git version. See apple.stackexchange.com/questions/327817/… for example of what might go wrong.
    – Boris Bukh
    Commented May 6, 2020 at 2:43

You must log in to answer this question.

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