From 7a478b36aa1f202aed207a230def66563227f30e Mon Sep 17 00:00:00 2001 From: Chayoung You Date: Tue, 1 Jan 2019 23:05:08 +0900 Subject: [PATCH 1/2] zsh: complete unquoted paths with spaces correctly The following is the description of -Q flag of zsh compadd [1]: This flag instructs the completion code not to quote any metacharacters in the words when inserting them into the command line. Let's say there is a file named 'foo bar.txt' in repository, but it's not yet added to the repository. Then the following command triggers a completion: git add fo git add 'fo git add "fo The completion results in bash: git add foo\ bar.txt git add 'foo bar.txt' git add "foo bar.txt" While them in zsh: git add foo bar.txt git add 'foo bar.txt' git add "foo bar.txt" The first one, where the pathname is not enclosed in quotes, should escape the space with a backslash, just like bash completion does. Otherwise, this leads git to think there are two files; foo, and bar.txt. The main cause of this behavior is __gitcomp_file_direct(). The both implementions of bash and zsh are called with an argument 'foo bar.txt', but only bash adds a backslash before a space on command line. [1]: http://zsh.sourceforge.net/Doc/Release/Completion-Widgets.html Signed-off-by: Chayoung You Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 4 ++-- contrib/completion/git-completion.zsh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 9e8ec95c3c..816ee32805 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -2993,7 +2993,7 @@ if [[ -n ${ZSH_VERSION-} ]] && local IFS=$'\n' compset -P '*[=:]' - compadd -Q -f -- ${=1} && _ret=0 + compadd -f -- ${=1} && _ret=0 } __gitcomp_file () @@ -3002,7 +3002,7 @@ if [[ -n ${ZSH_VERSION-} ]] && local IFS=$'\n' compset -P '*[=:]' - compadd -Q -p "${2-}" -f -- ${=1} && _ret=0 + compadd -p "${2-}" -f -- ${=1} && _ret=0 } _git () diff --git a/contrib/completion/git-completion.zsh b/contrib/completion/git-completion.zsh index 049d6b80f6..886bf95d1f 100644 --- a/contrib/completion/git-completion.zsh +++ b/contrib/completion/git-completion.zsh @@ -99,7 +99,7 @@ __gitcomp_file_direct () local IFS=$'\n' compset -P '*[=:]' - compadd -Q -f -- ${=1} && _ret=0 + compadd -f -- ${=1} && _ret=0 } __gitcomp_file () @@ -108,7 +108,7 @@ __gitcomp_file () local IFS=$'\n' compset -P '*[=:]' - compadd -Q -p "${2-}" -f -- ${=1} && _ret=0 + compadd -p "${2-}" -f -- ${=1} && _ret=0 } __git_zsh_bash_func () From 6d54f528c74604b289ce7237cd30a1cfc5511f18 Mon Sep 17 00:00:00 2001 From: Chayoung You Date: Tue, 1 Jan 2019 23:05:09 +0900 Subject: [PATCH 2/2] 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 git show HEAD:ab 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 git show HEAD:'abc de git show HEAD:"abc de should uniquely complete 'abc def', but bash completes 'abc def' and 'abc deg' instead. In zsh, triggering a completion: git show HEAD:abc\ def/ 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\ 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/ So that the last space on command like should be removed on zsh to complete filenames under 'abc def/'. Signed-off-by: Chayoung You Signed-off-by: Junio C Hamano --- 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 816ee32805..26ea310fda 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 137fdc9bd5..94157e5879 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 ': 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 '