I’ve used Git since pretty much day one, and I use it all the time, so it’s important to me that it’s easy to type Git commands quickly and efficiently. I use zsh, which I believe is way superior to bash, unfortunately I found many issues with its Git completion.

In this blog post I will try to guide you through the ordeal from how I identified a problem, and how I ended up fixing it years after, for everyone’s benefit.

The issue

I work on the Linux (kernel) source tree from time to time, and I noticed that sometimes completion took a long, looong time. Specifically, I found that typing ‘git show v’ took several seconds to complete.

I decided to bring that issue up to the zsh developers, and it caused a lot of fuzz. I won’t go to every detail of the discussion, but long story short; they were not going to fix the issue because of their uncompromising principles; correctness over functionality, even if very few people use that correctness, and the functionality is almost completely broken to the point the completion is not usable in certain cases. I argued that completion is meant to make typing commands more efficient, and if completing a command takes longer than what it would have taken me to type it manually, the completion is failing its purpose. I thought any sane person would see the problem with that, but apparently I was wrong (or was I?).

Fortunately zsh has bash completion emulation, so it’s possible to use Git’s official bash completion in zsh. You loose some of the features of zsh completion, but it works very efficiently (‘git show v’ was instantaneous).

Unfortunately, zsh’s bash emulation, and zsh’ bash completion emulation (two different things), are not perfect, so some workarounds were needed in Git’s bash completion script, and those workarounds were not working properly by the time I started to use such completion, so that’s when my involvement begin.

Fixing the bridge

Each time I found a bug, I tried to fix it in Git (patch), and made sure that zsh folks fixed in their side too (commit), so eventually no workarounds would be needed, and everything would work correctly.

The completion worked for the most part, but with workarounds, and not exactly as good as bash’s. So I decided to fix zsh’s bash completion emulation once and for all. After my patches were applied by zsh developers, Git’s official completion worked much closer to how it did in bash, but there were still minor issues.

Moreover, Git’s bash completion was constantly changing, and it was only a matter of time before one change broke zsh’s completion, so I decided to get involved, understand the code and simplify it to minimize the possibility (e.g. d79f81a, 583e4d5). I saw a lot of areas of improvement, but in order to make sure nothing got broken in the process of simplification, I thought it would make sense to have some tests (5c293a6). Git’s testing framework is one of the most powerful and simple there is, so it was a pleasure to write those tests. Eventually the completion tests were good enough that I became confident in changing a lot of the completion code.

At the same time I realized most of zsh’s bash completion emulation code was not needed at all, so I wrote a very small version of it that only worked with Git’s completion. The result was very simple, and it worked perfectly, yet it could be even simpler, if only I could simplify Git’s completion even more.

The culmination of that work was the creation of __git_complete (6b179ad), a helper that has nothing to do with zsh, but it solved a long standing problem with Git completion and aliases. It’s not worth going into details about what was the problem, and why it received so much push-back from Git developers (mostly because of naming issues), what is important is that I implemented it with a wrapper function, a wrapper function that was *exactly* what my zsh simple completion wrapper needed.

Now that everything was in place, the final wrapper script ended up very small and simple (c940786), it didn’t have any of the bugs zsh’s bash completion emulation had, and was under full control of the Git project, so it could be improved later on.

Finally. I had Git completion in zsh that worked *perfectly*; it worked exactly the same as it did on bash. But that was not enough.

Now that Git completion worked just like in bash, it was time to implement some extras. zsh completion is extremely powerful, and does things bash cannot even dream of doing, and with my custom wrapper, it was possible to have the best of both worlds, and that’s exactly what I decided to do (4911589).

Finally

So there it is, after years of work, several hundreds of mails, tons of patches through different iterations… Git now has nice zsh completion that not only works as efficiently as in bash without any difference, but in fact it even has more features.

If you want to give it a try, just follow the instructions: contrib/completion/git-completion.zsh

😉

Felipe Contreras (54): git-completion: fix regression in zsh support git-completion: workaround zsh COMPREPLY bug completion: work around zsh option propagation bug completion: use ls -1 instead of rolling a loop to do that ourselves completion: simplify __gitcomp and __gitcomp_nl implementations tests: add initial bash completion tests completion: simplify __gitcomp_1 completion: simplify by using $prev completion: add missing general options completion: simplify __git_complete_revlist_file completion: add new __git_complete helper completion: rename internal helpers _git and _gitk completion: add support for backwards compatibility completion: remove executable mode completion: split __git_ps1 into a separate script completion: fix shell expansion of items completion: add format-patch options to send-email completion: add comment for test_completion() completion: standardize final space marker in tests completion: simplify tests using test_completion_long() completion: consolidate test_completion*() tests completion: refactor __gitcomp related tests completion: simplify __gitcomp() test helper completion: add new zsh completion completion: start moving to the new zsh completion completion: fix warning for zsh completion: add more cherry-pick options completion: trivial test improvement completion: get rid of empty COMPREPLY assignments completion: add new __gitcompadd helper completion: add __gitcomp_nl tests completion: get rid of compgen completion: inline __gitcomp_1 to its sole callsite completion: small optimization prompt: fix untracked files for zsh completion: add file completion tests completion: document tilde expansion failure in tests completion; remove unuseful comments completion: use __gitcompadd for __gitcomp_file completion: refactor diff_index wrappers completion: refactor __git_complete_index_file() completion: add hack to enable file mode in bash < 4 completion: add space after completed filename completion: remove __git_index_file_list_filter() completion: add missing format-patch options complete: zsh: trivial simplification complete: zsh: use zsh completion for the main cmd completion: zsh: don't override suffix on _detault completion: cleanup zsh wrapper completion: synchronize zsh wrapper completion: regression fix for zsh prompt: fix for simple rebase completion: zsh: improve bash script loading completion: avoid ls-remote in certain scenarios