From 4df096a5ca24f2f39042c51cf51b8a2bec66a2b5 Mon Sep 17 00:00:00 2001 From: Franck Bui-Huu Date: Thu, 7 Sep 2006 15:12:02 +0200 Subject: [PATCH 01/18] Add git-archive git-archive is a command to make TAR and ZIP archives of a git tree. It helps prevent a proliferation of git-{format}-tree commands. Instead of directly calling git-{tar,zip}-tree command, it defines a very simple API, that archiver should implement and register in "git-archive.c". This API is made up by 2 functions whose prototype is defined in "archive.h" file. - The first one is used to parse 'extra' parameters which have signification only for the specific archiver. That would allow different archive backends to have different kind of options. - The second one is used to ask to an archive backend to build the archive given some already resolved parameters. The main reason for making this API is to avoid using git-{tar,zip}-tree commands, hence making them useless. Maybe it's time for them to die ? It also implements remote operations by defining a very simple protocol: it first sends the name of the specific uploader followed the repository name (git-upload-tar git://example.org/repo.git). Then it sends options. It's done by sending a sequence of one argument per packet, with prefix "argument ", followed by a flush. The remote protocol is implemented in "git-archive.c" for client side and is triggered by "--remote=" option. For example, to fetch a TAR archive in a remote repo, you can issue: $ git archive --format=tar --remote=git://xxx/yyy/zzz.git HEAD We choose to not make a new command "git-fetch-archive" for example, avoind one more GIT command which should be nice for users (less commands to remember, keeps existing --remote option). Signed-off-by: Franck Bui-Huu Acked-by: Rene Scharfe Signed-off-by: Junio C Hamano --- .gitignore | 1 + Documentation/git-archive.txt | 100 +++++++++++++++ Makefile | 3 +- archive.h | 41 ++++++ builtin-archive.c | 228 ++++++++++++++++++++++++++++++++++ builtin.h | 1 + generate-cmdlist.sh | 1 + git.c | 1 + 8 files changed, 375 insertions(+), 1 deletion(-) create mode 100644 Documentation/git-archive.txt create mode 100644 archive.h create mode 100644 builtin-archive.c diff --git a/.gitignore b/.gitignore index 0d608fe12a..a3f33d4209 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ git-apply git-applymbox git-applypatch git-archimport +git-archive git-bisect git-branch git-cat-file diff --git a/Documentation/git-archive.txt b/Documentation/git-archive.txt new file mode 100644 index 0000000000..913528d373 --- /dev/null +++ b/Documentation/git-archive.txt @@ -0,0 +1,100 @@ +git-archive(1) +============== + +NAME +---- +git-archive - Creates a archive of the files in the named tree + + +SYNOPSIS +-------- +'git-archive' --format= [--list] [--prefix=/] [] + [--remote=] [path...] + +DESCRIPTION +----------- +Creates an archive of the specified format containing the tree +structure for the named tree. If is specified it is +prepended to the filenames in the archive. + +'git-archive' behaves differently when given a tree ID versus when +given a commit ID or tag ID. In the first case the current time is +used as modification time of each file in the archive. In the latter +case the commit time as recorded in the referenced commit object is +used instead. Additionally the commit ID is stored in a global +extended pax header if the tar format is used; it can be extracted +using 'git-get-tar-commit-id'. In ZIP files it is stored as a file +comment. + +OPTIONS +------- + +--format=:: + Format of the resulting archive: 'tar', 'zip'... + +--list:: + Show all available formats. + +--prefix=/:: + Prepend / to each filename in the archive. + +:: + This can be any options that the archiver backend understand. + +--remote=:: + Instead of making a tar archive from local repository, + retrieve a tar archive from a remote repository. + +:: + The tree or commit to produce an archive for. + +path:: + If one or more paths are specified, include only these in the + archive, otherwise include all files and subdirectories. + +CONFIGURATION +------------- +By default, file and directories modes are set to 0666 or 0777 in tar +archives. It is possible to change this by setting the "umask" variable +in the repository configuration as follows : + +[tar] + umask = 002 ;# group friendly + +The special umask value "user" indicates that the user's current umask +will be used instead. The default value remains 0, which means world +readable/writable files and directories. + +EXAMPLES +-------- +git archive --format=tar --prefix=junk/ HEAD | (cd /var/tmp/ && tar xf -):: + + Create a tar archive that contains the contents of the + latest commit on the current branch, and extracts it in + `/var/tmp/junk` directory. + +git archive --format=tar --prefix=git-1.4.0/ v1.4.0 | gzip >git-1.4.0.tar.gz:: + + Create a compressed tarball for v1.4.0 release. + +git archive --format=tar --prefix=git-1.4.0/ v1.4.0{caret}\{tree\} | gzip >git-1.4.0.tar.gz:: + + Create a compressed tarball for v1.4.0 release, but without a + global extended pax header. + +git archive --format=zip --prefix=git-docs/ HEAD:Documentation/ > git-1.4.0-docs.zip:: + + Put everything in the current head's Documentation/ directory + into 'git-1.4.0-docs.zip', with the prefix 'git-docs/'. + +Author +------ +Written by Franck Bui-Huu and Rene Scharfe. + +Documentation +-------------- +Documentation by David Greaves, Junio C Hamano and the git-list . + +GIT +--- +Part of the gitlink:git[7] suite diff --git a/Makefile b/Makefile index 7b3114f3aa..8f62ceffaf 100644 --- a/Makefile +++ b/Makefile @@ -232,7 +232,7 @@ LIB_FILE=libgit.a XDIFF_LIB=xdiff/lib.a LIB_H = \ - blob.h cache.h commit.h csum-file.h delta.h \ + archive.h blob.h cache.h commit.h csum-file.h delta.h \ diff.h object.h pack.h pkt-line.h quote.h refs.h \ run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \ tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h @@ -256,6 +256,7 @@ LIB_OBJS = \ BUILTIN_OBJS = \ builtin-add.o \ builtin-apply.o \ + builtin-archive.o \ builtin-cat-file.o \ builtin-checkout-index.o \ builtin-check-ref-format.o \ diff --git a/archive.h b/archive.h new file mode 100644 index 0000000000..24b016f001 --- /dev/null +++ b/archive.h @@ -0,0 +1,41 @@ +#ifndef ARCHIVE_H +#define ARCHIVE_H + +#define MAX_EXTRA_ARGS 32 +#define MAX_ARGS (MAX_EXTRA_ARGS + 32) + +struct archiver_args { + const char *base; + struct tree *tree; + const unsigned char *commit_sha1; + time_t time; + const char **pathspec; + void *extra; +}; + +typedef int (*write_archive_fn_t)(struct archiver_args *); + +typedef void *(*parse_extra_args_fn_t)(int argc, const char **argv); + +struct archiver { + const char *name; + const char *remote; + struct archiver_args args; + write_archive_fn_t write_archive; + parse_extra_args_fn_t parse_extra; +}; + +extern struct archiver archivers[]; + +extern int parse_archive_args(int argc, + const char **argv, + struct archiver *ar); + +extern void parse_treeish_arg(const char **treeish, + struct archiver_args *ar_args, + const char *prefix); + +extern void parse_pathspec_arg(const char **pathspec, + struct archiver_args *args); + +#endif /* ARCHIVE_H */ diff --git a/builtin-archive.c b/builtin-archive.c new file mode 100644 index 0000000000..f6bc269fdc --- /dev/null +++ b/builtin-archive.c @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2006 Franck Bui-Huu + * Copyright (c) 2006 Rene Scharfe + */ +#include +#include "cache.h" +#include "builtin.h" +#include "archive.h" +#include "commit.h" +#include "tree-walk.h" +#include "exec_cmd.h" +#include "pkt-line.h" + +static const char archive_usage[] = \ +"git-archive --format= [--prefix=/] [] [path...]"; + +struct archiver archivers[] = { + { "" /* dummy */ }, +}; + +static int run_remote_archiver(struct archiver *ar, int argc, + const char **argv) +{ + char *url, buf[1024]; + int fd[2], i, len, rv; + pid_t pid; + + sprintf(buf, "git-upload-archive"); + + url = xstrdup(ar->remote); + pid = git_connect(fd, url, buf); + if (pid < 0) + return pid; + + for (i = 1; i < argc; i++) { + if (!strncmp(argv[i], "--remote=", 9)) + continue; + 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"); + if (buf[len-1] == '\n') + buf[--len] = 0; + if (strcmp(buf, "ACK")) { + if (len > 5 && !strncmp(buf, "NACK ", 5)) + 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"); + + /* Now, start reading from fd[0] and spit it out to stdout */ + rv = copy_fd(fd[0], 1); + + close(fd[0]); + rv |= finish_connect(pid); + + return !!rv; +} + +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)) { + memcpy(ar, &archivers[i], sizeof(struct archiver)); + 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; + 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"); + + free(tree); + tree = parse_tree_indirect(tree_sha1); + } + ar_args->tree = tree; + ar_args->commit_sha1 = commit_sha1; + 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 i; + + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + + if (!strcmp(arg, "--list") || !strcmp(arg, "-l")) { + list = 1; + continue; + } + if (!strncmp(arg, "--format=", 9)) { + format = arg + 9; + continue; + } + if (!strncmp(arg, "--prefix=", 9)) { + base = arg + 9; + continue; + } + if (!strncmp(arg, "--remote=", 9)) { + remote = 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; + } + + 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) + 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)); + } + ar->args.extra = ar->parse_extra(extra_argc, extra_argv); + } + ar->remote = remote; + ar->args.base = base; + + return i; +} + +int cmd_archive(int argc, const char **argv, const char *prefix) +{ + struct archiver ar; + int tree_idx; + + tree_idx = parse_archive_args(argc, argv, &ar); + + if (ar.remote) + return run_remote_archiver(&ar, argc, argv); + + 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); +} diff --git a/builtin.h b/builtin.h index 25431d7081..50852cd6a9 100644 --- a/builtin.h +++ b/builtin.h @@ -15,6 +15,7 @@ extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix); extern int cmd_add(int argc, const char **argv, const char *prefix); extern int cmd_apply(int argc, const char **argv, const char *prefix); +extern int cmd_archive(int argc, const char **argv, const char *prefix); extern int cmd_cat_file(int argc, const char **argv, const char *prefix); extern int cmd_checkout_index(int argc, const char **argv, const char *prefix); extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix); diff --git a/generate-cmdlist.sh b/generate-cmdlist.sh index ec1eda20de..5450918be3 100755 --- a/generate-cmdlist.sh +++ b/generate-cmdlist.sh @@ -12,6 +12,7 @@ struct cmdname_help common_cmds[] = {" sort <<\EOF | add apply +archive bisect branch checkout diff --git a/git.c b/git.c index 335f405c20..8c64b2753f 100644 --- a/git.c +++ b/git.c @@ -220,6 +220,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) } commands[] = { { "add", cmd_add, RUN_SETUP }, { "apply", cmd_apply }, + { "archive", cmd_archive }, { "cat-file", cmd_cat_file, RUN_SETUP }, { "checkout-index", cmd_checkout_index, RUN_SETUP }, { "check-ref-format", cmd_check_ref_format }, From efd8696cd7ccfa5042ef710e89fe0d10efdcd085 Mon Sep 17 00:00:00 2001 From: Franck Bui-Huu Date: Thu, 7 Sep 2006 15:12:03 +0200 Subject: [PATCH 02/18] git-archive: wire up TAR format. This is based on Rene Scharfe's earlier patch, but uses the archiver support introduced by the previous patch. Signed-off-by: Franck Bui-Huu Acked-by: Rene Scharfe Signed-off-by: Junio C Hamano --- archive.h | 4 +++ builtin-archive.c | 2 +- builtin-tar-tree.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 1 deletion(-) diff --git a/archive.h b/archive.h index 24b016f001..5c3f29b8df 100644 --- a/archive.h +++ b/archive.h @@ -37,5 +37,9 @@ extern void parse_treeish_arg(const char **treeish, extern void parse_pathspec_arg(const char **pathspec, struct archiver_args *args); +/* + * Archive-format specific backends. + */ +extern int write_tar_archive(struct archiver_args *); #endif /* ARCHIVE_H */ diff --git a/builtin-archive.c b/builtin-archive.c index f6bc269fdc..c6423b9c48 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -15,7 +15,7 @@ static const char archive_usage[] = \ "git-archive --format= [--prefix=/] [] [path...]"; struct archiver archivers[] = { - { "" /* dummy */ }, + { .name = "tar", .write_archive = write_tar_archive }, }; static int run_remote_archiver(struct archiver *ar, int argc, diff --git a/builtin-tar-tree.c b/builtin-tar-tree.c index fa666f78c5..c20eb0e364 100644 --- a/builtin-tar-tree.c +++ b/builtin-tar-tree.c @@ -9,6 +9,7 @@ #include "tar.h" #include "builtin.h" #include "pkt-line.h" +#include "archive.h" #define RECORDSIZE (512) #define BLOCKSIZE (RECORDSIZE * 20) @@ -338,6 +339,72 @@ static int generate_tar(int argc, const char **argv, const char *prefix) 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; + + if (args->commit_sha1) + write_global_extended_header(args->commit_sha1); + + if (args->base && plen > 0 && args->base[plen - 1] == '/') { + char *base = strdup(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) From ec06bff5e6f76b46c22a3b2c97452568f088fa3c Mon Sep 17 00:00:00 2001 From: Franck Bui-Huu Date: Thu, 7 Sep 2006 15:12:04 +0200 Subject: [PATCH 03/18] git-archive: wire up ZIP format. Again, this is based on Rene Scharfe's earlier patch, but uses the archiver support introduced by the previous patch. Signed-off-by: Franck Bui-Huu Acked-by: Rene Scharfe Signed-off-by: Junio C Hamano --- archive.h | 1 + builtin-archive.c | 1 + builtin-zip-tree.c | 28 ++++++++++++++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/archive.h b/archive.h index 5c3f29b8df..f3d344b652 100644 --- a/archive.h +++ b/archive.h @@ -41,5 +41,6 @@ extern void parse_pathspec_arg(const char **pathspec, * Archive-format specific backends. */ extern int write_tar_archive(struct archiver_args *); +extern int write_zip_archive(struct archiver_args *); #endif /* ARCHIVE_H */ diff --git a/builtin-archive.c b/builtin-archive.c index c6423b9c48..651d1bf6d9 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -16,6 +16,7 @@ static const char archive_usage[] = \ struct archiver archivers[] = { { .name = "tar", .write_archive = write_tar_archive }, + { .name = "zip", .write_archive = write_zip_archive }, }; static int run_remote_archiver(struct archiver *ar, int argc, diff --git a/builtin-zip-tree.c b/builtin-zip-tree.c index 1c1f6830c1..3afb7bde74 100644 --- a/builtin-zip-tree.c +++ b/builtin-zip-tree.c @@ -8,6 +8,7 @@ #include "tree.h" #include "quote.h" #include "builtin.h" +#include "archive.h" static const char zip_tree_usage[] = "git-zip-tree [-0|...|-9] [ ]"; @@ -351,3 +352,30 @@ int cmd_zip_tree(int argc, const char **argv, const char *prefix) return 0; } + +int write_zip_archive(struct archiver_args *args) +{ + int plen = strlen(args->base); + + dos_time(&args->time, &zip_date, &zip_time); + + zip_dir = xmalloc(ZIP_DIRECTORY_MIN_SIZE); + zip_dir_size = ZIP_DIRECTORY_MIN_SIZE; + + if (args->base && plen > 0 && args->base[plen - 1] == '/') { + char *base = strdup(args->base); + int baselen = strlen(base); + + while (baselen > 0 && base[baselen - 1] == '/') + base[--baselen] = '\0'; + write_zip_entry(args->tree->object.sha1, "", 0, base, 040777, 0); + free(base); + } + read_tree_recursive(args->tree, args->base, plen, 0, + args->pathspec, write_zip_entry); + write_zip_trailer(args->commit_sha1); + + free(zip_dir); + + return 0; +} From 39345a216ff37bda9fb7cec85f6de44069f5205d Mon Sep 17 00:00:00 2001 From: Franck Bui-Huu Date: Thu, 7 Sep 2006 15:12:05 +0200 Subject: [PATCH 04/18] Add git-upload-archive This command implements the git archive protocol on the server side. This command is not intended to be used by the end user. Underlying git-archive command line options are sent over the protocol from "git-archive --remote=...", just like upload-tar currently does with "git-tar-tree=...". As for "git-archive" command implementation, this new command does not execute any existing "git-{tar,zip}-tree" but rely on the archive API defined by "git-archive" patch. Hence we get 2 good points: - "git-archive" and "git-upload-archive" share all option parsing code. - All kind of git-upload-{tar,zip} can be deprecated. Signed-off-by: Franck Bui-Huu Signed-off-by: Junio C Hamano --- .gitignore | 1 + Documentation/git-upload-archive.txt | 37 ++++++++++++++ Makefile | 1 + builtin-upload-archive.c | 72 ++++++++++++++++++++++++++++ builtin.h | 1 + daemon.c | 7 +++ git.c | 1 + 7 files changed, 120 insertions(+) create mode 100644 Documentation/git-upload-archive.txt create mode 100644 builtin-upload-archive.c diff --git a/.gitignore b/.gitignore index a3f33d4209..90d6d7c667 100644 --- a/.gitignore +++ b/.gitignore @@ -119,6 +119,7 @@ git-unpack-objects git-update-index git-update-ref git-update-server-info +git-upload-archive git-upload-pack git-upload-tar git-var diff --git a/Documentation/git-upload-archive.txt b/Documentation/git-upload-archive.txt new file mode 100644 index 0000000000..388bb53d29 --- /dev/null +++ b/Documentation/git-upload-archive.txt @@ -0,0 +1,37 @@ +git-upload-archive(1) +==================== + +NAME +---- +git-upload-archive - Send archive + + +SYNOPSIS +-------- +'git-upload-archive' + +DESCRIPTION +----------- +Invoked by 'git-archive --remote' and sends a generated archive to the +other end over the git protocol. + +This command is usually not invoked directly by the end user. The UI +for the protocol is on the 'git-archive' side, and the program pair +is meant to be used to get an archive from a remote repository. + +OPTIONS +------- +:: + The repository to get a tar archive from. + +Author +------ +Written by Franck Bui-Huu. + +Documentation +-------------- +Documentation by Junio C Hamano and the git-list . + +GIT +--- +Part of the gitlink:git[7] suite diff --git a/Makefile b/Makefile index 8f62ceffaf..4ac85fdc7b 100644 --- a/Makefile +++ b/Makefile @@ -293,6 +293,7 @@ BUILTIN_OBJS = \ builtin-unpack-objects.o \ builtin-update-index.o \ builtin-update-ref.o \ + builtin-upload-archive.o \ builtin-upload-tar.o \ builtin-verify-pack.o \ builtin-write-tree.o \ diff --git a/builtin-upload-archive.c b/builtin-upload-archive.c new file mode 100644 index 0000000000..3bdb607e37 --- /dev/null +++ b/builtin-upload-archive.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2006 Franck Bui-Huu + */ +#include +#include "cache.h" +#include "builtin.h" +#include "archive.h" +#include "pkt-line.h" + +static const char upload_archive_usage[] = + "git-upload-archive "; + + +int cmd_upload_archive(int argc, const char **argv, const char *prefix) +{ + struct archiver ar; + const char *sent_argv[MAX_ARGS]; + const char *arg_cmd = "argument "; + char *p, buf[4096]; + int treeish_idx; + int sent_argc; + int len; + + if (argc != 2) + usage(upload_archive_usage); + + if (strlen(argv[1]) > sizeof(buf)) + die("insanely long repository name"); + + strcpy(buf, argv[1]); /* enter-repo smudges its argument */ + + if (!enter_repo(buf, 0)) + die("not a git archive"); + + /* put received options in sent_argv[] */ + sent_argc = 1; + sent_argv[0] = "git-upload-archive"; + for (p = buf;;) { + /* This will die if not enough free space in buf */ + len = packet_read_line(0, p, (buf + sizeof buf) - p); + if (len == 0) + break; /* got a flush */ + if (sent_argc > MAX_ARGS - 2) + die("Too many options (>29)"); + + if (p[len-1] == '\n') { + p[--len] = 0; + } + if (len < strlen(arg_cmd) || + strncmp(arg_cmd, p, strlen(arg_cmd))) + die("'argument' token or flush expected"); + + len -= strlen(arg_cmd); + memmove(p, p + strlen(arg_cmd), len); + sent_argv[sent_argc++] = p; + p += len; + *p++ = 0; + } + sent_argv[sent_argc] = NULL; + + /* parse all options sent by the client */ + treeish_idx = parse_archive_args(sent_argc, sent_argv, &ar); + + parse_treeish_arg(sent_argv + treeish_idx, &ar.args, prefix); + parse_pathspec_arg(sent_argv + treeish_idx + 1, &ar.args); + + packet_write(1, "ACK\n"); + packet_flush(1); + + return ar.write_archive(&ar.args); +} + diff --git a/builtin.h b/builtin.h index 50852cd6a9..6bfd2e79f5 100644 --- a/builtin.h +++ b/builtin.h @@ -57,6 +57,7 @@ extern int cmd_zip_tree(int argc, const char **argv, const char *prefix); extern int cmd_unpack_objects(int argc, const char **argv, const char *prefix); extern int cmd_update_index(int argc, const char **argv, const char *prefix); extern int cmd_update_ref(int argc, const char **argv, const char *prefix); +extern int cmd_upload_archive(int argc, const char **argv, const char *prefix); extern int cmd_upload_tar(int argc, const char **argv, const char *prefix); extern int cmd_version(int argc, const char **argv, const char *prefix); extern int cmd_whatchanged(int argc, const char **argv, const char *prefix); diff --git a/daemon.c b/daemon.c index b14d8083bb..a2954a0451 100644 --- a/daemon.c +++ b/daemon.c @@ -325,7 +325,14 @@ static int upload_pack(void) return -1; } +static int upload_archive(void) +{ + execl_git_cmd("upload-archive", ".", NULL); + return -1; +} + static struct daemon_service daemon_service[] = { + { "upload-archive", "uploadarch", upload_archive, 0, 1 }, { "upload-pack", "uploadpack", upload_pack, 1, 1 }, }; diff --git a/git.c b/git.c index 8c64b2753f..bcf3fc8444 100644 --- a/git.c +++ b/git.c @@ -262,6 +262,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "unpack-objects", cmd_unpack_objects, RUN_SETUP }, { "update-index", cmd_update_index, RUN_SETUP }, { "update-ref", cmd_update_ref, RUN_SETUP }, + { "upload-archive", cmd_upload_archive }, { "upload-tar", cmd_upload_tar }, { "version", cmd_version }, { "whatchanged", cmd_whatchanged, RUN_SETUP | USE_PAGER }, From 854c4168e77a692dc198311f04bf31939355f2a3 Mon Sep 17 00:00:00 2001 From: Rene Scharfe Date: Sat, 9 Sep 2006 17:02:38 +0200 Subject: [PATCH 05/18] git-archive: make compression level of ZIP archives configurable Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- archive.h | 1 + builtin-archive.c | 11 +++++++++-- builtin-zip-tree.c | 13 +++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/archive.h b/archive.h index f3d344b652..d8cca735d2 100644 --- a/archive.h +++ b/archive.h @@ -42,5 +42,6 @@ extern void parse_pathspec_arg(const char **pathspec, */ extern int write_tar_archive(struct archiver_args *); extern int write_zip_archive(struct archiver_args *); +extern void *parse_extra_zip_args(int argc, const char **argv); #endif /* ARCHIVE_H */ diff --git a/builtin-archive.c b/builtin-archive.c index 651d1bf6d9..b944737550 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -15,8 +15,15 @@ static const char archive_usage[] = \ "git-archive --format= [--prefix=/] [] [path...]"; struct archiver archivers[] = { - { .name = "tar", .write_archive = write_tar_archive }, - { .name = "zip", .write_archive = write_zip_archive }, + { + .name = "tar", + .write_archive = write_tar_archive, + }, + { + .name = "zip", + .write_archive = write_zip_archive, + .parse_extra = parse_extra_zip_args, + }, }; static int run_remote_archiver(struct archiver *ar, int argc, diff --git a/builtin-zip-tree.c b/builtin-zip-tree.c index 3afb7bde74..4e796338af 100644 --- a/builtin-zip-tree.c +++ b/builtin-zip-tree.c @@ -379,3 +379,16 @@ int write_zip_archive(struct archiver_args *args) return 0; } + +void *parse_extra_zip_args(int argc, const char **argv) +{ + for (; argc > 0; argc--, argv++) { + const char *arg = argv[0]; + + if (arg[0] == '-' && isdigit(arg[1]) && arg[2] == '\0') + zlib_compression_level = arg[1] - '0'; + else + die("Unknown argument for zip format: %s", arg); + } + return NULL; +} From 37f944363d5b5fb5bcbf2d184865534739713c01 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 9 Sep 2006 23:48:03 -0700 Subject: [PATCH 06/18] archive: allow remote to have more formats than we understand. This fixes git-archive --remote not to parse archiver arguments; otherwise if the remote end implements formats other than the one known locally we will not be able to access that format. Signed-off-by: Junio C Hamano --- archive.h | 1 - builtin-archive.c | 79 ++++++++++++++++++++++++++++------------------- 2 files changed, 47 insertions(+), 33 deletions(-) diff --git a/archive.h b/archive.h index d8cca735d2..e0782b9dcf 100644 --- a/archive.h +++ b/archive.h @@ -19,7 +19,6 @@ typedef void *(*parse_extra_args_fn_t)(int argc, const char **argv); struct archiver { const char *name; - const char *remote; struct archiver_args args; write_archive_fn_t write_archive; parse_extra_args_fn_t parse_extra; diff --git a/builtin-archive.c b/builtin-archive.c index b944737550..c70488c537 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -26,7 +26,7 @@ struct archiver archivers[] = { }, }; -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]; @@ -35,16 +35,13 @@ static int run_remote_archiver(struct archiver *ar, int argc, sprintf(buf, "git-upload-archive"); - url = xstrdup(ar->remote); + url = xstrdup(remote); pid = git_connect(fd, url, buf); if (pid < 0) return pid; - for (i = 1; i < argc; i++) { - if (!strncmp(argv[i], "--remote=", 9)) - 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)); @@ -150,17 +147,16 @@ 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 i; for (i = 1; i < argc; i++) { const char *arg = argv[i]; if (!strcmp(arg, "--list") || !strcmp(arg, "-l")) { - list = 1; - continue; + for (i = 0; i < ARRAY_SIZE(archivers); i++) + printf("%s\n", archivers[i].name); + exit(0); } if (!strncmp(arg, "--format=", 9)) { format = arg + 9; @@ -170,10 +166,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; @@ -187,44 +179,67 @@ 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) { + if (extra_argc) { + if (!ar->parse_extra) die("%s", default_parse_extra(ar, extra_argv)); - } ar->args.extra = ar->parse_extra(extra_argc, extra_argv); } - ar->remote = remote; ar->args.base = base; return i; } +static const char *remote_request(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; + remote = remote_request(&argc, argv); + if (remote) + return run_remote_archiver(remote, argc, argv); + + memset(&ar, 0, sizeof(ar)); tree_idx = parse_archive_args(argc, argv, &ar); - - if (ar.remote) - return run_remote_archiver(&ar, argc, argv); - if (prefix == NULL) prefix = setup_git_directory(); From 49a52b1d1fb120f52de3a67f1e4e5ae81512ab81 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 10 Sep 2006 01:06:33 -0700 Subject: [PATCH 07/18] Move sideband client side support into reusable form. This moves the receiver side of the sideband support from fetch-clone.c to sideband.c and its header file, so that archiver protocol can use it. Signed-off-by: Junio C Hamano --- Makefile | 4 ++-- fetch-clone.c | 32 +++++--------------------------- sideband.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ sideband.h | 11 +++++++++++ 4 files changed, 66 insertions(+), 29 deletions(-) create mode 100644 sideband.c create mode 100644 sideband.h diff --git a/Makefile b/Makefile index 7b3114f3aa..a46cd52713 100644 --- a/Makefile +++ b/Makefile @@ -233,7 +233,7 @@ XDIFF_LIB=xdiff/lib.a LIB_H = \ blob.h cache.h commit.h csum-file.h delta.h \ - diff.h object.h pack.h pkt-line.h quote.h refs.h \ + diff.h object.h pack.h pkt-line.h quote.h refs.h sideband.h \ run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \ tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h @@ -245,7 +245,7 @@ DIFF_OBJS = \ LIB_OBJS = \ blob.o commit.o connect.o csum-file.o cache-tree.o base85.o \ date.o diff-delta.o entry.o exec_cmd.o ident.o lockfile.o \ - object.o pack-check.o patch-delta.o path.o pkt-line.o \ + object.o pack-check.o patch-delta.o path.o pkt-line.o sideband.o \ quote.o read-cache.o refs.o run-command.o dir.o object-refs.o \ server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \ tag.o tree.o usage.o config.o environment.o ctype.o copy.o \ diff --git a/fetch-clone.c b/fetch-clone.c index c5cf4776fa..b62feac17d 100644 --- a/fetch-clone.c +++ b/fetch-clone.c @@ -1,6 +1,7 @@ #include "cache.h" #include "exec_cmd.h" #include "pkt-line.h" +#include "sideband.h" #include #include @@ -114,36 +115,13 @@ static pid_t setup_sideband(int sideband, const char *me, int fd[2], int xd[2]) die("%s: unable to fork off sideband demultiplexer", me); if (!side_pid) { /* subprocess */ + char buf[DEFAULT_PACKET_MAX]; + close(fd[0]); if (xd[0] != xd[1]) close(xd[1]); - while (1) { - char buf[1024]; - int len = packet_read_line(xd[0], buf, sizeof(buf)); - if (len == 0) - break; - if (len < 1) - die("%s: protocol error: no band designator", - me); - len--; - switch (buf[0] & 0xFF) { - case 3: - safe_write(2, "remote: ", 8); - safe_write(2, buf+1, len); - safe_write(2, "\n", 1); - exit(1); - case 2: - safe_write(2, "remote: ", 8); - safe_write(2, buf+1, len); - continue; - case 1: - safe_write(fd[1], buf+1, len); - continue; - default: - die("%s: protocol error: bad band #%d", - me, (buf[0] & 0xFF)); - } - } + if (recv_sideband(me, xd[0], fd[1], 2, buf, sizeof(buf))) + exit(1); exit(0); } close(xd[0]); diff --git a/sideband.c b/sideband.c new file mode 100644 index 0000000000..861f6219db --- /dev/null +++ b/sideband.c @@ -0,0 +1,48 @@ +#include "pkt-line.h" +#include "sideband.h" + +/* + * Receive multiplexed output stream over git native protocol. + * in_stream is the input stream from the remote, which carries data + * in pkt_line format with band designator. Demultiplex it into out + * and err and return error appropriately. Band #1 carries the + * primary payload. Things coming over band #2 is not necessarily + * error; they are usually informative message on the standard error + * stream, aka "verbose"). A message over band #3 is a signal that + * the remote died unexpectedly. A flush() concludes the stream. + */ +int recv_sideband(const char *me, int in_stream, int out, int err, char *buf, int bufsz) +{ + while (1) { + int len = packet_read_line(in_stream, buf, bufsz); + if (len == 0) + break; + if (len < 1) { + len = sprintf(buf, "%s: protocol error: no band designator\n", me); + safe_write(err, buf, len); + return SIDEBAND_PROTOCOL_ERROR; + } + len--; + switch (buf[0] & 0xFF) { + case 3: + safe_write(err, "remote: ", 8); + safe_write(err, buf+1, len); + safe_write(err, "\n", 1); + return SIDEBAND_REMOTE_ERROR; + case 2: + safe_write(err, "remote: ", 8); + safe_write(err, buf+1, len); + continue; + case 1: + safe_write(out, buf+1, len); + continue; + default: + len = sprintf(buf + 1, + "%s: protocol error: bad band #%d\n", + me, buf[0] & 0xFF); + safe_write(err, buf+1, len); + return SIDEBAND_PROTOCOL_ERROR; + } + } + return 0; +} diff --git a/sideband.h b/sideband.h new file mode 100644 index 0000000000..90b385580e --- /dev/null +++ b/sideband.h @@ -0,0 +1,11 @@ +#ifndef SIDEBAND_H +#define SIDEBAND_H + +#define SIDEBAND_PROTOCOL_ERROR -2 +#define SIDEBAND_REMOTE_ERROR -1 + +#define DEFAULT_PACKET_MAX 1000 + +int recv_sideband(const char *me, int in_stream, int out, int err, char *, int); + +#endif From 958c24b1b8f463bca857f45c41a2f8198e345c2f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 10 Sep 2006 03:20:24 -0700 Subject: [PATCH 08/18] Move sideband server side support into reusable form. The server side support; this is just the very low level, and the caller needs to know which band it wants to send things out. Signed-off-by: Junio C Hamano (cherry picked from b786552b67878c7780c50def4c069d46dc54efbe commit) --- sideband.c | 26 ++++++++++++++++++++++++++ sideband.h | 1 + upload-pack.c | 48 ++++++++++++------------------------------------ 3 files changed, 39 insertions(+), 36 deletions(-) diff --git a/sideband.c b/sideband.c index 861f6219db..1b14ff8892 100644 --- a/sideband.c +++ b/sideband.c @@ -46,3 +46,29 @@ int recv_sideband(const char *me, int in_stream, int out, int err, char *buf, in } return 0; } + +/* + * fd is connected to the remote side; send the sideband data + * over multiplexed packet stream. + */ +ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max) +{ + ssize_t ssz = sz; + const char *p = data; + + while (sz) { + unsigned n; + char hdr[5]; + + n = sz; + if (packet_max - 5 < n) + n = packet_max - 5; + sprintf(hdr, "%04x", n + 5); + hdr[4] = band; + safe_write(fd, hdr, 5); + safe_write(fd, p, n); + p += n; + sz -= n; + } + return ssz; +} diff --git a/sideband.h b/sideband.h index 90b385580e..c645cf2c52 100644 --- a/sideband.h +++ b/sideband.h @@ -7,5 +7,6 @@ #define DEFAULT_PACKET_MAX 1000 int recv_sideband(const char *me, int in_stream, int out, int err, char *, int); +ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max); #endif diff --git a/upload-pack.c b/upload-pack.c index 51ce936b06..1f2f7f75e5 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -4,6 +4,7 @@ #include "cache.h" #include "refs.h" #include "pkt-line.h" +#include "sideband.h" #include "tag.h" #include "object.h" #include "commit.h" @@ -33,45 +34,19 @@ static int strip(char *line, int len) return len; } -#define PACKET_MAX 1000 static ssize_t send_client_data(int fd, const char *data, ssize_t sz) { - ssize_t ssz; - const char *p; + if (use_sideband) + return send_sideband(1, fd, data, sz, DEFAULT_PACKET_MAX); - if (!data) { - if (!use_sideband) - return 0; - packet_flush(1); + if (fd == 3) + /* emergency quit */ + fd = 2; + if (fd == 2) { + xwrite(fd, data, sz); + return sz; } - - if (!use_sideband) { - if (fd == 3) - /* emergency quit */ - fd = 2; - if (fd == 2) { - xwrite(fd, data, sz); - return sz; - } - return safe_write(fd, data, sz); - } - p = data; - ssz = sz; - while (sz) { - unsigned n; - char hdr[5]; - - n = sz; - if (PACKET_MAX - 5 < n) - n = PACKET_MAX - 5; - sprintf(hdr, "%04x", n + 5); - hdr[4] = fd; - safe_write(1, hdr, 5); - safe_write(1, p, n); - p += n; - sz -= n; - } - return ssz; + return safe_write(fd, data, sz); } static void create_pack_file(void) @@ -308,7 +283,8 @@ static void create_pack_file(void) goto fail; fprintf(stderr, "flushed.\n"); } - send_client_data(1, NULL, 0); + if (use_sideband) + packet_flush(1); return; } fail: From 326711c16879792da8d7159bf29d080569c3a1e0 Mon Sep 17 00:00:00 2001 From: Rene Scharfe Date: Sun, 10 Sep 2006 18:10:01 +0200 Subject: [PATCH 09/18] Use xstrdup instead of strdup in builtin-{tar,zip}-tree.c Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano (cherry picked from 5d2aea4cb383a43e40d47ab69d8ad7a495df6ea2 commit) --- builtin-tar-tree.c | 2 +- builtin-zip-tree.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin-tar-tree.c b/builtin-tar-tree.c index c20eb0e364..e8e492fa0f 100644 --- a/builtin-tar-tree.c +++ b/builtin-tar-tree.c @@ -390,7 +390,7 @@ int write_tar_archive(struct archiver_args *args) write_global_extended_header(args->commit_sha1); if (args->base && plen > 0 && args->base[plen - 1] == '/') { - char *base = strdup(args->base); + char *base = xstrdup(args->base); int baselen = strlen(base); while (baselen > 0 && base[baselen - 1] == '/') diff --git a/builtin-zip-tree.c b/builtin-zip-tree.c index 4e796338af..fdac2bdd70 100644 --- a/builtin-zip-tree.c +++ b/builtin-zip-tree.c @@ -363,7 +363,7 @@ int write_zip_archive(struct archiver_args *args) zip_dir_size = ZIP_DIRECTORY_MIN_SIZE; if (args->base && plen > 0 && args->base[plen - 1] == '/') { - char *base = strdup(args->base); + char *base = xstrdup(args->base); int baselen = strlen(base); while (baselen > 0 && base[baselen - 1] == '/') From 8142f603b9955648228549d2e83ace7fbe834114 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 10 Sep 2006 04:16:39 -0700 Subject: [PATCH 10/18] archive: force line buffered output to stderr Otherwise the remote notification that comes with -v option can get clumped together. Signed-off-by: Junio C Hamano (cherry picked from a675cda60ead41f439b04bc69e0f19ace04e59d3 commit) --- builtin-archive.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/builtin-archive.c b/builtin-archive.c index c70488c537..3a8be57e15 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -238,6 +238,8 @@ int cmd_archive(int argc, const char **argv, const char *prefix) if (remote) return run_remote_archiver(remote, argc, argv); + setlinebuf(stderr); + memset(&ar, 0, sizeof(ar)); tree_idx = parse_archive_args(argc, argv, &ar); if (prefix == NULL) From e0ffb24877d4530208905512f7c91dd8d71e2c95 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 9 Sep 2006 22:42:02 -0700 Subject: [PATCH 11/18] Add --verbose to git-archive And teach backends about it. Signed-off-by: Junio C Hamano (cherry picked from 9e2c44a2893ae90944a0b7c9f40a9d22b759b5c0 commit) --- archive.h | 1 + builtin-archive.c | 8 +++++++- builtin-tar-tree.c | 4 ++++ builtin-zip-tree.c | 4 ++++ 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/archive.h b/archive.h index e0782b9dcf..16dcdb875c 100644 --- a/archive.h +++ b/archive.h @@ -10,6 +10,7 @@ struct archiver_args { const unsigned char *commit_sha1; time_t time; const char **pathspec; + unsigned int verbose : 1; void *extra; }; diff --git a/builtin-archive.c b/builtin-archive.c index 3a8be57e15..7544ad3ca1 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -12,7 +12,7 @@ #include "pkt-line.h" static const char archive_usage[] = \ -"git-archive --format= [--prefix=/] [] [path...]"; +"git-archive --format= [--prefix=/] [--verbose] [] [path...]"; struct archiver archivers[] = { { @@ -148,6 +148,7 @@ int parse_archive_args(int argc, const char **argv, struct archiver *ar) int extra_argc = 0; const char *format = NULL; /* might want to default to "tar" */ const char *base = ""; + int verbose = 0; int i; for (i = 1; i < argc; i++) { @@ -158,6 +159,10 @@ int parse_archive_args(int argc, const char **argv, struct archiver *ar) printf("%s\n", archivers[i].name); exit(0); } + if (!strcmp(arg, "--verbose") || !strcmp(arg, "-v")) { + verbose = 1; + continue; + } if (!strncmp(arg, "--format=", 9)) { format = arg + 9; continue; @@ -192,6 +197,7 @@ int parse_archive_args(int argc, const char **argv, struct archiver *ar) die("%s", default_parse_extra(ar, extra_argv)); ar->args.extra = ar->parse_extra(extra_argc, extra_argv); } + ar->args.verbose = verbose; ar->args.base = base; return i; diff --git a/builtin-tar-tree.c b/builtin-tar-tree.c index e8e492fa0f..f2679a8637 100644 --- a/builtin-tar-tree.c +++ b/builtin-tar-tree.c @@ -22,6 +22,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) @@ -169,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; @@ -385,6 +388,7 @@ int write_tar_archive(struct archiver_args *args) git_config(git_tar_config); archive_time = args->time; + verbose = args->verbose; if (args->commit_sha1) write_global_extended_header(args->commit_sha1); diff --git a/builtin-zip-tree.c b/builtin-zip-tree.c index fdac2bdd70..52d4b7a17e 100644 --- a/builtin-zip-tree.c +++ b/builtin-zip-tree.c @@ -13,6 +13,7 @@ static const char zip_tree_usage[] = "git-zip-tree [-0|...|-9] [ ]"; +static int verbose; static int zip_date; static int zip_time; @@ -164,6 +165,8 @@ static int write_zip_entry(const unsigned char *sha1, crc = crc32(0, Z_NULL, 0); path = construct_path(base, baselen, filename, S_ISDIR(mode), &pathlen); + if (verbose) + fprintf(stderr, "%s\n", path); if (pathlen > 0xffff) { error("path too long (%d chars, SHA1: %s): %s", pathlen, sha1_to_hex(sha1), path); @@ -361,6 +364,7 @@ int write_zip_archive(struct archiver_args *args) zip_dir = xmalloc(ZIP_DIRECTORY_MIN_SIZE); zip_dir_size = ZIP_DIRECTORY_MIN_SIZE; + verbose = args->verbose; if (args->base && plen > 0 && args->base[plen - 1] == '/') { char *base = xstrdup(args->base); From fe5ab763f848cfcda22001f9280625f06c4c3760 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 10 Sep 2006 04:02:57 -0700 Subject: [PATCH 12/18] Teach --exec to git-archive --remote Some people needed --exec to specify the location of the upload-pack executable, because their default SSH log-in does not include the directory they have their own private copy of git on the $PATH. These people need to be able to say --exec to git-archive --remote for the same reason. Signed-off-by: Junio C Hamano --- builtin-archive.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/builtin-archive.c b/builtin-archive.c index 7544ad3ca1..dd7ffc043d 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -32,16 +32,30 @@ static int run_remote_archiver(const char *remote, int argc, char *url, buf[1024]; 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(remote); - pid = git_connect(fd, url, buf); + pid = git_connect(fd, url, exec); if (pid < 0) return pid; - for (i = 1; i < argc; i++) + for (i = 1; i < argc; i++) { + if (i == exec_at) + continue; packet_write(fd[1], "argument %s\n", argv[i]); + } packet_flush(fd[1]); len = packet_read_line(fd[0], buf, sizeof(buf)); From d47f3db75c58139cdcbca5cc63b17bf5db293b6a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 10 Sep 2006 16:27:08 -0700 Subject: [PATCH 13/18] Prepare larger packet buffer for upload-pack protocol. The original side-band support added to the upload-pack protocol used the default 1000-byte packet length. The pkt-line format allows up to 64k, so prepare the receiver for the maximum size, and have the uploader and downloader negotiate if larger packet length is allowed. Signed-off-by: Junio C Hamano --- fetch-clone.c | 2 +- fetch-pack.c | 12 +++++++++--- sideband.h | 1 + upload-pack.c | 14 +++++++++----- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/fetch-clone.c b/fetch-clone.c index b62feac17d..b632ca0438 100644 --- a/fetch-clone.c +++ b/fetch-clone.c @@ -115,7 +115,7 @@ static pid_t setup_sideband(int sideband, const char *me, int fd[2], int xd[2]) die("%s: unable to fork off sideband demultiplexer", me); if (!side_pid) { /* subprocess */ - char buf[DEFAULT_PACKET_MAX]; + char buf[LARGE_PACKET_MAX]; close(fd[0]); if (xd[0] != xd[1]) diff --git a/fetch-pack.c b/fetch-pack.c index 377feded1c..1b2d6ee20d 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -166,10 +166,11 @@ static int find_common(int fd[2], unsigned char *result_sha1, } if (!fetching) - packet_write(fd[1], "want %s%s%s%s\n", + packet_write(fd[1], "want %s%s%s%s%s\n", sha1_to_hex(remote), (multi_ack ? " multi_ack" : ""), - (use_sideband ? " side-band" : ""), + (use_sideband == 2 ? " side-band-64k" : ""), + (use_sideband == 1 ? " side-band" : ""), (use_thin_pack ? " thin-pack" : "")); else packet_write(fd[1], "want %s\n", sha1_to_hex(remote)); @@ -426,7 +427,12 @@ static int fetch_pack(int fd[2], int nr_match, char **match) fprintf(stderr, "Server supports multi_ack\n"); multi_ack = 1; } - if (server_supports("side-band")) { + if (server_supports("side-band-64k")) { + if (verbose) + fprintf(stderr, "Server supports side-band-64k\n"); + use_sideband = 2; + } + else if (server_supports("side-band")) { if (verbose) fprintf(stderr, "Server supports side-band\n"); use_sideband = 1; diff --git a/sideband.h b/sideband.h index c645cf2c52..4872106fa0 100644 --- a/sideband.h +++ b/sideband.h @@ -5,6 +5,7 @@ #define SIDEBAND_REMOTE_ERROR -1 #define DEFAULT_PACKET_MAX 1000 +#define LARGE_PACKET_MAX 65520 int recv_sideband(const char *me, int in_stream, int out, int err, char *, int); ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max); diff --git a/upload-pack.c b/upload-pack.c index 1f2f7f75e5..b673d8cb97 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -20,6 +20,9 @@ static int use_thin_pack; static struct object_array have_obj; static struct object_array want_obj; static unsigned int timeout; +/* 0 for no sideband, + * otherwise maximum packet size (up to 65520 bytes). + */ static int use_sideband; static void reset_timeout(void) @@ -37,8 +40,7 @@ static int strip(char *line, int len) static ssize_t send_client_data(int fd, const char *data, ssize_t sz) { if (use_sideband) - return send_sideband(1, fd, data, sz, DEFAULT_PACKET_MAX); - + return send_sideband(1, fd, data, sz, use_sideband); if (fd == 3) /* emergency quit */ fd = 2; @@ -389,8 +391,10 @@ static void receive_needs(void) multi_ack = 1; if (strstr(line+45, "thin-pack")) use_thin_pack = 1; - if (strstr(line+45, "side-band")) - use_sideband = 1; + if (strstr(line+45, "side-band-64k")) + use_sideband = LARGE_PACKET_MAX; + else if (strstr(line+45, "side-band")) + use_sideband = DEFAULT_PACKET_MAX; /* We have sent all our refs already, and the other end * should have chosen out of them; otherwise they are @@ -412,7 +416,7 @@ static void receive_needs(void) static int send_ref(const char *refname, const unsigned char *sha1) { - static const char *capabilities = "multi_ack thin-pack side-band"; + static const char *capabilities = "multi_ack thin-pack side-band side-band-64k"; struct object *o = parse_object(sha1); if (!o) From 23d6d112c004d4242f9dbd8161f79ccdeb47bde8 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 10 Sep 2006 03:33:34 -0700 Subject: [PATCH 14/18] Add sideband status report to git-archive protocol Using the refactored sideband code from existing upload-pack protocol, this lets the error condition and status output sent from the remote process to be shown locally. Signed-off-by: Junio C Hamano --- builtin-archive.c | 6 +-- builtin-upload-archive.c | 94 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 93 insertions(+), 7 deletions(-) diff --git a/builtin-archive.c b/builtin-archive.c index dd7ffc043d..cb883dfe5f 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -10,6 +10,7 @@ #include "tree-walk.h" #include "exec_cmd.h" #include "pkt-line.h" +#include "sideband.h" static const char archive_usage[] = \ "git-archive --format= [--prefix=/] [--verbose] [] [path...]"; @@ -29,7 +30,7 @@ struct archiver archivers[] = { 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"; @@ -74,8 +75,7 @@ static int run_remote_archiver(const char *remote, 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, buf, sizeof(buf)); close(fd[0]); rv |= finish_connect(pid); diff --git a/builtin-upload-archive.c b/builtin-upload-archive.c index 3bdb607e37..42cb9f8876 100644 --- a/builtin-upload-archive.c +++ b/builtin-upload-archive.c @@ -6,12 +6,18 @@ #include "builtin.h" #include "archive.h" #include "pkt-line.h" +#include "sideband.h" +#include +#include static const char upload_archive_usage[] = "git-upload-archive "; +static const char deadchild[] = +"git-upload-archive: archiver died with error"; -int cmd_upload_archive(int argc, const char **argv, const char *prefix) + +static int run_upload_archive(int argc, const char **argv, const char *prefix) { struct archiver ar; const char *sent_argv[MAX_ARGS]; @@ -64,9 +70,89 @@ int cmd_upload_archive(int argc, const char **argv, const char *prefix) parse_treeish_arg(sent_argv + treeish_idx, &ar.args, prefix); parse_pathspec_arg(sent_argv + treeish_idx + 1, &ar.args); - packet_write(1, "ACK\n"); - packet_flush(1); - return ar.write_archive(&ar.args); } +int cmd_upload_archive(int argc, const char **argv, const char *prefix) +{ + pid_t writer; + int fd1[2], fd2[2]; + /* + * Set up sideband subprocess. + * + * We (parent) monitor and read from child, sending its fd#1 and fd#2 + * multiplexed out to our fd#1. If the child dies, we tell the other + * end over channel #3. + */ + if (pipe(fd1) < 0 || pipe(fd2) < 0) { + int err = errno; + packet_write(1, "NACK pipe failed on the remote side\n"); + die("upload-archive: %s", strerror(err)); + } + writer = fork(); + if (writer < 0) { + int err = errno; + packet_write(1, "NACK fork failed on the remote side\n"); + die("upload-archive: %s", strerror(err)); + } + if (!writer) { + /* child - connect fd#1 and fd#2 to the pipe */ + dup2(fd1[1], 1); + dup2(fd2[1], 2); + close(fd1[1]); close(fd2[1]); + close(fd1[0]); close(fd2[0]); /* we do not read from pipe */ + + exit(run_upload_archive(argc, argv, prefix)); + } + + /* parent - read from child, multiplex and send out to fd#1 */ + close(fd1[1]); close(fd2[1]); /* we do not write to pipe */ + packet_write(1, "ACK\n"); + packet_flush(1); + + while (1) { + struct pollfd pfd[2]; + char buf[16384]; + ssize_t sz; + pid_t pid; + int status; + + pfd[0].fd = fd1[0]; + pfd[0].events = POLLIN; + pfd[1].fd = fd2[0]; + pfd[1].events = POLLIN; + if (poll(pfd, 2, -1) < 0) { + if (errno != EINTR) { + error("poll failed resuming: %s", + strerror(errno)); + sleep(1); + } + continue; + } + if (pfd[0].revents & (POLLIN|POLLHUP)) { + /* Data stream ready */ + sz = read(pfd[0].fd, buf, sizeof(buf)); + send_sideband(1, 1, buf, sz, LARGE_PACKET_MAX); + } + if (pfd[1].revents & (POLLIN|POLLHUP)) { + /* Status stream ready */ + sz = read(pfd[1].fd, buf, sizeof(buf)); + send_sideband(1, 2, buf, sz, LARGE_PACKET_MAX); + } + + if (((pfd[0].revents | pfd[1].revents) & POLLHUP) == 0) + continue; + /* did it die? */ + pid = waitpid(writer, &status, WNOHANG); + if (!pid) { + fprintf(stderr, "Hmph, HUP?\n"); + continue; + } + if (!WIFEXITED(status) || WEXITSTATUS(status) > 0) + send_sideband(1, 3, deadchild, strlen(deadchild), + LARGE_PACKET_MAX); + packet_flush(1); + break; + } + return 0; +} From d3788e19e20cd14aeac99d1f294d9a368437284f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 12 Sep 2006 00:26:57 -0700 Subject: [PATCH 15/18] upload-archive: monitor child communication more carefully. Franck noticed that the code around polling and relaying messages from the child process was quite bogus. Here is an attempt to clean it up a bit, based on his patch: - When POLLHUP is set, it goes ahead and reads the file descriptor. Worse yet, it does not check the return value of read() for errors when it does. - When we processed one POLLIN, we should just go back and see if any more data is available. We can check if the child is still there when poll gave control back at us but without any actual input. [jc: with simplification suggested by Franck. ] Signed-off-by: Junio C Hamano --- builtin-upload-archive.c | 60 +++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/builtin-upload-archive.c b/builtin-upload-archive.c index 42cb9f8876..115a12dc13 100644 --- a/builtin-upload-archive.c +++ b/builtin-upload-archive.c @@ -16,6 +16,9 @@ static const char upload_archive_usage[] = static const char deadchild[] = "git-upload-archive: archiver died with error"; +static const char lostchild[] = +"git-upload-archive: archiver process was lost"; + static int run_upload_archive(int argc, const char **argv, const char *prefix) { @@ -73,6 +76,31 @@ static int run_upload_archive(int argc, const char **argv, const char *prefix) return ar.write_archive(&ar.args); } +static void error_clnt(const char *fmt, ...) +{ + char buf[1024]; + va_list params; + int len; + + va_start(params, fmt); + len = vsprintf(buf, fmt, params); + va_end(params); + send_sideband(1, 3, buf, len, LARGE_PACKET_MAX); + die("sent error to the client: %s", buf); +} + +static void process_input(int child_fd, int band) +{ + char buf[16384]; + ssize_t sz = read(child_fd, buf, sizeof(buf)); + if (sz < 0) { + if (errno != EINTR) + error_clnt("read error: %s\n", strerror(errno)); + return; + } + send_sideband(1, band, buf, sz, LARGE_PACKET_MAX); +} + int cmd_upload_archive(int argc, const char **argv, const char *prefix) { pid_t writer; @@ -112,9 +140,6 @@ int cmd_upload_archive(int argc, const char **argv, const char *prefix) while (1) { struct pollfd pfd[2]; - char buf[16384]; - ssize_t sz; - pid_t pid; int status; pfd[0].fd = fd1[0]; @@ -129,28 +154,19 @@ int cmd_upload_archive(int argc, const char **argv, const char *prefix) } continue; } - if (pfd[0].revents & (POLLIN|POLLHUP)) { + if (pfd[0].revents & POLLIN) /* Data stream ready */ - sz = read(pfd[0].fd, buf, sizeof(buf)); - send_sideband(1, 1, buf, sz, LARGE_PACKET_MAX); - } - if (pfd[1].revents & (POLLIN|POLLHUP)) { + process_input(pfd[0].fd, 1); + if (pfd[1].revents & POLLIN) /* Status stream ready */ - sz = read(pfd[1].fd, buf, sizeof(buf)); - send_sideband(1, 2, buf, sz, LARGE_PACKET_MAX); - } + process_input(pfd[1].fd, 2); + if ((pfd[0].revents | pfd[1].revents) == POLLIN) + continue; - if (((pfd[0].revents | pfd[1].revents) & POLLHUP) == 0) - continue; - /* did it die? */ - pid = waitpid(writer, &status, WNOHANG); - if (!pid) { - fprintf(stderr, "Hmph, HUP?\n"); - continue; - } - if (!WIFEXITED(status) || WEXITSTATUS(status) > 0) - send_sideband(1, 3, deadchild, strlen(deadchild), - LARGE_PACKET_MAX); + if (waitpid(writer, &status, 0) < 0) + error_clnt("%s", lostchild); + else if (!WIFEXITED(status) || WEXITSTATUS(status) > 0) + error_clnt("%s", deadchild); packet_flush(1); break; } From d751864cf7f5253da1ce749cf71215466590037f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 12 Sep 2006 22:42:31 -0700 Subject: [PATCH 16/18] builtin-archive.c: rename remote_request() to extract_remote_arg() Suggested by Franck, and I think it makes sense. Signed-off-by: Junio C Hamano --- builtin-archive.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin-archive.c b/builtin-archive.c index cb883dfe5f..da3f714705 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -217,7 +217,7 @@ int parse_archive_args(int argc, const char **argv, struct archiver *ar) return i; } -static const char *remote_request(int *ac, const char **av) +static const char *extract_remote_arg(int *ac, const char **av) { int ix, iy, cnt = *ac; int no_more_options = 0; @@ -254,7 +254,7 @@ int cmd_archive(int argc, const char **argv, const char *prefix) int tree_idx; const char *remote = NULL; - remote = remote_request(&argc, argv); + remote = extract_remote_arg(&argc, argv); if (remote) return run_remote_archiver(remote, argc, argv); From 2232c0c69ff114a23011a0698b119aeea0272e8b Mon Sep 17 00:00:00 2001 From: Rene Scharfe Date: Wed, 13 Sep 2006 22:55:04 +0200 Subject: [PATCH 17/18] git-archive: inline default_parse_extra() Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- builtin-archive.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/builtin-archive.c b/builtin-archive.c index da3f714705..6dabdee201 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -145,17 +145,6 @@ 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]; @@ -208,7 +197,8 @@ int parse_archive_args(int argc, const char **argv, struct archiver *ar) if (extra_argc) { if (!ar->parse_extra) - die("%s", default_parse_extra(ar, extra_argv)); + 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; From 87af29f09f119c4a4fa7fdf308483ae1abb74b49 Mon Sep 17 00:00:00 2001 From: Rene Scharfe Date: Sat, 16 Sep 2006 21:20:36 +0200 Subject: [PATCH 18/18] git-tar-tree: devolve git-tar-tree into a wrapper for git-archive This patch removes the custom tree walker tree_traverse(), and makes generate_tar() use write_tar_archive() and the infrastructure provided by git-archive instead. As a kind of side effect, make write_tar_archive() able to handle NULL as base directory, as this is what the new and simple generate_tar() uses to indicate the absence of a base directory. This was simpler and cleaner than playing tricks with empty strings. The behaviour of git-tar-tree should be unchanged (quick tests didn't indicate otherwise) except for the text of some error messages. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- builtin-tar-tree.c | 88 +++++++++------------------------------------- 1 file changed, 17 insertions(+), 71 deletions(-) diff --git a/builtin-tar-tree.c b/builtin-tar-tree.c index f2679a8637..437eb726a9 100644 --- a/builtin-tar-tree.c +++ b/builtin-tar-tree.c @@ -3,7 +3,6 @@ */ #include #include "cache.h" -#include "tree-walk.h" #include "commit.h" #include "strbuf.h" #include "tar.h" @@ -248,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")) { @@ -295,51 +263,29 @@ 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); + result = write_tar_archive(&args); + free(base); - 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(); - free(buffer); - free(current_path.buf); - return 0; + return result; } static int write_tar_entry(const unsigned char *sha1, @@ -383,7 +329,7 @@ static int write_tar_entry(const unsigned char *sha1, int write_tar_archive(struct archiver_args *args) { - int plen = strlen(args->base); + int plen = args->base ? strlen(args->base) : 0; git_config(git_tar_config);