From: Lessley Dennington <lessleydennington@xxxxxxxxx> Update the __gitcomp_directories method to de-quote and handle unusual characters in directory names. Although this initially involved an attempt to re-use the logic in __git_index_files, this method removed subdirectories (e.g. folder1/0/ became folder1/), so instead new custom logic was placed directly in the __gitcomp_directories method. Co-authored-by: Johannes Schindelin <johannes.schindelin@xxxxxx> Co-authored-by: Lessley Dennington <lessleydennington@xxxxxxxxx> Signed-off-by: Lessley Dennington <lessleydennington@xxxxxxxxx> --- contrib/completion/git-completion.bash | 50 +++++++++++++------------- t/t9902-completion.sh | 28 +++++++++++++++ 2 files changed, 52 insertions(+), 26 deletions(-) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index c5c8df6b6e5..c47e9ce09b2 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -2988,32 +2988,30 @@ _git_show_branch () __gitcomp_directories () { - local _tmp_dir _tmp_completions - - # Get the directory of the current token; this differs from dirname - # in that it keeps up to the final trailing slash. If no slash found - # that's fine too. - [[ "$cur" =~ .*/ ]] - _tmp_dir=$BASH_REMATCH - - # Find possible directory completions, adding trailing '/' characters - _tmp_completions="$(git ls-tree -d --name-only HEAD $_tmp_dir | - sed -e s%$%/%)" - - if [[ -n "$_tmp_completions" ]]; then - # There were some directory completions, so find ones that - # start with "$cur", the current token, and put those in COMPREPLY - local i=0 c IFS=$' \t\n' - for c in $_tmp_completions; do - if [[ $c == "$cur"* ]]; then - COMPREPLY+=("$c") - fi - done - elif [[ "$cur" =~ /$ ]]; then - # No possible further completions any deeper, so assume we're at - # a leaf directory and just consider it complete - __gitcomp_direct_append "$cur " - fi + local _tmp_dir _tmp_completions _found=0 + + # Get the directory of the current token; this differs from dirname + # in that it keeps up to the final trailing slash. If no slash found + # that's fine too. + [[ "$cur" =~ .*/ ]] + _tmp_dir=$BASH_REMATCH + + # Find possible directory completions, adding trailing '/' characters, + # de-quoting, and handling unusual characters. + while IFS= read -r -d $'\0' c ; do + # If there are directory completions, find ones that start + # with "$cur", the current token, and put those in COMPREPLY + if [[ $c == "$cur"* ]]; then + COMPREPLY+=("$c/") + _found=1 + fi + done < <(git ls-tree -z -d --name-only HEAD $_tmp_dir) + + if [[ $_found == 0 ]] && [[ "$cur" =~ /$ ]]; then + # No possible further completions any deeper, so assume we're at + # a leaf directory and just consider it complete + __gitcomp_direct_append "$cur " + fi } _git_sparse_checkout () diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh index b38a7302249..7f63d6057be 100755 --- a/t/t9902-completion.sh +++ b/t/t9902-completion.sh @@ -1508,6 +1508,34 @@ test_expect_success 'cone mode sparse-checkout completes directory names' ' ) ' +# use FUNNYNAMES to avoid running on Windows, which doesn't permit backslashes in paths +test_expect_success FUNNYNAMES 'cone mode sparse-checkout completes directory names with special characters' ' + # reset sparse-checkout + git -C sparse-checkout sparse-checkout disable && + ( + cd sparse-checkout && + mkdir "directory with spaces" && + mkdir "$(printf "directory\twith\ttabs")" && + mkdir "directory\with\backslashes" && + mkdir "directory-with-áccent" && + >"directory with spaces/randomfile" && + >"$(printf "directory\twith\ttabs")/randomfile" && + >"directory\with\backslashes/randomfile" && + >"directory-with-áccent/randomfile" && + git add . && + git commit -m "Add directories containing unusual characters" && + git sparse-checkout set --cone "directory with spaces" \ + "$(printf "directory\twith\ttabs")" "directory\with\backslashes" \ + "directory-with-áccent" && + test_completion "git sparse-checkout add dir" <<-\EOF + directory with spaces/ + directory with tabs/ + directory\with\backslashes/ + directory-with-áccent/ + EOF + ) +' + test_expect_success 'non-cone mode sparse-checkout uses bash completion' ' # reset sparse-checkout repo to non-cone mode git -C sparse-checkout sparse-checkout disable && -- gitgitgadget