Optimising tig's bash completion by a factor of 1000

Has this ever happened to you?

~/linux.git (master) $ tig log v5.4-<tab>
[swearing]
[fetching another cup of tea]
[some more swearing]
[coworkers looking funny at you]
[1 minute later]
~/linux.git (master) $ tig log v5.4-rc1

Until yesterday, the bash completion for tig, the ncurses git viewer, was horribly slow in large Git repositories. This mostly stemmed from the fact that it was forked off from the upstream git bash completion a few years ago, and never updated since. Back then, the git bash completion itself was slow in those cases, but since then upstream had more eyeballs and people power to optimise its completion for those large repos, like the linux repo.

The problem gets worse once you consider that bash completions can not be aborted by a Ctrl-C, which means that your current shell is basically unusable for a minute while you wait for the completion to complete. On a heavily loaded build server, the runtime can easily exceed 2 minutes. Add to that the frustration when you realise you shouldn't have accidentally pressed Tab…

The corresponding bug report for tig has been existing since March 2018, and that tab has been open in my browser for some time now… so let's see how to solve this. (A quick look on the corresponding xkcd shows I can spend about 4 hours on that topic… :-))

A preliminary evaluation already shows how much faster we can be. Compare the runtime of tig's file completion function with the one from git:

linux.git (master) $ time __tig_complete_file

real    0m29.363s
user    0m27.805s
sys     0m1.600s

linux.git (master) $ time __git_complete_file

real    0m0.063s
user    0m0.051s
sys     0m0.012s

Holy cow, that's already a factor of 466! And these times are mostly reproducible, so caching issues can be ruled out.

Since tig takes basically the same command line options as git-log and git-diff, the easiest solution is to reuse git's own completion here, so that's what I did in Pull Request #960. With that, we also don't need to fix tig's completion whenever git commands get shiny new options, and we also pull in all other optimisations from git's completion.

That being said, here is a comparison of the old and new version of the full completion function in an extreme case, showing a speedup of 1863:

linux.git (master) $ uptime
 16:45:52 up 36 days,  3:33, 224 users,  load average: 24.17, 38.87, 31.21

# The old completion:
linux.git (master) $ time COMP_WORDS=("tig log") COMP_CWORD=2 _tig

real    2m1.145s
user    1m40.379s
sys     0m1.347s

# The new completion:
linux.git (master) $ time COMP_WORDS=("tig log") COMP_CWORD=2 __git_wrap_tig

real    0m0.127s
user    0m0.085s
sys     0m0.024s

Further Readings

umpf - Git on a New Level

Modern software development is commonly accompanied by a version control system like Git in order to have changes to the source code being documented in a traceable manner. Furthermore, any version state should be easily reproducible at any time. However, for work on complex projects such as the BSP ("Board Support Package") of an embedded system with its multiple development aspects, the simple stacking of the individual changes on top of each other does not scale.