Search code examples
gitshellzshtab-completioniterm2

Tab completion freezes for Git commands only


I have some strange behavior regarding my setup that I can't seem to narrow down.

I am using tab completion in my shell without any issues (my shell is zsh). The issue I'm having is regarding tab completion after issuing a git command.

Example 1 (works fine):

I make a new directory, change into it and git init. I then touch hello.rb. If I do git add <tab> it will change it to git add hello.rb.

Example 2 (doesn't work):

I'm in a rails app that really isn't very big, and if I try to run git add G<tab> with the intent that it will pull up my Gemfile, it just hangs and hangs until I kill it with ctrl-c which outputs:

Killed by signal in __git_complete_index_file after 159s

In zsh I'm using:

# completion
autoload -U compinit
compinit

Has anyone else had this issue? I can work around it but I have to be doing something wrong and I'm unsure where else to look.

Versions of things:

git version 2.1.2
zsh 5.0.7
iTerm2 Build 2.0.0.20141103

Update:

Git v 2.2.0 has fixed this issue so just upgrade if you're running into this issue.


Solution

  • I'm assuming you are using RVM or some tool like that.

    There is a bug in the git-completion.bash shipped with the current git (2.1.3) and older versions, causing an endless loop when listing file completions in directories where RVM is used.

    The reason for this endless loop is a change of chpwd_functions, made by RVM and some other tools.

    I've found a patch for the git-comletion.bash affecting only the __git_ls_files_helper method which is used for listing files. The patch ignores the chpwd_functions and hence, these endless loops are omitted.

    In short: The __git_ls_files_helper function needs to be changed from:

    __git_ls_files_helper ()
    {
      (
        test -n "${CDPATH+set}" && unset CDPATH
        cd "$1"
        if [ "$2" == "--committable" ]; then
          git diff-index --name-only --relative HEAD
        else
          # NOTE: $2 is not quoted in order to support multiple options
          git ls-files --exclude-standard $2
        fi
       ) 2>/dev/null
    }
    

    to:

    __git_ls_files_helper ()
    {
      (
        test -n "${CDPATH+set}" && unset CDPATH
        (( ${+functions[chpwd]} )) && unfunction chpwd
        (( ${#chpwd_functions} )) && chpwd_functions=()
        setopt chaselinks
        builtin cd "$1" 2>/dev/null
        if [ "$2" == "--committable" ]; then
          git diff-index --name-only --relative HEAD
        else
          # NOTE: $2 is not quoted in order to support multiple options
          git ls-files --exclude-standard $2
        fi
      ) 2>/dev/null
    }
    

    Further information can be found in the RVM issue discussion on Github. The location of your git-completion.bash depends on how you have installed git. When using Homebrew, the location is something like

    /usr/local/Cellar/git/<git version>/etc/bash_completion.d/
    

    on other systems, or when using other package managers, it usually should be something like

    /opt/local/etc/bash_completion.d
    

    For further information about the git-completion.bash, take a look at the Git Tips and Tricks, chapter 2.7 in the git-scm.com book.

    Update:

    Git v 2.2.0 has fixed this issue so just upgrade if you're running into this issue.