git-clone --reference: saner handling of borrowed symrefs.
[gitweb.git] / builtin-archive.c
index c6423b9c48b4d84269343e30c790d5c82dc9cd83..f613ac25164beadb4874d8a07b6bbac62796a530 100644 (file)
@@ -2,7 +2,6 @@
  * Copyright (c) 2006 Franck Bui-Huu
  * Copyright (c) 2006 Rene Scharfe
  */
-#include <time.h>
 #include "cache.h"
 #include "builtin.h"
 #include "archive.h"
 #include "tree-walk.h"
 #include "exec_cmd.h"
 #include "pkt-line.h"
+#include "sideband.h"
 
 static const char archive_usage[] = \
-"git-archive --format=<fmt> [--prefix=<prefix>/] [<extra>] <tree-ish> [path...]";
+"git-archive --format=<fmt> [--prefix=<prefix>/] [--verbose] [<extra>] <tree-ish> [path...]";
 
-struct archiver archivers[] = {
-       { .name = "tar", .write_archive = write_tar_archive },
+static struct archiver_desc
+{
+       const char *name;
+       write_archive_fn_t write_archive;
+       parse_extra_args_fn_t parse_extra;
+} archivers[] = {
+       { "tar", write_tar_archive, NULL },
+       { "zip", write_zip_archive, parse_extra_zip_args },
 };
 
-static int run_remote_archiver(struct archiver *ar, int argc,
+static int run_remote_archiver(const char *remote, int argc,
                               const char **argv)
 {
-       char *url, buf[1024];
+       char *url, buf[LARGE_PACKET_MAX];
        int fd[2], i, len, rv;
        pid_t pid;
+       const char *exec = "git-upload-archive";
+       int exec_at = 0;
 
-       sprintf(buf, "git-upload-archive");
+       for (i = 1; i < argc; i++) {
+               const char *arg = argv[i];
+               if (!strncmp("--exec=", arg, 7)) {
+                       if (exec_at)
+                               die("multiple --exec specified");
+                       exec = arg + 7;
+                       exec_at = i;
+                       break;
+               }
+       }
 
-       url = xstrdup(ar->remote);
-       pid = git_connect(fd, url, buf);
+       url = xstrdup(remote);
+       pid = git_connect(fd, url, exec);
        if (pid < 0)
                return pid;
 
        for (i = 1; i < argc; i++) {
-               if (!strncmp(argv[i], "--remote=", 9))
+               if (i == exec_at)
                        continue;
                packet_write(fd[1], "argument %s\n", argv[i]);
        }
@@ -55,9 +72,9 @@ static int run_remote_archiver(struct archiver *ar, int argc,
                die("git-archive: expected a flush");
 
        /* Now, start reading from fd[0] and spit it out to stdout */
-       rv = copy_fd(fd[0], 1);
-
+       rv = recv_sideband("archive", fd[0], 1, 2);
        close(fd[0]);
+       close(fd[1]);
        rv |= finish_connect(pid);
 
        return !!rv;
@@ -69,7 +86,10 @@ static int init_archiver(const char *name, struct archiver *ar)
 
        for (i = 0; i < ARRAY_SIZE(archivers); i++) {
                if (!strcmp(name, archivers[i].name)) {
-                       memcpy(ar, &archivers[i], sizeof(struct archiver));
+                       memset(ar, 0, sizeof(*ar));
+                       ar->name = archivers[i].name;
+                       ar->write_archive = archivers[i].write_archive;
+                       ar->parse_extra = archivers[i].parse_extra;
                        rv = 0;
                        break;
                }
@@ -118,7 +138,6 @@ void parse_treeish_arg(const char **argv, struct archiver_args *ar_args,
                if (err || !S_ISDIR(mode))
                        die("current working directory is untracked");
 
-               free(tree);
                tree = parse_tree_indirect(tree_sha1);
        }
        ar_args->tree = tree;
@@ -126,32 +145,25 @@ void parse_treeish_arg(const char **argv, struct archiver_args *ar_args,
        ar_args->time = archive_time;
 }
 
-static const char *default_parse_extra(struct archiver *ar,
-                                      const char **argv)
-{
-       static char msg[64];
-
-       snprintf(msg, sizeof(msg) - 4, "'%s' format does not handle %s",
-                ar->name, *argv);
-
-       return strcat(msg, "...");
-}
-
 int parse_archive_args(int argc, const char **argv, struct archiver *ar)
 {
        const char *extra_argv[MAX_EXTRA_ARGS];
        int extra_argc = 0;
        const char *format = NULL; /* might want to default to "tar" */
-       const char *remote = NULL;
        const char *base = "";
-       int list = 0;
+       int verbose = 0;
        int i;
 
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
 
                if (!strcmp(arg, "--list") || !strcmp(arg, "-l")) {
-                       list = 1;
+                       for (i = 0; i < ARRAY_SIZE(archivers); i++)
+                               printf("%s\n", archivers[i].name);
+                       exit(0);
+               }
+               if (!strcmp(arg, "--verbose") || !strcmp(arg, "-v")) {
+                       verbose = 1;
                        continue;
                }
                if (!strncmp(arg, "--format=", 9)) {
@@ -162,10 +174,6 @@ int parse_archive_args(int argc, const char **argv, struct archiver *ar)
                        base = arg + 9;
                        continue;
                }
-               if (!strncmp(arg, "--remote=", 9)) {
-                       remote = arg + 9;
-                       continue;
-               }
                if (!strcmp(arg, "--")) {
                        i++;
                        break;
@@ -179,44 +187,71 @@ int parse_archive_args(int argc, const char **argv, struct archiver *ar)
                break;
        }
 
-       if (list) {
-               if (!remote) {
-                       for (i = 0; i < ARRAY_SIZE(archivers); i++)
-                               printf("%s\n", archivers[i].name);
-                       exit(0);
-               }
-               die("--list and --remote are mutually exclusive");
-       }
-
-       if (argc - i < 1)
+       /* We need at least one parameter -- tree-ish */
+       if (argc - 1 < i)
                usage(archive_usage);
        if (!format)
                die("You must specify an archive format");
        if (init_archiver(format, ar) < 0)
                die("Unknown archive format '%s'", format);
 
-       if (extra_argc && !remote) {
-               if (!ar->parse_extra) {
-                       die("%s", default_parse_extra(ar, extra_argv));
-               }
+       if (extra_argc) {
+               if (!ar->parse_extra)
+                       die("'%s' format does not handle %s",
+                           ar->name, extra_argv[0]);
                ar->args.extra = ar->parse_extra(extra_argc, extra_argv);
        }
-       ar->remote = remote;
+       ar->args.verbose = verbose;
        ar->args.base = base;
 
        return i;
 }
 
+static const char *extract_remote_arg(int *ac, const char **av)
+{
+       int ix, iy, cnt = *ac;
+       int no_more_options = 0;
+       const char *remote = NULL;
+
+       for (ix = iy = 1; ix < cnt; ix++) {
+               const char *arg = av[ix];
+               if (!strcmp(arg, "--"))
+                       no_more_options = 1;
+               if (!no_more_options) {
+                       if (!strncmp(arg, "--remote=", 9)) {
+                               if (remote)
+                                       die("Multiple --remote specified");
+                               remote = arg + 9;
+                               continue;
+                       }
+                       if (arg[0] != '-')
+                               no_more_options = 1;
+               }
+               if (ix != iy)
+                       av[iy] = arg;
+               iy++;
+       }
+       if (remote) {
+               av[--cnt] = NULL;
+               *ac = cnt;
+       }
+       return remote;
+}
+
 int cmd_archive(int argc, const char **argv, const char *prefix)
 {
        struct archiver ar;
        int tree_idx;
+       const char *remote = NULL;
 
-       tree_idx = parse_archive_args(argc, argv, &ar);
+       remote = extract_remote_arg(&argc, argv);
+       if (remote)
+               return run_remote_archiver(remote, argc, argv);
 
-       if (ar.remote)
-               return run_remote_archiver(&ar, argc, argv);
+       setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
 
+       memset(&ar, 0, sizeof(ar));
+       tree_idx = parse_archive_args(argc, argv, &ar);
        if (prefix == NULL)
                prefix = setup_git_directory();