During git-aware path completion we complete one path component at a time, i.e. 'git add <TAB>' offers only 'dir/' at first, not 'dir/subdir/file' right away, just like Bash's own filename completion. However, since both 'git ls-files' and 'git diff-index' dive deep into subdirectories, we have to strip all trailing path components from the listed paths, keeping only the leading path component. This stripping is currently done in a shell loop in __git_index_files(), which can take a significant amount of time when it has to iterate through a large number of paths. Replace this shell loop with a little 'awk' script using '/' as input field separator and printing the first field, which produces the same output much faster. Listing all tracked files (12) and directories (23) at the top of the worktree in linux.git (over 62k files), i.e. what's doing all the hard work behind 'git rm <TAB>': Before this patch, best of five, using GNU awk on Linux: $ time cur= __git_complete_index_file real 0m2.149s user 0m1.307s sys 0m1.086s After: real 0m0.067s user 0m0.089s sys 0m0.023s Difference: -96.9% Speedup: 32.1x Note that this could be done with 'sed', or even with 'cut', just as well, but the upcoming patches require 'awk's scriptability. Note also that this change means one more fork()+exec()ed process during path completion, adding more overhead especially on Windows, but a later patch will more than make up for it by eliminating two other processes in the same function. Signed-off-by: SZEDER Gábor <szeder.dev@xxxxxxxxx> --- contrib/completion/git-completion.bash | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 3948265d32..0abba88462 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -452,15 +452,12 @@ __git_ls_files_helper () # 3: List only paths matching this path component (optional). __git_index_files () { - local root="$2" match="$3" file + local root="$2" match="$3" __git_ls_files_helper "$root" "$1" "$match" | - while read -r file; do - case "$file" in - ?*/*) echo "${file%%/*}" ;; - *) echo "$file" ;; - esac - done | sort | uniq + awk -F / '{ + print $1 + }' | sort | uniq } # __git_complete_index_file requires 1 argument: -- 2.17.0.366.gbe216a3084