completion: treat results of git ls-tree as file paths

Let's say there are files named 'foo bar.txt', and 'abc def/test.txt' in repository. When following commands trigger a completion: git show HEAD:fo<Tab> git show HEAD:ab<Tab> The completion results in bash/zsh: git show HEAD:foo bar.txt git show HEAD:abc def/ Where the both of them have an unescaped space in paths, so they'll be misread by git. All entries of git ls-tree either a filename or a directory, so __gitcomp_file() is proper rather than __gitcomp_nl(). Note the commit f12785a3, which handles quoted paths properly. Like this case, we should dequote $cur_ for ?*:* case. For example, let's say there is untracked directory 'abc deg', then trigger a completion: git show HEAD:abc\ de<Tab> git show HEAD:'abc de<Tab> git show HEAD:"abc de<Tab> should uniquely complete 'abc def', but bash completes 'abc def' and 'abc deg' instead. In zsh, triggering a completion: git show HEAD:abc\ def/<Tab> should complete 'test.txt', but nothing comes. The both problems will be resolved by dequoting paths. __git_complete_revlist_file() passes arguments to __gitcomp_nl() where the first one is a list something like: abc def/Z foo bar.txt Z where Z is the mark of the EOL. - The trailing space of blob in __git ls-tree | sed. It makes the completion results become: git show HEAD:foo\ bar.txt\ <CURSOR> So git will try to find a file named 'foo bar.txt ' instead. - The trailing slash of tree in __git ls-tree | sed. It makes the completion results on zsh become: git show HEAD:abc\ def/ <CURSOR> So that the last space on command like should be removed on zsh to complete filenames under 'abc def/'. Signed-off-by: Chayoung You <yousbe@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Chayoung You committed Jan 1, 2019 at 23:05 UTC 6d54f528c74604b289ce7237cd30a1cfc5511f18
2 files changed +17 -24
contrib/completion/git-completion.bash
+12 -19
@@ -855,7 +855,7 @@ __git_compute_merge_strategies ()
855
856 __git_complete_revlist_file ()
857 {
858 - local pfx ls ref cur_="$cur"
858 + local dequoted_word pfx ls ref cur_="$cur"
859 case "$cur_" in
860 *..?*:*)
861 return
@@ -863,14 +863,18 @@ __git_complete_revlist_file ()
863 ?*:*)
864 ref="${cur_%%:*}"
865 cur_="${cur_#*:}"
866 - case "$cur_" in
866 +
867 + __git_dequote "$cur_"
868 +
869 + case "$dequoted_word" in
870 ?*/*)
868 - pfx="${cur_%/*}"
869 - cur_="${cur_##*/}"
871 + pfx="${dequoted_word%/*}"
872 + cur_="${dequoted_word##*/}"
873 ls="$ref:$pfx"
874 pfx="$pfx/"
875 ;;
876 *)
877 + cur_="$dequoted_word"
878 ls="$ref"
879 ;;
880 esac
@@ -880,21 +884,10 @@ __git_complete_revlist_file ()
884 *) pfx="$ref:$pfx" ;;
885 esac
886
883 - __gitcomp_nl "$(__git ls-tree "$ls" \
884 - | sed '/^100... blob /{
885 - s,^.* ,,
886 - s,$, ,
887 - }
888 - /^120000 blob /{
889 - s,^.* ,,
890 - s,$, ,
891 - }
892 - /^040000 tree /{
893 - s,^.* ,,
894 - s,$,/,
895 - }
896 - s/^.* //')" \
897 - "$pfx" "$cur_" ""
887 + __gitcomp_file "$(__git ls-tree "$ls" \
888 + | sed 's/^.* //
889 + s/$//')" \
890 + "$pfx" "$cur_"
891 ;;
892 *...*)
893 pfx="${cur_%...*}..."
t/t9902-completion.sh
+5 -5
@@ -1515,8 +1515,8 @@ test_expect_success 'show completes all refs' '
1515
1516 test_expect_success '<ref>: completes paths' '
1517 test_completion "git show mytag:f" <<-\EOF
1518 - file1 Z
1519 - file2 Z
1518 + file1Z
1519 + file2Z
1520 EOF
1521 '
1522
@@ -1525,7 +1525,7 @@ test_expect_success 'complete tree filename with spaces' '
1525 git add "name with spaces" &&
1526 git commit -m spaces &&
1527 test_completion "git show HEAD:nam" <<-\EOF
1528 - name with spaces Z
1528 + name with spacesZ
1529 EOF
1530 '
1531
@@ -1534,8 +1534,8 @@ test_expect_success 'complete tree filename with metacharacters' '
1534 git add "name with \${meta}" &&
1535 git commit -m meta &&
1536 test_completion "git show HEAD:nam" <<-\EOF
1537 - name with ${meta} Z
1538 - name with spaces Z
1537 + name with ${meta}Z
1538 + name with spacesZ
1539 EOF
1540 '
1541