diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 69ef33b56b..23e4c90890 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -2988,7 +2988,7 @@ _git_show_branch () __gitcomp_directories () { - local _tmp_dir _tmp_completions + 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 @@ -2996,20 +2996,18 @@ __gitcomp_directories () [[ "$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 + # 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") + COMPREPLY+=("$c/") + _found=1 fi - done - elif [[ "$cur" =~ /$ ]]; then + 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 " diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh index f309c7532e..96dbda97cf 100755 --- a/t/t9902-completion.sh +++ b/t/t9902-completion.sh @@ -1505,6 +1505,55 @@ test_expect_success 'cone mode sparse-checkout completes directory names' ' ) ' +test_expect_success 'cone mode sparse-checkout completes directory names with spaces and accents' ' + # reset sparse-checkout + git -C sparse-checkout sparse-checkout disable && + ( + cd sparse-checkout && + mkdir "directory with spaces" && + mkdir "directory-with-áccent" && + >"directory with spaces/randomfile" && + >"directory-with-áccent/randomfile" && + git add . && + git commit -m "Add directory with spaces and directory with accent" && + git sparse-checkout set --cone "directory with spaces" \ + "directory-with-áccent" && + test_completion "git sparse-checkout add dir" <<-\EOF && + directory with spaces/ + directory-with-áccent/ + EOF + rm -rf "directory with spaces" && + rm -rf "directory-with-áccent" && + git add . && + git commit -m "Remove directory with spaces and directory with accent" + ) +' + +# use FUNNYNAMES to avoid running on Windows, which doesn't permit backslashes or tabs in paths +test_expect_success FUNNYNAMES 'cone mode sparse-checkout completes directory names with backslashes and tabs' ' + # reset sparse-checkout + git -C sparse-checkout sparse-checkout disable && + ( + cd sparse-checkout && + mkdir "directory\with\backslashes" && + mkdir "$(printf "directory\twith\ttabs")" && + >"directory\with\backslashes/randomfile" && + >"$(printf "directory\twith\ttabs")/randomfile" && + git add . && + git commit -m "Add directory with backslashes and directory with tabs" && + git sparse-checkout set --cone "directory\with\backslashes" \ + "$(printf "directory\twith\ttabs")" && + test_completion "git sparse-checkout add dir" <<-\EOF && + directory\with\backslashes/ + directory with tabs/ + EOF + rm -rf "directory\with\backslashes" && + rm -rf "$(printf "directory\twith\ttabs")" && + git add . && + git commit -m "Remove directory with backslashes and directory with tabs" + ) +' + 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 &&