diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 1cd019d2e9..63903f22f1 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -461,8 +461,70 @@ __git_index_files () paths[$1] = 1 } END { - for (p in paths) - print p + for (p in paths) { + if (substr(p, 1, 1) != "\"") { + # No special characters, easy! + print p + continue + } + + # The path is quoted. + p = dequote(p) + if (p == "") + continue + + # Even when a directory name itself does not contain + # any special characters, it will still be quoted if + # any of its (stripped) trailing path components do. + # Because of this we may have seen the same direcory + # both quoted and unquoted. + if (p in paths) + # We have seen the same directory unquoted, + # skip it. + continue + else + print p + } + } + function dequote(p, bs_idx, out, esc, esc_idx, dec) { + # Skip opening double quote. + p = substr(p, 2) + + # Interpret backslash escape sequences. + while ((bs_idx = index(p, "\\")) != 0) { + out = out substr(p, 1, bs_idx - 1) + esc = substr(p, bs_idx + 1, 1) + p = substr(p, bs_idx + 2) + + if ((esc_idx = index("abtvfr\"\\", esc)) != 0) { + # C-style one-character escape sequence. + out = out substr("\a\b\t\v\f\r\"\\", + esc_idx, 1) + } else if (esc == "n") { + # Uh-oh, a newline character. + # We cant reliably put a pathname + # containing a newline into COMPREPLY, + # and the newline would create a mess. + # Skip this path. + return "" + } else { + # Must be a \nnn octal value, then. + dec = esc * 64 + \ + substr(p, 1, 1) * 8 + \ + substr(p, 2, 1) + out = out sprintf("%c", dec) + p = substr(p, 3) + } + } + # Drop closing double quote, if there is one. + # (There isnt any if this is a directory, as it was + # already stripped with the trailing path components.) + if (substr(p, length(p), 1) == "\"") + out = out substr(p, 1, length(p) - 1) + else + out = out p + + return out }' } diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh index 9d460768ef..955932174c 100755 --- a/t/t9902-completion.sh +++ b/t/t9902-completion.sh @@ -1527,7 +1527,7 @@ else say "Your filesystem does not allow \\ and \" in filenames." rm -rf 'New\Dir' fi -test_expect_failure FUNNYNAMES_BS_DQ \ +test_expect_success FUNNYNAMES_BS_DQ \ 'complete files - C-style escapes in ls-files output' ' test_when_finished "rm -r \"New\\\\Dir\"" && @@ -1548,7 +1548,7 @@ else say 'Your filesystem does not allow special separator characters (FS, GS, RS, US) in filenames.' rm -rf $'New\034Special\035Dir' fi -test_expect_failure FUNNYNAMES_SEPARATORS \ +test_expect_success FUNNYNAMES_SEPARATORS \ 'complete files - \nnn-escaped control characters in ls-files output' ' test_when_finished "rm -r '$'New\034Special\035Dir''" && @@ -1562,6 +1562,19 @@ test_expect_failure FUNNYNAMES_SEPARATORS \ "'$'New\034Special\035Dir/New\036Special\037File''" ' +test_expect_success FUNNYNAMES_BS_DQ \ + 'complete files - removing repeated quoted path components' ' + test_when_finished rm -rf NewDir && + mkdir NewDir && # A dirname which in itself would not be quoted ... + >NewDir/0-file && + >NewDir/1\"file && # ... but here the file makes the dirname quoted ... + >NewDir/2-file && + >NewDir/3\"file && # ... and here, too. + + # Still, we should only list it once. + test_completion "git test-path-comp New" "NewDir" +' + test_expect_success "completion uses completion for alias: !sh -c 'git ...'" ' test_config alias.co "!sh -c '"'"'git checkout ...'"'"'" &&