X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=builtin-archive.c;h=60adef93632ec64c528e443cf1fb3d656a54e36c;hb=e785dadc90df20e1038c053dc168a6458b1e672b;hp=14a1b3077cd7a5c4d69672bccfadc1354568dc4a;hpb=fb5fd011482b5aa0b340a4a5bd9192c0efc1edb7;p=git.git diff --git a/builtin-archive.c b/builtin-archive.c index 14a1b3077..60adef936 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -5,70 +5,51 @@ #include "cache.h" #include "builtin.h" #include "archive.h" -#include "commit.h" -#include "tree-walk.h" -#include "exec_cmd.h" +#include "parse-options.h" #include "pkt-line.h" #include "sideband.h" -#include "attr.h" -static const char archive_usage[] = \ -"git-archive --format= [--prefix=/] [--verbose] [] [path...]"; - -static struct archiver_desc +static void create_output_file(const char *output_file) { - 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 }, -}; + int output_fd = open(output_file, O_CREAT | O_WRONLY | O_TRUNC, 0666); + if (output_fd < 0) + die("could not create archive file: %s ", output_file); + if (output_fd != 1) { + if (dup2(output_fd, 1) < 0) + die("could not redirect output"); + else + close(output_fd); + } +} -static int run_remote_archiver(const char *remote, int argc, - const char **argv) +static int run_remote_archiver(int argc, const char **argv, + const char *remote, const char *exec) { char *url, buf[LARGE_PACKET_MAX]; int fd[2], i, len, rv; struct child_process *conn; - const char *exec = "git-upload-archive"; - int exec_at = 0; - - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - if (!prefixcmp(arg, "--exec=")) { - if (exec_at) - die("multiple --exec specified"); - exec = arg + 7; - exec_at = i; - break; - } - } url = xstrdup(remote); conn = git_connect(fd, url, exec, 0); - for (i = 1; i < argc; i++) { - if (i == exec_at) - continue; + for (i = 1; i < argc; i++) packet_write(fd[1], "argument %s\n", argv[i]); - } packet_flush(fd[1]); len = packet_read_line(fd[0], buf, sizeof(buf)); if (!len) - die("git-archive: expected ACK/NAK, got EOF"); + die("git archive: expected ACK/NAK, got EOF"); if (buf[len-1] == '\n') buf[--len] = 0; if (strcmp(buf, "ACK")) { if (len > 5 && !prefixcmp(buf, "NACK ")) - die("git-archive: NACK %s", buf + 5); - die("git-archive: protocol error"); + die("git archive: NACK %s", buf + 5); + die("git archive: protocol error"); } len = packet_read_line(fd[0], buf, sizeof(buf)); if (len) - die("git-archive: expected a flush"); + die("git archive: expected a flush"); /* Now, start reading from fd[0] and spit it out to stdout */ rv = recv_sideband("archive", fd[0], 1, 2); @@ -79,263 +60,35 @@ static int run_remote_archiver(const char *remote, int argc, return !!rv; } -static void format_subst(const struct commit *commit, - const char *src, size_t len, - struct strbuf *buf) -{ - char *to_free = NULL; - struct strbuf fmt; - - if (src == buf->buf) - to_free = strbuf_detach(buf, NULL); - strbuf_init(&fmt, 0); - for (;;) { - const char *b, *c; - - b = memmem(src, len, "$Format:", 8); - if (!b || src + len < b + 9) - break; - c = memchr(b + 8, '$', len - 8); - if (!c) - break; - - strbuf_reset(&fmt); - strbuf_add(&fmt, b + 8, c - b - 8); - - strbuf_add(buf, src, b - src); - format_commit_message(commit, fmt.buf, buf); - len -= c + 1 - src; - src = c + 1; - } - strbuf_add(buf, src, len); - strbuf_release(&fmt); - free(to_free); -} - -static int convert_to_archive(const char *path, - const void *src, size_t len, - struct strbuf *buf, - const struct commit *commit) -{ - static struct git_attr *attr_export_subst; - struct git_attr_check check[1]; - - if (!commit) - return 0; - - if (!attr_export_subst) - attr_export_subst = git_attr("export-subst", 12); - - check[0].attr = attr_export_subst; - if (git_checkattr(path, ARRAY_SIZE(check), check)) - return 0; - if (!ATTR_TRUE(check[0].value)) - return 0; - - format_subst(commit, src, len, buf); - return 1; -} - -void *sha1_file_to_archive(const char *path, const unsigned char *sha1, - unsigned int mode, enum object_type *type, - unsigned long *sizep, - const struct commit *commit) -{ - void *buffer; - - buffer = read_sha1_file(sha1, type, sizep); - if (buffer && S_ISREG(mode)) { - struct strbuf buf; - size_t size = 0; - - strbuf_init(&buf, 0); - strbuf_attach(&buf, buffer, *sizep, *sizep + 1); - convert_to_working_tree(path, buf.buf, buf.len, &buf); - convert_to_archive(path, buf.buf, buf.len, &buf, commit); - buffer = strbuf_detach(&buf, &size); - *sizep = size; - } +#define PARSE_OPT_KEEP_ALL ( PARSE_OPT_KEEP_DASHDASH | \ + PARSE_OPT_KEEP_ARGV0 | \ + PARSE_OPT_KEEP_UNKNOWN | \ + PARSE_OPT_NO_INTERNAL_HELP ) - return buffer; -} - -static int init_archiver(const char *name, struct archiver *ar) -{ - int rv = -1, i; - - for (i = 0; i < ARRAY_SIZE(archivers); i++) { - if (!strcmp(name, archivers[i].name)) { - 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; - } - } - return rv; -} - -void parse_pathspec_arg(const char **pathspec, struct archiver_args *ar_args) -{ - ar_args->pathspec = get_pathspec(ar_args->base, pathspec); -} - -void parse_treeish_arg(const char **argv, struct archiver_args *ar_args, - const char *prefix) -{ - const char *name = argv[0]; - const unsigned char *commit_sha1; - time_t archive_time; - struct tree *tree; - const struct commit *commit; - unsigned char sha1[20]; - - if (get_sha1(name, sha1)) - die("Not a valid object name"); - - commit = lookup_commit_reference_gently(sha1, 1); - if (commit) { - commit_sha1 = commit->object.sha1; - archive_time = commit->date; - } else { - commit_sha1 = NULL; - archive_time = time(NULL); - } - - tree = parse_tree_indirect(sha1); - if (tree == NULL) - die("not a tree object"); - - if (prefix) { - unsigned char tree_sha1[20]; - unsigned int mode; - int err; - - err = get_tree_entry(tree->object.sha1, prefix, - tree_sha1, &mode); - if (err || !S_ISDIR(mode)) - die("current working directory is untracked"); - - tree = parse_tree_indirect(tree_sha1); - } - ar_args->tree = tree; - ar_args->commit_sha1 = commit_sha1; - ar_args->commit = commit; - ar_args->time = archive_time; -} - -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 = "tar"; - const char *base = ""; - int verbose = 0; - int i; - - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - - if (!strcmp(arg, "--list") || !strcmp(arg, "-l")) { - 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 (!prefixcmp(arg, "--format=")) { - format = arg + 9; - continue; - } - if (!prefixcmp(arg, "--prefix=")) { - base = arg + 9; - continue; - } - if (!strcmp(arg, "--")) { - i++; - break; - } - if (arg[0] == '-') { - if (extra_argc > MAX_EXTRA_ARGS - 1) - die("Too many extra options"); - extra_argv[extra_argc++] = arg; - continue; - } - break; - } - - /* We need at least one parameter -- tree-ish */ - if (argc - 1 < i) - usage(archive_usage); - if (init_archiver(format, ar) < 0) - die("Unknown archive format '%s'", format); - - 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->args.verbose = verbose; - ar->args.base = base; - - return i; -} - -static const char *extract_remote_arg(int *ac, const char **av) +int cmd_archive(int argc, const char **argv, const char *prefix) { - int ix, iy, cnt = *ac; - int no_more_options = 0; + const char *exec = "git-upload-archive"; + const char *output = NULL; const char *remote = NULL; + struct option local_opts[] = { + OPT_STRING(0, "output", &output, "file", + "write the archive to this file"), + OPT_STRING(0, "remote", &remote, "repo", + "retrieve the archive from remote repository "), + OPT_STRING(0, "exec", &exec, "cmd", + "path to the remote git-upload-archive command"), + OPT_END() + }; - for (ix = iy = 1; ix < cnt; ix++) { - const char *arg = av[ix]; - if (!strcmp(arg, "--")) - no_more_options = 1; - if (!no_more_options) { - if (!prefixcmp(arg, "--remote=")) { - 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; -} + argc = parse_options(argc, argv, local_opts, NULL, PARSE_OPT_KEEP_ALL); -int cmd_archive(int argc, const char **argv, const char *prefix) -{ - struct archiver ar; - int tree_idx; - const char *remote = NULL; + if (output) + create_output_file(output); - remote = extract_remote_arg(&argc, argv); if (remote) - return run_remote_archiver(remote, argc, argv); + return run_remote_archiver(argc, argv, remote, exec); 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(); - - argv += tree_idx; - parse_treeish_arg(argv, &ar.args, prefix); - parse_pathspec_arg(argv + 1, &ar.args); - - return ar.write_archive(&ar.args); + return write_archive(argc, argv, prefix, 1); }