]> git.ipfire.org Git - thirdparty/git.git/commitdiff
archive --add-virtual-file: allow paths containing colons
authorJohannes Schindelin <johannes.schindelin@gmx.de>
Sat, 28 May 2022 23:11:13 +0000 (16:11 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 31 May 2022 06:07:31 +0000 (23:07 -0700)
By allowing the path to be enclosed in double-quotes, we can avoid
the limitation that paths cannot contain colons.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/git-archive.txt
archive.c
t/t5003-archive-zip.sh

index b41cc5bc2e076d7ec9eaa78c64a72b8b04a69023..56989a2f3493aa80530cb9efc72e74ba8d42eb56 100644 (file)
@@ -69,10 +69,16 @@ OPTIONS
        by concatenating the value of the last `--prefix` option (if any)
        before this `--add-virtual-file` and `<path>`.
 +
-The `<path>` cannot contain any colon, the file mode is limited to
-a regular file, and the option may be subject to platform-dependent
-command-line limits. For non-trivial cases, write an untracked file
-and use `--add-file` instead.
+The `<path>` argument can start and end with a literal double-quote
+character; the contained file name is interpreted as a C-style string,
+i.e. the backslash is interpreted as escape character. The path must
+be quoted if it contains a colon, to avoid the colon from being
+misinterpreted as the separator between the path and the contents, or
+if the path begins or ends with a double-quote character.
++
+The file mode is limited to a regular file, and the option may be
+subject to platform-dependent command-line limits. For non-trivial
+cases, write an untracked file and use `--add-file` instead.
 
 --worktree-attributes::
        Look for attributes in .gitattributes files in the working tree
index 29a90c703294cd152ebf9013e4fcda1413c694a0..d5109abb894200f9b37b3631286a529437463816 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -9,6 +9,7 @@
 #include "parse-options.h"
 #include "unpack-trees.h"
 #include "dir.h"
+#include "quote.h"
 
 static char const * const archive_usage[] = {
        N_("git archive [<options>] <tree-ish> [<path>...]"),
@@ -535,22 +536,31 @@ static int add_file_cb(const struct option *opt, const char *arg, int unset)
                        die(_("Not a regular file: %s"), path);
                info->content = NULL; /* read the file later */
        } else if (!strcmp(opt->long_name, "add-virtual-file")) {
-               const char *colon = strchr(arg, ':');
-               char *p;
+               struct strbuf buf = STRBUF_INIT;
+               const char *p = arg;
+
+               if (*p != '"')
+                       p = strchr(p, ':');
+               else if (unquote_c_style(&buf, p, &p) < 0)
+                       die(_("unclosed quote: '%s'"), arg);
 
-               if (!colon)
+               if (!p || *p != ':')
                        die(_("missing colon: '%s'"), arg);
 
-               p = xstrndup(arg, colon - arg);
-               if (!args->prefix)
-                       path = p;
-               else {
-                       path = prefix_filename(args->prefix, p);
-                       free(p);
+               if (p == arg)
+                       die(_("empty file name: '%s'"), arg);
+
+               path = buf.len ?
+                       strbuf_detach(&buf, NULL) : xstrndup(arg, p - arg);
+
+               if (args->prefix) {
+                       char *save = path;
+                       path = prefix_filename(args->prefix, path);
+                       free(save);
                }
                memset(&info->stat, 0, sizeof(info->stat));
                info->stat.st_mode = S_IFREG | 0644;
-               info->content = xstrdup(colon + 1);
+               info->content = xstrdup(p + 1);
                info->stat.st_size = strlen(info->content);
        } else {
                BUG("add_file_cb() called for %s", opt->long_name);
index d6027189e2565dd5c790631d5febf254cb3214c8..3992d08158a29ee8a9a1b73a7f7b6446308b2e6a 100755 (executable)
@@ -207,13 +207,21 @@ check_zip with_untracked
 check_added with_untracked untracked untracked
 
 test_expect_success UNZIP 'git archive --format=zip --add-virtual-file' '
+       if test_have_prereq FUNNYNAMES
+       then
+               PATHNAME="pathname with : colon"
+       else
+               PATHNAME="pathname without colon"
+       fi &&
        git archive --format=zip >with_file_with_content.zip \
+               --add-virtual-file=\""$PATHNAME"\": \
                --add-virtual-file=hello:world $EMPTY_TREE &&
        test_when_finished "rm -rf tmp-unpack" &&
        mkdir tmp-unpack && (
                cd tmp-unpack &&
                "$GIT_UNZIP" ../with_file_with_content.zip &&
                test_path_is_file hello &&
+               test_path_is_file "$PATHNAME" &&
                test world = $(cat hello)
        )
 '