Merge branch 'jc/sideband' into jc/archive
authorJunio C Hamano <junkio@cox.net>
Mon, 11 Sep 2006 00:58:45 +0000 (17:58 -0700)
committerJunio C Hamano <junkio@cox.net>
Mon, 11 Sep 2006 00:58:45 +0000 (17:58 -0700)
* jc/sideband:
Prepare larger packet buffer for upload-pack protocol.
Move sideband server side support into reusable form.
Move sideband client side support into reusable form.
get_sha1_hex() micro-optimization

13 files changed:
.gitignore
Documentation/git-archive.txt [new file with mode: 0644]
Documentation/git-upload-archive.txt [new file with mode: 0644]
Makefile
archive.h [new file with mode: 0644]
builtin-archive.c [new file with mode: 0644]
builtin-tar-tree.c
builtin-upload-archive.c [new file with mode: 0644]
builtin-zip-tree.c
builtin.h
daemon.c
generate-cmdlist.sh
git.c
index 0d608fe12ada909959a524ec60c4a6aa491bd3fc..90d6d7c667e414130e06bda7cebfc35b16b4b5c7 100644 (file)
@@ -8,6 +8,7 @@ git-apply
 git-applymbox
 git-applypatch
 git-archimport
+git-archive
 git-bisect
 git-branch
 git-cat-file
@@ -118,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-archive.txt b/Documentation/git-archive.txt
new file mode 100644 (file)
index 0000000..913528d
--- /dev/null
@@ -0,0 +1,100 @@
+git-archive(1)
+==============
+
+NAME
+----
+git-archive - Creates a archive of the files in the named tree
+
+
+SYNOPSIS
+--------
+'git-archive' --format=<fmt> [--list] [--prefix=<prefix>/] [<extra>]
+             [--remote=<repo>] <tree-ish> [path...]
+
+DESCRIPTION
+-----------
+Creates an archive of the specified format containing the tree
+structure for the named tree.  If <prefix> 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=<fmt>::
+       Format of the resulting archive: 'tar', 'zip'...
+
+--list::
+       Show all available formats.
+
+--prefix=<prefix>/::
+       Prepend <prefix>/ to each filename in the archive.
+
+<extra>::
+       This can be any options that the archiver backend understand.
+
+--remote=<repo>::
+       Instead of making a tar archive from local repository,
+       retrieve a tar archive from a remote repository.
+
+<tree-ish>::
+       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@vger.kernel.org>.
+
+GIT
+---
+Part of the gitlink:git[7] suite
diff --git a/Documentation/git-upload-archive.txt b/Documentation/git-upload-archive.txt
new file mode 100644 (file)
index 0000000..388bb53
--- /dev/null
@@ -0,0 +1,37 @@
+git-upload-archive(1)
+====================
+
+NAME
+----
+git-upload-archive - Send archive
+
+
+SYNOPSIS
+--------
+'git-upload-archive' <directory>
+
+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
+-------
+<directory>::
+       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@vger.kernel.org>.
+
+GIT
+---
+Part of the gitlink:git[7] suite
index a46cd52713a7adfccbb240f6696b20c1ce7bc370..c724b481a4426ed3336c12289eafdcb581c09e42 100644 (file)
--- 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 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
@@ -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 \
@@ -292,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/archive.h b/archive.h
new file mode 100644 (file)
index 0000000..16dcdb8
--- /dev/null
+++ b/archive.h
@@ -0,0 +1,47 @@
+#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;
+       unsigned int verbose : 1;
+       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;
+       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);
+/*
+ * Archive-format specific backends.
+ */
+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
new file mode 100644 (file)
index 0000000..dd7ffc0
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 2006 Franck Bui-Huu
+ * Copyright (c) 2006 Rene Scharfe
+ */
+#include <time.h>
+#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=<fmt> [--prefix=<prefix>/] [--verbose] [<extra>] <tree-ish> [path...]";
+
+struct archiver archivers[] = {
+       {
+               .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(const char *remote, int argc,
+                              const char **argv)
+{
+       char *url, buf[1024];
+       int fd[2], i, len, rv;
+       pid_t pid;
+       const char *exec = "git-upload-archive";
+       int exec_at = 0;
+
+       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, exec);
+       if (pid < 0)
+               return pid;
+
+       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));
+       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 *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 (!strncmp(arg, "--format=", 9)) {
+                       format = arg + 9;
+                       continue;
+               }
+               if (!strncmp(arg, "--prefix=", 9)) {
+                       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 (!format)
+               die("You must specify an archive format");
+       if (init_archiver(format, ar) < 0)
+               die("Unknown archive format '%s'", format);
+
+       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->args.verbose = verbose;
+       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);
+
+       setlinebuf(stderr);
+
+       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);
+}
index fa666f78c5b5e44617495abb2716eded8407626c..f2679a86378371293aca257d4e839e87f759ae18 100644 (file)
@@ -9,6 +9,7 @@
 #include "tar.h"
 #include "builtin.h"
 #include "pkt-line.h"
+#include "archive.h"
 
 #define RECORDSIZE     (512)
 #define BLOCKSIZE      (RECORDSIZE * 20)
@@ -21,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)
@@ -168,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;
@@ -338,6 +342,73 @@ 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;
+       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)
diff --git a/builtin-upload-archive.c b/builtin-upload-archive.c
new file mode 100644 (file)
index 0000000..3bdb607
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2006 Franck Bui-Huu
+ */
+#include <time.h>
+#include "cache.h"
+#include "builtin.h"
+#include "archive.h"
+#include "pkt-line.h"
+
+static const char upload_archive_usage[] =
+       "git-upload-archive <repo>";
+
+
+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);
+}
+
index 1c1f6830c1a8b9303eec3b1c11cfb1805d3049e4..52d4b7a17e39831964fe7c9c9c0cb374d0d61a62 100644 (file)
@@ -8,10 +8,12 @@
 #include "tree.h"
 #include "quote.h"
 #include "builtin.h"
+#include "archive.h"
 
 static const char zip_tree_usage[] =
 "git-zip-tree [-0|...|-9] <tree-ish> [ <base> ]";
 
+static int verbose;
 static int zip_date;
 static int zip_time;
 
@@ -163,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);
@@ -351,3 +355,44 @@ 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;
+       verbose = args->verbose;
+
+       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_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;
+}
+
+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;
+}
index 25431d70816634cec4ca6c54dc670ac536246277..6bfd2e79f5d88313ae38fed4544b434540acf427 100644 (file)
--- 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);
@@ -56,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);
index b14d8083bb14bd28a824c4417712842b1f70d829..a2954a0451316197621aed152c194d398aaee624 100644 (file)
--- 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 },
 };
 
index ec1eda20de8d510a9acfd981150a5f1a438a0fe4..5450918be339bf101a6562d518c0f4cc7b37c0f2 100755 (executable)
@@ -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 335f405c20b3b07af5bb2e96dd00a75eb47ff809..bcf3fc8444cf911866805dc84dadab04eae48fae 100644 (file)
--- 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 },
@@ -261,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 },