builtin-archive.con commit Merge branch 'sb/dashless' (588c038)
   1/*
   2 * Copyright (c) 2006 Franck Bui-Huu
   3 * Copyright (c) 2006 Rene Scharfe
   4 */
   5#include "cache.h"
   6#include "builtin.h"
   7#include "archive.h"
   8#include "commit.h"
   9#include "tree-walk.h"
  10#include "exec_cmd.h"
  11#include "pkt-line.h"
  12#include "sideband.h"
  13#include "attr.h"
  14
  15static const char archive_usage[] = \
  16"git archive --format=<fmt> [--prefix=<prefix>/] [--verbose] [<extra>] <tree-ish> [path...]";
  17
  18#define USES_ZLIB_COMPRESSION 1
  19
  20const struct archiver archivers[] = {
  21        { "tar", write_tar_archive },
  22        { "zip", write_zip_archive, USES_ZLIB_COMPRESSION },
  23};
  24
  25static int run_remote_archiver(const char *remote, int argc,
  26                               const char **argv)
  27{
  28        char *url, buf[LARGE_PACKET_MAX];
  29        int fd[2], i, len, rv;
  30        struct child_process *conn;
  31        const char *exec = "git-upload-archive";
  32        int exec_at = 0;
  33
  34        for (i = 1; i < argc; i++) {
  35                const char *arg = argv[i];
  36                if (!prefixcmp(arg, "--exec=")) {
  37                        if (exec_at)
  38                                die("multiple --exec specified");
  39                        exec = arg + 7;
  40                        exec_at = i;
  41                        break;
  42                }
  43        }
  44
  45        url = xstrdup(remote);
  46        conn = git_connect(fd, url, exec, 0);
  47
  48        for (i = 1; i < argc; i++) {
  49                if (i == exec_at)
  50                        continue;
  51                packet_write(fd[1], "argument %s\n", argv[i]);
  52        }
  53        packet_flush(fd[1]);
  54
  55        len = packet_read_line(fd[0], buf, sizeof(buf));
  56        if (!len)
  57                die("git-archive: expected ACK/NAK, got EOF");
  58        if (buf[len-1] == '\n')
  59                buf[--len] = 0;
  60        if (strcmp(buf, "ACK")) {
  61                if (len > 5 && !prefixcmp(buf, "NACK "))
  62                        die("git-archive: NACK %s", buf + 5);
  63                die("git-archive: protocol error");
  64        }
  65
  66        len = packet_read_line(fd[0], buf, sizeof(buf));
  67        if (len)
  68                die("git-archive: expected a flush");
  69
  70        /* Now, start reading from fd[0] and spit it out to stdout */
  71        rv = recv_sideband("archive", fd[0], 1, 2);
  72        close(fd[0]);
  73        close(fd[1]);
  74        rv |= finish_connect(conn);
  75
  76        return !!rv;
  77}
  78
  79static const struct archiver *lookup_archiver(const char *name)
  80{
  81        int i;
  82
  83        for (i = 0; i < ARRAY_SIZE(archivers); i++) {
  84                if (!strcmp(name, archivers[i].name))
  85                        return &archivers[i];
  86        }
  87        return NULL;
  88}
  89
  90void parse_pathspec_arg(const char **pathspec, struct archiver_args *ar_args)
  91{
  92        ar_args->pathspec = get_pathspec(ar_args->base, pathspec);
  93}
  94
  95void parse_treeish_arg(const char **argv, struct archiver_args *ar_args,
  96                       const char *prefix)
  97{
  98        const char *name = argv[0];
  99        const unsigned char *commit_sha1;
 100        time_t archive_time;
 101        struct tree *tree;
 102        const struct commit *commit;
 103        unsigned char sha1[20];
 104
 105        if (get_sha1(name, sha1))
 106                die("Not a valid object name");
 107
 108        commit = lookup_commit_reference_gently(sha1, 1);
 109        if (commit) {
 110                commit_sha1 = commit->object.sha1;
 111                archive_time = commit->date;
 112        } else {
 113                commit_sha1 = NULL;
 114                archive_time = time(NULL);
 115        }
 116
 117        tree = parse_tree_indirect(sha1);
 118        if (tree == NULL)
 119                die("not a tree object");
 120
 121        if (prefix) {
 122                unsigned char tree_sha1[20];
 123                unsigned int mode;
 124                int err;
 125
 126                err = get_tree_entry(tree->object.sha1, prefix,
 127                                     tree_sha1, &mode);
 128                if (err || !S_ISDIR(mode))
 129                        die("current working directory is untracked");
 130
 131                tree = parse_tree_indirect(tree_sha1);
 132        }
 133        ar_args->tree = tree;
 134        ar_args->commit_sha1 = commit_sha1;
 135        ar_args->commit = commit;
 136        ar_args->time = archive_time;
 137}
 138
 139int parse_archive_args(int argc, const char **argv, const struct archiver **ar,
 140                struct archiver_args *args)
 141{
 142        const char *format = "tar";
 143        const char *base = "";
 144        int compression_level = -1;
 145        int verbose = 0;
 146        int i;
 147
 148        for (i = 1; i < argc; i++) {
 149                const char *arg = argv[i];
 150
 151                if (!strcmp(arg, "--list") || !strcmp(arg, "-l")) {
 152                        for (i = 0; i < ARRAY_SIZE(archivers); i++)
 153                                printf("%s\n", archivers[i].name);
 154                        exit(0);
 155                }
 156                if (!strcmp(arg, "--verbose") || !strcmp(arg, "-v")) {
 157                        verbose = 1;
 158                        continue;
 159                }
 160                if (!prefixcmp(arg, "--format=")) {
 161                        format = arg + 9;
 162                        continue;
 163                }
 164                if (!prefixcmp(arg, "--prefix=")) {
 165                        base = arg + 9;
 166                        continue;
 167                }
 168                if (!strcmp(arg, "--")) {
 169                        i++;
 170                        break;
 171                }
 172                if (arg[0] == '-' && isdigit(arg[1]) && arg[2] == '\0') {
 173                        compression_level = arg[1] - '0';
 174                        continue;
 175                }
 176                if (arg[0] == '-')
 177                        die("Unknown argument: %s", arg);
 178                break;
 179        }
 180
 181        /* We need at least one parameter -- tree-ish */
 182        if (argc - 1 < i)
 183                usage(archive_usage);
 184        *ar = lookup_archiver(format);
 185        if (!*ar)
 186                die("Unknown archive format '%s'", format);
 187
 188        if (compression_level != -1) {
 189                if ((*ar)->flags & USES_ZLIB_COMPRESSION)
 190                        zlib_compression_level = compression_level;
 191                else {
 192                        die("Argument not supported for format '%s': -%d",
 193                                        format, compression_level);
 194                }
 195        }
 196        args->verbose = verbose;
 197        args->base = base;
 198        args->baselen = strlen(base);
 199
 200        return i;
 201}
 202
 203static const char *extract_remote_arg(int *ac, const char **av)
 204{
 205        int ix, iy, cnt = *ac;
 206        int no_more_options = 0;
 207        const char *remote = NULL;
 208
 209        for (ix = iy = 1; ix < cnt; ix++) {
 210                const char *arg = av[ix];
 211                if (!strcmp(arg, "--"))
 212                        no_more_options = 1;
 213                if (!no_more_options) {
 214                        if (!prefixcmp(arg, "--remote=")) {
 215                                if (remote)
 216                                        die("Multiple --remote specified");
 217                                remote = arg + 9;
 218                                continue;
 219                        }
 220                        if (arg[0] != '-')
 221                                no_more_options = 1;
 222                }
 223                if (ix != iy)
 224                        av[iy] = arg;
 225                iy++;
 226        }
 227        if (remote) {
 228                av[--cnt] = NULL;
 229                *ac = cnt;
 230        }
 231        return remote;
 232}
 233
 234int cmd_archive(int argc, const char **argv, const char *prefix)
 235{
 236        const struct archiver *ar = NULL;
 237        struct archiver_args args;
 238        int tree_idx;
 239        const char *remote = NULL;
 240
 241        remote = extract_remote_arg(&argc, argv);
 242        if (remote)
 243                return run_remote_archiver(remote, argc, argv);
 244
 245        setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
 246
 247        tree_idx = parse_archive_args(argc, argv, &ar, &args);
 248        if (prefix == NULL)
 249                prefix = setup_git_directory();
 250
 251        argv += tree_idx;
 252        parse_treeish_arg(argv, &args, prefix);
 253        parse_pathspec_arg(argv + 1, &args);
 254
 255        return ar->write_archive(&args);
 256}