I have the following

git reset HEAD^ half_entered_file_n<Tab>

At this point I would like the half_entered_file_name.txt to be Tab-completed.

My own investigation

I can get Tab completion to work if i write

git reset HEAD\^ ...

instead, escaping the "^".

Writing a "^" as the last character doesn't seem to affect autocomplete, unless HEAD is also present:

#autocomplete works
git reset RANDOM^ half_entered_file_n<Tab> #works

Zsh configuration

I am using Oh-my-zsh. On top of oh-my-zsh, i have configured the following which may be relevant:

# Let <TAB> auto completion add a slash at the end instead of space (like BASH)
zstyle ':completion:*' special-dirs true 

# Unless this option is set, you can't write git checkout HEAD^^ without escaping ^ as \^ in zsh
setopt NO_NOMATCH 

# Standard git plugins
plugins=(git git-extras)

For completeness, here is my configuration

I am running this on OSX in iTerm2.



I have found some semi-solutions, but none that lead to an "answered" status yet, which basically makes them non-solutions.

  1. set compdef -d git in .zshrc as proposed by "caveman"
    • Solves: Now HEAD^ no longer breaks file autocompletion.
    • Drawback: git command autocompletion doesn't work anymore.
  2. Use the accepted answer by "ralphtheninja" from this question
    • Solves: Should replace the way the file completion list is generated, which would solve the problem.
    • Drawback: Doesn't work. It seems git-completion.zsh/.bash has changed syntax.

I don't know shell scripting well enough to understand exactly what is going on in git-completion.zsh/.bash, and if that is the reason things are breaking.


Why you shouldn't assume

For the longest time I was assuming that the git completion in my zsh configuration was coming from /usr/local/share/git-core/contrib/completion/, specifically from git-completion.zsh or even git-completion.bash. I guess I was assuming this because most searches will yield results talking about these files. However I didn't explicitly include any of these files and for the longest time I was just guessing oh-my-zsh was including them.


We're not in Kansas anymore

It wasn't until I used zsh method tracing, enabled via setopt xtrace, that I realized (by googling some method names) that the script being used was in fact /usr/local/share/zsh/functions/_git. I had previously used XCode Instruments.app to monitor which scripts were being accessed on the file system, but didn't figure it out then (the output is quite chatty, and shows accesses from other apps as well).

What's going on?

The traces showed this (partial traces ahead!)

git reset HEAD <TAB>
+__git_tree_files:17> tree=HEAD
+__git_tree_files:18> tree_files+=+__git_tree_files:18> _call_program tree-files git ls-tree --name-only -z HEAD ./
+__git_tree_files:18> tree_files+=( first second third )


git reset HEAD^ <TAB>
+__git_tree_files:17> tree=HEAD^
+__git_tree_files:18> tree_files+=+__git_tree_files:18> _call_program tree-files git ls-tree --name-only -z 'HEAD^' ./
+__git_tree_files:18> tree_files+=( )

That empty tree_files looks suspicious.

On line 6058 in /usr/local/share/zsh/functions/_git we find

tree_files+=(${(ps:\0:)"$(_call_program tree-files git ls-tree $extra_args --name-only -z $tree $Path 2>/dev/null)"})

We definitely need to escape that $tree variable. Said and done:

tree_files+=(${(ps:\0:)"$(_call_program tree-files git ls-tree $extra_args --name-only -z ${(q)tree} $Path 2>/dev/null)"}) 


There are some more bugs going on in this script (e.g. the file list for git reset <tree-ish> is based on the <tree-ish>, when it should be based on HEAD. Now I know where to fix them!


There is also the possibility of running the completion scripts from git instead. You can do that by following this answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.