diff options
Diffstat (limited to 'builtin-tar-tree.c')
-rw-r--r-- | builtin-tar-tree.c | 155 |
1 files changed, 86 insertions, 69 deletions
diff --git a/builtin-tar-tree.c b/builtin-tar-tree.c index fa666f78c..437eb726a 100644 --- a/builtin-tar-tree.c +++ b/builtin-tar-tree.c @@ -3,12 +3,12 @@ */ #include <time.h> #include "cache.h" -#include "tree-walk.h" #include "commit.h" #include "strbuf.h" #include "tar.h" #include "builtin.h" #include "pkt-line.h" +#include "archive.h" #define RECORDSIZE (512) #define BLOCKSIZE (RECORDSIZE * 20) @@ -21,6 +21,7 @@ static unsigned long offset; static time_t archive_time; static int tar_umask; +static int verbose; /* writes out the whole block, but only if it is full */ static void write_if_needed(void) @@ -168,6 +169,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; @@ -244,37 +247,6 @@ static void write_global_extended_header(const unsigned char *sha1) free(ext_header.buf); } -static void traverse_tree(struct tree_desc *tree, struct strbuf *path) -{ - int pathlen = path->len; - struct name_entry entry; - - while (tree_entry(tree, &entry)) { - void *eltbuf; - char elttype[20]; - unsigned long eltsize; - - eltbuf = read_sha1_file(entry.sha1, elttype, &eltsize); - if (!eltbuf) - die("cannot read %s", sha1_to_hex(entry.sha1)); - - path->len = pathlen; - strbuf_append_string(path, entry.path); - if (S_ISDIR(entry.mode)) - strbuf_append_string(path, "/"); - - write_entry(entry.sha1, path, entry.mode, eltbuf, eltsize); - - if (S_ISDIR(entry.mode)) { - struct tree_desc subtree; - subtree.buf = eltbuf; - subtree.size = eltsize; - traverse_tree(&subtree, path); - } - free(eltbuf); - } -} - static int git_tar_config(const char *var, const char *value) { if (!strcmp(var, "tar.umask")) { @@ -291,50 +263,95 @@ static int git_tar_config(const char *var, const char *value) 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; + struct archiver_args args; + int result; + char *base = NULL; git_config(git_tar_config); - switch (argc) { - case 3: - strbuf_append_string(¤t_path, argv[2]); - strbuf_append_string(¤t_path, "/"); - /* FALLTHROUGH */ - case 2: - if (get_sha1(argv[1], sha1)) - die("Not a valid object name %s", argv[1]); - break; - default: + memset(&args, 0, sizeof(args)); + if (argc != 2 && argc != 3) usage(tar_tree_usage); + if (argc == 3) { + int baselen = strlen(argv[2]); + base = xmalloc(baselen + 2); + memcpy(base, argv[2], baselen); + base[baselen] = '/'; + base[baselen + 1] = '\0'; } + args.base = base; + parse_treeish_arg(argv + 1, &args, NULL); - commit = lookup_commit_reference_gently(sha1, 1); - if (commit) { - write_global_extended_header(commit->object.sha1); - archive_time = commit->date; - } else - archive_time = time(NULL); - - 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)); - - if (current_path.len > 0) - write_entry(tree_sha1, ¤t_path, 040777, NULL, 0); - traverse_tree(&tree, ¤t_path); - write_trailer(); + result = write_tar_archive(&args); + free(base); + + return result; +} + +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); - free(current_path.buf); + + return READ_TREE_RECURSIVE; +} + +int write_tar_archive(struct archiver_args *args) +{ + int plen = args->base ? strlen(args->base) : 0; + + 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; } |