diff options
-rw-r--r-- | contrib/completion/git-completion.bash | 76 | ||||
-rwxr-xr-x | t/t9902-completion.sh | 46 |
2 files changed, 116 insertions, 6 deletions
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 2a8fe2aa18..cdcf8b9c37 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -94,6 +94,70 @@ __git () ${__git_dir:+--git-dir="$__git_dir"} "$@" 2>/dev/null } +# Removes backslash escaping, single quotes and double quotes from a word, +# stores the result in the variable $dequoted_word. +# 1: The word to dequote. +__git_dequote () +{ + local rest="$1" len ch + + dequoted_word="" + + while test -n "$rest"; do + len=${#dequoted_word} + dequoted_word="$dequoted_word${rest%%[\\\'\"]*}" + rest="${rest:$((${#dequoted_word}-$len))}" + + case "${rest:0:1}" in + \\) + ch="${rest:1:1}" + case "$ch" in + $'\n') + ;; + *) + dequoted_word="$dequoted_word$ch" + ;; + esac + rest="${rest:2}" + ;; + \') + rest="${rest:1}" + len=${#dequoted_word} + dequoted_word="$dequoted_word${rest%%\'*}" + rest="${rest:$((${#dequoted_word}-$len+1))}" + ;; + \") + rest="${rest:1}" + while test -n "$rest" ; do + len=${#dequoted_word} + dequoted_word="$dequoted_word${rest%%[\\\"]*}" + rest="${rest:$((${#dequoted_word}-$len))}" + case "${rest:0:1}" in + \\) + ch="${rest:1:1}" + case "$ch" in + \"|\\|\$|\`) + dequoted_word="$dequoted_word$ch" + ;; + $'\n') + ;; + *) + dequoted_word="$dequoted_word\\$ch" + ;; + esac + rest="${rest:2}" + ;; + \") + rest="${rest:1}" + break + ;; + esac + done + ;; + esac + done +} + # The following function is based on code from: # # bash_completion - programmable completion functions for bash 3.2+ @@ -406,13 +470,17 @@ __git_index_files () # The exception is --committable, which finds the files appropriate commit. __git_complete_index_file () { - local pfx="" cur_="$cur" + local dequoted_word pfx="" cur_ - case "$cur_" in + __git_dequote "$cur" + + case "$dequoted_word" in ?*/*) - pfx="${cur_%/*}/" - cur_="${cur_##*/}" + pfx="${dequoted_word%/*}/" + cur_="${dequoted_word##*/}" ;; + *) + cur_="$dequoted_word" esac __gitcomp_file "$(__git_index_files "$1" "$pfx")" "$pfx" "$cur_" diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh index f7d7becbb9..f8fcedae2c 100755 --- a/t/t9902-completion.sh +++ b/t/t9902-completion.sh @@ -400,6 +400,46 @@ test_expect_success '__gitdir - remote as argument' ' test_cmp expected "$actual" ' + +test_expect_success '__git_dequote - plain unquoted word' ' + __git_dequote unquoted-word && + verbose test unquoted-word = "$dequoted_word" +' + +# input: b\a\c\k\'\\\"s\l\a\s\h\es +# expected: back'\"slashes +test_expect_success '__git_dequote - backslash escaped' ' + __git_dequote "b\a\c\k\\'\''\\\\\\\"s\l\a\s\h\es" && + verbose test "back'\''\\\"slashes" = "$dequoted_word" +' + +# input: sin'gle\' '"quo'ted +# expected: single\ "quoted +test_expect_success '__git_dequote - single quoted' ' + __git_dequote "'"sin'gle\\\\' '\\\"quo'ted"'" && + verbose test '\''single\ "quoted'\'' = "$dequoted_word" +' + +# input: dou"ble\\" "\"\quot"ed +# expected: double\ "\quoted +test_expect_success '__git_dequote - double quoted' ' + __git_dequote '\''dou"ble\\" "\"\quot"ed'\'' && + verbose test '\''double\ "\quoted'\'' = "$dequoted_word" +' + +# input: 'open single quote +test_expect_success '__git_dequote - open single quote' ' + __git_dequote "'\''open single quote" && + verbose test "open single quote" = "$dequoted_word" +' + +# input: "open double quote +test_expect_success '__git_dequote - open double quote' ' + __git_dequote "\"open double quote" && + verbose test "open double quote" = "$dequoted_word" +' + + test_expect_success '__gitcomp_direct - puts everything into COMPREPLY as-is' ' sed -e "s/Z$//g" >expected <<-EOF && with-trailing-space Z @@ -1437,7 +1477,7 @@ _git_test_path_comp () __git_complete_index_file --others } -test_expect_failure 'complete files - escaped characters on cmdline' ' +test_expect_success 'complete files - escaped characters on cmdline' ' test_when_finished "rm -rf \"New|Dir\"" && mkdir "New|Dir" && >"New|Dir/New&File.c" && @@ -1453,11 +1493,13 @@ test_expect_failure 'complete files - escaped characters on cmdline' ' "New|Dir/New&File.c" ' -test_expect_failure 'complete files - quoted characters on cmdline' ' +test_expect_success 'complete files - quoted characters on cmdline' ' test_when_finished "rm -r \"New(Dir\"" && mkdir "New(Dir" && >"New(Dir/New)File.c" && + # Testing with an opening but without a corresponding closing + # double quote is important. test_completion "git test-path-comp \"New(D" "New(Dir" && test_completion "git test-path-comp \"New(Dir/New)F" \ "New(Dir/New)File.c" |