Subject: [PATCH v3 2/2] completion: treat results of git ls-tree as file paths

From: Chayoung You <[email-masked]>
Date: Tue, 1 Jan 2019 23:05:09 +0900

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 <[email-masked]>
---
 contrib/completion/git-completion.bash | 31 ++++++++++----------------
 t/t9902-completion.sh                  | 10 ++++-----
 2 files changed, 17 insertions(+), 24 deletions(-)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 816ee3280..26ea310fd 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -855,7 +855,7 @@ __git_compute_merge_strategies ()
 
 __git_complete_revlist_file ()
 {
-	local pfx ls ref cur_="$cur"
+	local dequoted_word pfx ls ref cur_="$cur"
 	case "$cur_" in
 	*..?*:*)
 		return
@@ -863,14 +863,18 @@ __git_complete_revlist_file ()
 	?*:*)
 		ref="${cur_%%:*}"
 		cur_="${cur_#*:}"
-		case "$cur_" in
+
+		__git_dequote "$cur_"
+
+		case "$dequoted_word" in
 		?*/*)
-			pfx="${cur_%/*}"
-			cur_="${cur_##*/}"
+			pfx="${dequoted_word%/*}"
+			cur_="${dequoted_word##*/}"
 			ls="$ref:$pfx"
 			pfx="$pfx/"
 			;;
 		*)
+			cur_="$dequoted_word"
 			ls="$ref"
 			;;
 		esac
@@ -880,21 +884,10 @@ __git_complete_revlist_file ()
 		*)   pfx="$ref:$pfx" ;;
 		esac
 
-		__gitcomp_nl "$(__git ls-tree "$ls" \
-				| sed '/^100... blob /{
-				           s,^.*	,,
-				           s,$, ,
-				       }
-				       /^120000 blob /{
-				           s,^.*	,,
-				           s,$, ,
-				       }
-				       /^040000 tree /{
-				           s,^.*	,,
-				           s,$,/,
-				       }
-				       s/^.*	//')" \
-			"$pfx" "$cur_" ""
+		__gitcomp_file "$(__git ls-tree "$ls" \
+				| sed 's/^.*	//
+				       s/$//')" \
+			"$pfx" "$cur_"
 		;;
 	*...*)
 		pfx="${cur_%...*}..."
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 137fdc9bd..94157e587 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -1515,8 +1515,8 @@ test_expect_success 'show completes all refs' '
 
 test_expect_success '<ref>: completes paths' '
 	test_completion "git show mytag:f" <<-\EOF
-	file1 Z
-	file2 Z
+	file1Z
+	file2Z
 	EOF
 '
 
@@ -1525,7 +1525,7 @@ test_expect_success 'complete tree filename with spaces' '
 	git add "name with spaces" &&
 	git commit -m spaces &&
 	test_completion "git show HEAD:nam" <<-\EOF
-	name with spaces Z
+	name with spacesZ
 	EOF
 '
 
@@ -1534,8 +1534,8 @@ test_expect_success 'complete tree filename with metacharacters' '
 	git add "name with \${meta}" &&
 	git commit -m meta &&
 	test_completion "git show HEAD:nam" <<-\EOF
-	name with ${meta} Z
-	name with spaces Z
+	name with ${meta}Z
+	name with spacesZ
 	EOF
 '
 
-- 
2.17.1