builtin-archive.c: rename remote_request() to extract_remote_arg()
[gitweb.git] / builtin-tar-tree.c
index e5aaded820bddfe2d5175508cc9c422b55a66eec..f2679a86378371293aca257d4e839e87f759ae18 100644 (file)
@@ -9,43 +9,26 @@
 #include "tar.h"
 #include "builtin.h"
 #include "pkt-line.h"
+#include "archive.h"
 
 #define RECORDSIZE     (512)
 #define BLOCKSIZE      (RECORDSIZE * 20)
 
 static const char tar_tree_usage[] =
-"git-tar-tree [--remote=<repo>] <ent> [basedir]";
+"git-tar-tree [--remote=<repo>] <tree-ish> [basedir]";
 
 static char block[BLOCKSIZE];
 static unsigned long offset;
 
 static time_t archive_time;
 static int tar_umask;
-
-/* tries hard to write, either succeeds or dies in the attempt */
-static void reliable_write(const void *data, unsigned long size)
-{
-       const char *buf = data;
-
-       while (size > 0) {
-               long ret = xwrite(1, buf, size);
-               if (ret < 0) {
-                       if (errno == EPIPE)
-                               exit(0);
-                       die("git-tar-tree: %s", strerror(errno));
-               } else if (!ret) {
-                       die("git-tar-tree: disk full?");
-               }
-               size -= ret;
-               buf += ret;
-       }
-}
+static int verbose;
 
 /* writes out the whole block, but only if it is full */
 static void write_if_needed(void)
 {
        if (offset == BLOCKSIZE) {
-               reliable_write(block, BLOCKSIZE);
+               write_or_die(1, block, BLOCKSIZE);
                offset = 0;
        }
 }
@@ -70,7 +53,7 @@ static void write_blocked(const void *data, unsigned long size)
                write_if_needed();
        }
        while (size >= BLOCKSIZE) {
-               reliable_write(buf, BLOCKSIZE);
+               write_or_die(1, buf, BLOCKSIZE);
                size -= BLOCKSIZE;
                buf += BLOCKSIZE;
        }
@@ -94,10 +77,10 @@ static void write_trailer(void)
 {
        int tail = BLOCKSIZE - offset;
        memset(block + offset, 0, tail);
-       reliable_write(block, BLOCKSIZE);
+       write_or_die(1, block, BLOCKSIZE);
        if (tail < 2 * RECORDSIZE) {
                memset(block, 0, offset);
-               reliable_write(block, BLOCKSIZE);
+               write_or_die(1, block, BLOCKSIZE);
        }
 }
 
@@ -187,6 +170,8 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path,
                mode = 0100666;
                sprintf(header.name, "%s.paxheader", sha1_to_hex(sha1));
        } else {
+               if (verbose)
+                       fprintf(stderr, "%.*s\n", path->len, path->buf);
                if (S_ISDIR(mode)) {
                        *header.typeflag = TYPEFLAG_DIR;
                        mode = (mode | 0777) & ~tar_umask;
@@ -294,7 +279,7 @@ static void traverse_tree(struct tree_desc *tree, struct strbuf *path)
        }
 }
 
-int git_tar_config(const char *var, const char *value)
+static int git_tar_config(const char *var, const char *value)
 {
        if (!strcmp(var, "tar.umask")) {
                if (!strcmp(value, "user")) {
@@ -308,18 +293,18 @@ int git_tar_config(const char *var, const char *value)
        return git_default_config(var, value);
 }
 
-static int generate_tar(int argc, const char **argv, char** envp)
+static int generate_tar(int argc, const char **argv, const char *prefix)
 {
        unsigned char sha1[20], tree_sha1[20];
        struct commit *commit;
        struct tree_desc tree;
        struct strbuf current_path;
+       void *buffer;
 
        current_path.buf = xmalloc(PATH_MAX);
        current_path.alloc = PATH_MAX;
        current_path.len = current_path.eof = 0;
 
-       setup_git_directory();
        git_config(git_tar_config);
 
        switch (argc) {
@@ -342,8 +327,8 @@ static int generate_tar(int argc, const char **argv, char** envp)
        } else
                archive_time = time(NULL);
 
-       tree.buf = read_object_with_reference(sha1, tree_type, &tree.size,
-                                             tree_sha1);
+       tree.buf = buffer = read_object_with_reference(sha1, tree_type,
+                                                      &tree.size, tree_sha1);
        if (!tree.buf)
                die("not a reference to a tag, commit or tree object: %s",
                    sha1_to_hex(sha1));
@@ -352,10 +337,78 @@ static int generate_tar(int argc, const char **argv, char** envp)
                write_entry(tree_sha1, &current_path, 040777, NULL, 0);
        traverse_tree(&tree, &current_path);
        write_trailer();
+       free(buffer);
        free(current_path.buf);
        return 0;
 }
 
+static int write_tar_entry(const unsigned char *sha1,
+                           const char *base, int baselen,
+                           const char *filename, unsigned mode, int stage)
+{
+       static struct strbuf path;
+       int filenamelen = strlen(filename);
+       void *buffer;
+       char type[20];
+       unsigned long size;
+
+       if (!path.alloc) {
+               path.buf = xmalloc(PATH_MAX);
+               path.alloc = PATH_MAX;
+               path.len = path.eof = 0;
+       }
+       if (path.alloc < baselen + filenamelen) {
+               free(path.buf);
+               path.buf = xmalloc(baselen + filenamelen);
+               path.alloc = baselen + filenamelen;
+       }
+       memcpy(path.buf, base, baselen);
+       memcpy(path.buf + baselen, filename, filenamelen);
+       path.len = baselen + filenamelen;
+       if (S_ISDIR(mode)) {
+               strbuf_append_string(&path, "/");
+               buffer = NULL;
+               size = 0;
+       } else {
+               buffer = read_sha1_file(sha1, type, &size);
+               if (!buffer)
+                       die("cannot read %s", sha1_to_hex(sha1));
+       }
+
+       write_entry(sha1, &path, mode, buffer, size);
+       free(buffer);
+
+       return READ_TREE_RECURSIVE;
+}
+
+int write_tar_archive(struct archiver_args *args)
+{
+       int plen = strlen(args->base);
+
+       git_config(git_tar_config);
+
+       archive_time = args->time;
+       verbose = args->verbose;
+
+       if (args->commit_sha1)
+               write_global_extended_header(args->commit_sha1);
+
+       if (args->base && plen > 0 && args->base[plen - 1] == '/') {
+               char *base = xstrdup(args->base);
+               int baselen = strlen(base);
+
+               while (baselen > 0 && base[baselen - 1] == '/')
+                       base[--baselen] = '\0';
+               write_tar_entry(args->tree->object.sha1, "", 0, base, 040777, 0);
+               free(base);
+       }
+       read_tree_recursive(args->tree, args->base, plen, 0,
+                           args->pathspec, write_tar_entry);
+       write_trailer();
+
+       return 0;
+}
+
 static const char *exec = "git-upload-tar";
 
 static int remote_tar(int argc, const char **argv)
@@ -369,7 +422,7 @@ static int remote_tar(int argc, const char **argv)
                usage(tar_tree_usage);
 
        /* --remote=<repo> */
-       url = strdup(argv[1]+9);
+       url = xstrdup(argv[1]+9);
        pid = git_connect(fd, url, exec);
        if (pid < 0)
                return 1;
@@ -402,19 +455,19 @@ static int remote_tar(int argc, const char **argv)
        return !!ret;
 }
 
-int cmd_tar_tree(int argc, const char **argv, char **envp)
+int cmd_tar_tree(int argc, const char **argv, const char *prefix)
 {
        if (argc < 2)
                usage(tar_tree_usage);
        if (!strncmp("--remote=", argv[1], 9))
                return remote_tar(argc, argv);
-       return generate_tar(argc, argv, envp);
+       return generate_tar(argc, argv, prefix);
 }
 
 /* ustar header + extended global header content */
 #define HEADERSIZE (2 * RECORDSIZE)
 
-int cmd_get_tar_commit_id(int argc, const char **argv, char **envp)
+int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
 {
        char buffer[HEADERSIZE];
        struct ustar_header *header = (struct ustar_header *)buffer;