{"thread":{"id":"5483","subject":"[PATCH 1/2] Add git-archive","startedAt":"2006-09-05T12:16:59Z","lastAt":"2006-09-10T16:10:01Z","messageCount":40,"participants":["Franck Bui-Huu","Junio C Hamano","Rene Scharfe","Jakub Narebski"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"26356","messageId":"cda58cb80609050516v699338b9y57fd54f50c66e49e@mail.gmail.com","threadId":"5483","inReplyTo":null,"subject":"[PATCH 1/2] Add git-archive","fromName":"Franck Bui-Huu","fromEmail":"vagabon.xyz@gmail.com","sentAt":"2006-09-05T12:16:59Z","receivedAt":"2006-09-05T12:16:59Z","isPatch":true,"sender":{"key":"vagabon.xyz@gmail.com","avatar":null},"body":"git-archive is a command to make TAR and ZIP archives of a git tree.\nIt helps prevent a proliferation of git-{format}-tree commands.\n\nInstead of directly calling git-{tar,zip}-tree command, it defines\na very simple API, that archiver should implement and register in\n\"git-archive.c\". This API is made up by 2 functions whose prototype\nis defined in \"archive.h\" file.\n\n - The first one is used to parse 'extra' parameters which have\n   signification only for the specific archiver. That would allow\n   different archive backends to have different kind of options.\n\n - The second one is used to ask to an archive backend to build\n   the archive given some already resolved parameters.\n\nThe main reason for making this API is to avoid using\ngit-{tar,zip}-tree commands, hence making them useless. Maybe it's\ntime for them to die ?\n\nIt also implements remote operations by defining a very simple\nprotocol: it first sends the name of the specific uploader followed\nthe repository name (git-upload-tar git://example.org/repo.git).\nThen it sends \"arguments\" key word followed by all options given\nwhen invoking 'git-archive'.\n\nThe remote protocol is implemented in \"git-archive.c\" for client\nside and is triggered by \"--remote=<repo>\" option. For example,\nto fetch a TAR archive in a remote repo, you can issue:\n\n$ git archive --format=tar --remote=git://xxx/yyy/zzz.git HEAD\n\nWe choose to not make a new command \"git-fetch-archive\" for example,\navoind one more GIT command which should be nice for users (less\ncommands to remember, keeps existing --remote option).\n\nSigned-off-by: Franck Bui-Huu <vagabon.xyz@gmail.com>\n---\n .gitignore          |    1\n Makefile            |    3 -\n archive.h           |   43 ++++++++\n builtin-archive.c   |  262 +++++++++++++++++++++++++++++++++++++++++++++++++++\n builtin-tar-tree.c  |   66 +++++++++++++\n builtin.h           |    1\n generate-cmdlist.sh |    1\n git.c               |    1\n 8 files changed, 377 insertions(+), 1 deletions(-)\n\ndiff --git a/.gitignore b/.gitignore\nindex 78cb671..a3e7ca1 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -8,6 +8,7 @@ git-apply\n git-applymbox\n git-applypatch\n git-archimport\n+git-archive\n git-bisect\n git-branch\n git-cat-file\ndiff --git a/Makefile b/Makefile\nindex 389daf7..51ed4dd 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -242,7 +242,7 @@ LIB_FILE=libgit.a\n XDIFF_LIB=xdiff/lib.a\n\n LIB_H = \\\n-\tblob.h cache.h commit.h csum-file.h delta.h \\\n+\tarchive.h blob.h cache.h commit.h csum-file.h delta.h \\\n \tdiff.h object.h pack.h para-walk.h pkt-line.h quote.h refs.h \\\n \trun-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \\\n \ttree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h\n@@ -267,6 +267,7 @@ LIB_OBJS = \\\n BUILTIN_OBJS = \\\n \tbuiltin-add.o \\\n \tbuiltin-apply.o \\\n+\tbuiltin-archive.o \\\n \tbuiltin-cat-file.o \\\n \tbuiltin-checkout-index.o \\\n \tbuiltin-check-ref-format.o \\\ndiff --git a/archive.h b/archive.h\nnew file mode 100644\nindex 0000000..6c69953\n--- /dev/null\n+++ b/archive.h\n@@ -0,0 +1,43 @@\n+#ifndef ARCHIVE_H\n+#define ARCHIVE_H\n+\n+typedef int (*write_archive_fn_t)(struct tree *tree,\n+\t\t\t\t  const unsigned char *commit_sha1,\n+\t\t\t\t  const char *prefix,\n+\t\t\t\t  time_t time,\n+\t\t\t\t  const char **pathspec);\n+\n+typedef int (*parse_extra_args_fn_t)(int argc,\n+\t\t\t\t     const char **argv,\n+\t\t\t\t     const char **reason);\n+\n+struct archiver_struct {\n+\tconst char *name;\n+\twrite_archive_fn_t write_archive;\n+\tparse_extra_args_fn_t parse_extra;\n+\tconst char *remote;\n+\tconst char *prefix;\n+};\n+\n+extern struct archiver_struct archivers[];\n+\n+extern int parse_archive_args(int argc, const char **argv,\n+\t\t\t      struct archiver_struct **ar,\n+\t\t\t      const char **reason);\n+\n+extern int parse_treeish_arg(const char **argv,\n+\t\t\t     struct tree **tree,\n+\t\t\t     const unsigned char **commit_sha1,\n+\t\t\t     time_t *archive_time,\n+\t\t\t     const char *prefix,\n+\t\t\t     const char **reason);\n+/*\n+ *\n+ */\n+extern int write_tar_archive(struct tree *tree,\n+\t\t\t     const unsigned char *commit_sha1,\n+\t\t\t     const char *prefix,\n+\t\t\t     time_t time,\n+\t\t\t     const char **pathspec);\n+\n+#endif\t/* ARCHIVE_H */\ndiff --git a/builtin-archive.c b/builtin-archive.c\nnew file mode 100644\nindex 0000000..9341813\n--- /dev/null\n+++ b/builtin-archive.c\n@@ -0,0 +1,262 @@\n+/*\n+ * Copyright (c) 2006 Franck Bui-Huu\n+ * Copyright (c) 2006 Rene Scharfe\n+ */\n+#include <time.h>\n+#include \"cache.h\"\n+#include \"builtin.h\"\n+#include \"archive.h\"\n+#include \"commit.h\"\n+#include \"tree-walk.h\"\n+#include \"exec_cmd.h\"\n+#include \"pkt-line.h\"\n+\n+static const char archive_usage[] = \\\n+\"git-archive --format=<fmt> [--prefix=<prefix>/] [-0|...|-9]\n<tree-ish> [path...]\";\n+\n+struct archiver_struct archivers[] = {\n+\t{ \"tar\", write_tar_archive },\n+};\n+\n+static void concat_argv(int argc, const char **argv, char line[], int size)\n+{\n+\tchar *p;\n+\tint len, i;\n+\n+\tp = line;\n+\tfor (i = 1; i < argc; i++) {\n+\t\t/* server needn't these options */\n+\t\tif (!strncmp(argv[i], \"--format=\", 9) ||\n+\t\t    !strncmp(argv[i], \"--remote=\", 9))\n+\t\t\tcontinue;\n+\n+\t\tlen = strlen(argv[i]);\n+\t\tif (p + len + 1> line + size)\n+\t\t\tdie(\"too many options\");\n+\n+\t\tstrcpy(p, argv[i]);\n+\t\tp += len;\n+\t\t*p++ = ' ';\n+\t}\n+\tif (p > line)\n+\t\tp--;\n+\t*p = '\\0';\n+}\n+\n+static int run_remote_archiver(struct archiver_struct *ar, int argc,\n+\t\t\t       const char **argv)\n+{\n+\tchar *url, buf[1024];\n+\tpid_t pid;\n+\tint fd[2];\n+\tint len, rv;\n+\n+\tsprintf(buf, \"git-upload-%s\", ar->name);\n+\n+\turl = strdup(ar->remote);\n+\tpid = git_connect(fd, url, buf);\n+\tif (pid < 0)\n+\t\treturn pid;\n+\n+\tconcat_argv(argc, argv, buf, sizeof(buf));\n+\n+\tpacket_write(fd[1], \"arguments %s\\n\", buf);\n+\tpacket_flush(fd[1]);\n+\n+\tlen = packet_read_line(fd[0], buf, sizeof(buf));\n+\tif (!len)\n+\t\tdie(\"git-archive: expected ACK/NAK, got EOF\");\n+\tif (buf[len-1] == '\\n')\n+\t\tbuf[--len] = 0;\n+\tif (strcmp(buf, \"ACK\")) {\n+\t\tif (len > 5 && !strncmp(buf, \"NACK \", 5))\n+\t\t\tdie(\"git-archive: NACK %s\", buf + 5);\n+\t\tdie(\"git-archive: protocol error\");\n+\t}\n+\n+\tlen = packet_read_line(fd[0], buf, sizeof(buf));\n+\tif (len)\n+\t\tdie(\"git-archive: expected a flush\");\n+\n+\t/* Now, start reading from fd[0] and spit it out to stdout */\n+\trv = copy_fd(fd[0], 1);\n+\n+\tclose(fd[0]);\n+\trv |= finish_connect(pid);\n+\n+\treturn !!rv;\n+}\n+\n+static struct archiver_struct *get_archiver(const char *name)\n+{\n+\tstruct archiver_struct *ar = NULL;\n+\tint i;\n+\n+\tfor (i = 0; i < ARRAY_SIZE(archivers); i++) {\n+\t\tif (!strcmp(name, archivers[i].name)) {\n+\t\t\tar = &archivers[i];\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\treturn ar;\n+}\n+\n+int parse_treeish_arg(const char **argv, struct tree **tree,\n+\t\t      const unsigned char **commit_sha1,\n+\t\t      time_t *archive_time, const char *prefix,\n+\t\t      const char **reason)\n+{\n+\tconst char *name = argv[0];\n+\tstruct commit *commit;\n+\tunsigned char sha1[20];\n+\tint rv = -1;\n+\n+\tif (get_sha1(name, sha1)) {\n+\t\t*reason = \"Not a valid object name\";\n+\t\tgoto out;\n+\t}\n+\n+\tcommit = lookup_commit_reference_gently(sha1, 1);\n+\tif (commit) {\n+\t\t*commit_sha1 = commit->object.sha1;\n+\t\t*archive_time = commit->date;\n+\t} else {\n+\t\t*archive_time = time(NULL);\n+\t}\n+\n+\t*tree = parse_tree_indirect(sha1);\n+\tif (*tree == NULL) {\n+\t\t*reason = \"not a tree object\";\n+\t\tgoto out;\n+\t}\n+\n+\tif (prefix) {\n+\t\tunsigned char tree_sha1[20];\n+\t\tunsigned int mode;\n+\t\tint err;\n+\n+\t\terr = get_tree_entry((*tree)->object.sha1, prefix,\n+\t\t\t\t     tree_sha1, &mode);\n+\t\tif (err || !S_ISDIR(mode)) {\n+\t\t\t*reason = \"current working directory is untracked\";\n+\t\t\tgoto out;\n+\t\t}\n+\t\tfree(*tree);\n+\t\t*tree = parse_tree_indirect(tree_sha1);\n+\t}\n+\t//free(*tree);\n+\trv = 0;\n+out:\n+\treturn rv;\n+}\n+\n+int parse_archive_args(int argc, const char **argv,\n+\t\t       struct archiver_struct **ar,\n+\t\t       const char **reason)\n+{\n+\tconst char *format = NULL;\n+\tconst char *remote = NULL;\n+\tconst char *prefix = NULL;\n+\tint list = 0;\n+\tint rv = -1, i;\n+\n+\tfor (i = 1; i < argc; i++) {\n+\t\tconst char *arg = argv[i];\n+\n+\t\tif (!strcmp(arg, \"--list\") || !strcmp(arg, \"-l\")) {\n+\t\t\tlist = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strncmp(arg, \"--format=\", 9)) {\n+\t\t\tformat = arg + 9;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strncmp(arg, \"--prefix=\", 9)) {\n+\t\t\tprefix = arg + 9;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strncmp(arg, \"--remote=\", 9)) {\n+\t\t\tremote = arg + 9;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (arg[0] == '-' && isdigit(arg[1]) && arg[2] == '\\0') {\n+\t\t\tzlib_compression_level = arg[1] - '0';\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"--\")) {\n+\t\t\ti++;\n+\t\t\tbreak;\n+\t\t}\n+\t\tif (arg[0] == '-') {\n+\t\t\t*reason = archive_usage;\n+\t\t\tgoto out;\n+\t\t}\n+\t\tbreak;\n+\t}\n+\tif (list) {\n+\t\tif (!remote) {\n+\t\t\tint i;\n+\n+\t\t\tfor (i = 0; i < ARRAY_SIZE(archivers); i++)\n+\t\t\t\tprintf(\"%s\\n\", archivers[i].name);\n+\t\t\texit(0);\n+\t\t}\n+\t\t*reason = \"--list and --remote not supported together\";\n+\t\tgoto out;\n+\t}\n+\tif (argc - i < 1) {\n+\t\t*reason = archive_usage;\n+\t\tgoto out;\n+\t}\n+\tif (!format){\n+\t\t*reason = \"You must specify an archive format\";\n+\t\tgoto out;\n+\t}\n+\t*ar = get_archiver(format);\n+\tif (*ar == NULL) {\n+\t\t*reason = \"Unknown archive format\";\n+\t\tgoto out;\n+\t}\n+\tif ((*ar)->parse_extra) {\n+\t\tif ((*ar)->parse_extra(argc, argv, reason) < 0)\n+\t\t\tgoto out;\n+\t}\n+\t(*ar)->remote = remote;\n+\t(*ar)->prefix = prefix ? : \"\";\n+\trv = i;\n+out:\n+\treturn rv;\n+}\n+\n+int cmd_archive(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct archiver_struct *ar;\n+\tconst char *reason;\n+\tconst char **pathspec;\n+\tstruct tree *tree;\n+\tconst unsigned char *commit_sha1;\n+\ttime_t archive_time;\n+\tint rv;\n+\n+\trv = parse_archive_args(argc, argv, &ar, &reason);\n+\tif (rv < 0)\n+\t\tgoto err;\n+\n+\tif (ar->remote)\n+\t\treturn run_remote_archiver(ar, argc, argv);\n+\n+\tif (prefix == NULL)\n+\t\tprefix = setup_git_directory();\n+\n+\targv += rv;\n+\tif (parse_treeish_arg(argv, &tree, &commit_sha1,\n+\t\t\t      &archive_time, prefix, &reason) < 0)\n+\t\tgoto err;\n+\n+\tpathspec = get_pathspec(ar->prefix, argv + 1);\n+\n+\treturn ar->write_archive(tree, commit_sha1, ar->prefix,\n+\t\t\t\t archive_time, pathspec);\n+err:\n+\treturn error(\"%s\", reason);\n+}\ndiff --git a/builtin-tar-tree.c b/builtin-tar-tree.c\nindex 61a4135..e0da01e 100644\n--- a/builtin-tar-tree.c\n+++ b/builtin-tar-tree.c\n@@ -9,6 +9,7 @@ #include \"strbuf.h\"\n #include \"tar.h\"\n #include \"builtin.h\"\n #include \"pkt-line.h\"\n+#include \"archive.h\"\n\n #define RECORDSIZE\t(512)\n #define BLOCKSIZE\t(RECORDSIZE * 20)\n@@ -338,6 +339,71 @@ static int generate_tar(int argc, const\n \treturn 0;\n }\n\n+static int write_tar_entry(const unsigned char *sha1,\n+                           const char *base, int baselen,\n+                           const char *filename, unsigned mode, int stage)\n+{\n+\tstatic struct strbuf path;\n+\tint filenamelen = strlen(filename);\n+\tvoid *buffer;\n+\tchar type[20];\n+\tunsigned long size;\n+\n+\tif (!path.alloc) {\n+\t\tpath.buf = xmalloc(PATH_MAX);\n+\t\tpath.alloc = PATH_MAX;\n+\t\tpath.len = path.eof = 0;\n+\t}\n+\tif (path.alloc < baselen + filenamelen) {\n+\t\tfree(path.buf);\n+\t\tpath.buf = xmalloc(baselen + filenamelen);\n+\t\tpath.alloc = baselen + filenamelen;\n+\t}\n+\tmemcpy(path.buf, base, baselen);\n+\tmemcpy(path.buf + baselen, filename, filenamelen);\n+\tpath.len = baselen + filenamelen;\n+\tif (S_ISDIR(mode)) {\n+\t\tstrbuf_append_string(&path, \"/\");\n+\t\tbuffer = NULL;\n+\t\tsize = 0;\n+\t} else {\n+\t\tbuffer = read_sha1_file(sha1, type, &size);\n+\t\tif (!buffer)\n+\t\t\tdie(\"cannot read %s\", sha1_to_hex(sha1));\n+\t}\n+\n+\twrite_entry(sha1, &path, mode, buffer, size);\n+\n+\treturn READ_TREE_RECURSIVE;\n+}\n+\n+int write_tar_archive(struct tree *tree, const unsigned char *commit_sha1,\n+                      const char *prefix, time_t time, const char **pathspec)\n+{\n+\tint plen = strlen(prefix);\n+\n+\tgit_config(git_tar_config);\n+\n+\tarchive_time = time;\n+\n+\tif (commit_sha1)\n+\t\twrite_global_extended_header(commit_sha1);\n+\n+\tif (prefix && plen > 0 && prefix[plen - 1] == '/') {\n+\t\tchar *base = strdup(prefix);\n+\t\tint baselen = strlen(base);\n+\n+\t\twhile (baselen > 0 && base[baselen - 1] == '/')\n+\t\t\tbase[--baselen] = '\\0';\n+\t\twrite_tar_entry(tree->object.sha1, \"\", 0, base, 040777, 0);\n+\t\tfree(base);\n+\t}\n+\tread_tree_recursive(tree, prefix, plen, 0, pathspec, write_tar_entry);\n+\twrite_trailer();\n+\n+\treturn 0;\n+}\n+\n static const char *exec = \"git-upload-tar\";\n\n static int remote_tar(int argc, const char **argv)\ndiff --git a/builtin.h b/builtin.h\nindex 8472c79..2391afb 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -15,6 +15,7 @@ extern int write_tree(unsigned char *sha\n\n extern int cmd_add(int argc, const char **argv, const char *prefix);\n extern int cmd_apply(int argc, const char **argv, const char *prefix);\n+extern int cmd_archive(int argc, const char **argv, const char *prefix);\n extern int cmd_cat_file(int argc, const char **argv, const char *prefix);\n extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);\n extern int cmd_check_ref_format(int argc, const char **argv, const\nchar *prefix);\ndiff --git a/generate-cmdlist.sh b/generate-cmdlist.sh\nindex ec1eda2..5450918 100755\n--- a/generate-cmdlist.sh\n+++ b/generate-cmdlist.sh\n@@ -12,6 +12,7 @@ struct cmdname_help common_cmds[] = {\"\n sort <<\\EOF |\n add\n apply\n+archive\n bisect\n branch\n checkout\ndiff --git a/git.c b/git.c\nindex 82c8fee..c62c5cf 100644\n--- a/git.c\n+++ b/git.c\n@@ -218,6 +218,7 @@ static void handle_internal_command(int\n \t} commands[] = {\n \t\t{ \"add\", cmd_add, RUN_SETUP },\n \t\t{ \"apply\", cmd_apply },\n+\t\t{ \"archive\", cmd_archive },\n \t\t{ \"cat-file\", cmd_cat_file, RUN_SETUP },\n \t\t{ \"checkout-index\", cmd_checkout_index, RUN_SETUP },\n \t\t{ \"check-ref-format\", cmd_check_ref_format },\n-- \n1.4.2.gbba4\n"},{"id":"26368","messageId":"7vfyf6ce29.fsf@assigned-by-dhcp.cox.net","threadId":"5483","inReplyTo":"cda58cb80609050516v699338b9y57fd54f50c66e49e@mail.gmail.com","subject":"Re: [PATCH 1/2] Add git-archive","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-05T19:23:58Z","receivedAt":"2006-09-05T19:23:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Franck Bui-Huu\" <vagabon.xyz@gmail.com> writes:\n\n> git-archive is a command to make TAR and ZIP archives of a git tree.\n> It helps prevent a proliferation of git-{format}-tree commands.\n\nThanks.  I like the overall structure, at least mostly.\nAlso dropping -tree suffix from the command name is nice, short\nand sweet.\n\nObviously I cannot apply this patch because it is totally\nwhitespace damaged, but here are some comments.\n\n> diff --git a/archive.h b/archive.h\n> new file mode 100644\n> index 0000000..6c69953\n> --- /dev/null\n> +++ b/archive.h\n> @@ -0,0 +1,43 @@\n> +#ifndef ARCHIVE_H\n> +#define ARCHIVE_H\n> +\n> +typedef int (*write_archive_fn_t)(struct tree *tree,\n> +\t\t\t\t  const unsigned char *commit_sha1,\n> +\t\t\t\t  const char *prefix,\n> +\t\t\t\t  time_t time,\n> +\t\t\t\t  const char **pathspec);\n\nThe type of the first argument might have to be different,\ndepending on performance analysis by Rene on struct tree vs\nstruct tree_desc.\n\n> +typedef int (*parse_extra_args_fn_t)(int argc,\n> +\t\t\t\t     const char **argv,\n> +\t\t\t\t     const char **reason);\n> +\n\nI do not see a way for parse_extra to record the parameter it\nsuccessfully parsed, other than in a source-file-global, static\nvariable.  Not a very nice design for a library, if we are\nbuilding one from scratch.\n\nAlso, you are passing \"reason\" around from everywhere, but that\nis used by the caller to pass it to error(), so it might be\nsimpler to just call error() when you want to assign to *reason,\nand make an error return.  The caller does not have to do\nanything if you do that.  Your way might interact with the\nremote protocol better, though -- I haven't thought this part\nthrough yet so do not take this as a serious objection, but just\na comment.\n\n> +struct archiver_struct {\n> +\tconst char *name;\n> +\twrite_archive_fn_t write_archive;\n> +\tparse_extra_args_fn_t parse_extra;\n> +\tconst char *remote;\n> +\tconst char *prefix;\n> +};\n\nSomehow \"struct foo_struct\" makes me feel uneasy, when I do not\nsee the reason to call it \"struct foo\".\n\nAlso, the first three fields are permanent property of the\narchiver while two are to wrap runtime arguments of one\nparticular invocation.  I would have liked...\n\n> +extern struct archiver_struct archivers[];\n\n... this array to have only the former, and a separate structure\n\"struct archive_args\" to be defined.\n\n\tstruct archive_args {\n        \tconst char *remote;\n                const char *prefix;\n\t};\n\nAfter parse_archive_args finds the archiver specified with\n--format=*, it can call its parse_extra to retrieve a suitable\nstruct that has struct archive_args embedded at the beginning,\nand then set remote and prefix on the returned structure.\n\nThen a specific parse_extra implementation can be written like this:\n\n\tstatic struct tar_archive_args {\n        \tstruct archive_args a;\n                int z_compress;\n                ...\n\t};\n\n\tstruct archive_args *\n        tar_archive_parse_extra(int ac, const char **av)\n\t{\n        \tstruct tar_archive_args *args = xcalloc(1, sizeof(*args));\n\n\t\twhile (ac--) {\n\t\t\tconst char *arg = *++av;\n                \tif (arg[0] == '-' &&\n                            '0' <= arg[1] && arg[1] <= '9')\n\t\t\t\targs->z_compress = arg[1] - '0';\n\t\t\t...\n\t\t}\n\t\treturn (struct archive_args *)args;\n\t}\n\nand this can be passed to tar_archive_write_archive as an\nargument.\n\n> +extern int parse_treeish_arg(const char **argv,\n> +\t\t\t     struct tree **tree,\n> +\t\t\t     const unsigned char **commit_sha1,\n> +\t\t\t     time_t *archive_time,\n> +\t\t\t     const char *prefix,\n> +\t\t\t     const char **reason);\n> +extern int write_tar_archive(struct tree *tree,\n> +\t\t\t     const unsigned char *commit_sha1,\n> +\t\t\t     const char *prefix,\n> +\t\t\t     time_t time,\n> +\t\t\t     const char **pathspec);\n\nI suspect we would want \"struct tree_desc\" based interface,\ninstead of \"struct tree\".\n\n> +static const char archive_usage[] = \\\n> +\"git-archive --format=<fmt> [--prefix=<prefix>/] [-0|...|-9]\n> <tree-ish> [path...]\";\n\nI do not think \"-[0-9]\" belongs to generic \"git-archive\".  It\ndoes not make much sense to run compress on zip output.  More\nlike:\n\ngit-archive --format=<fmt> [--prefix=<prefix>] [format specific options] <tree-ish> [path...]\n\nIt has one potential advantage, though -- git-daemon _could_\nlook at it and notice that the client asks for too expensive\ncompression level.  But I do not think it is the only way to\nachive that to make \"-[0-9]\" a generic option.\n\n> +static int run_remote_archiver(struct archiver_struct *ar, int argc,\n> +\t\t\t       const char **argv)\n> +{\n> +\tchar *url, buf[1024];\n> +\tpid_t pid;\n> +\tint fd[2];\n> +\tint len, rv;\n> +\n> +\tsprintf(buf, \"git-upload-%s\", ar->name);\n\nAre you calling git-upload-{tar,zip,rar,...} here?\n\n> +\turl = strdup(ar->remote);\n> +\tpid = git_connect(fd, url, buf);\n> +\tif (pid < 0)\n> +\t\treturn pid;\n> +\n> +\tconcat_argv(argc, argv, buf, sizeof(buf));\n> +\tpacket_write(fd[1], \"arguments %s\\n\", buf);\n> +\tpacket_flush(fd[1]);\n\nParameter concatenation with SP is a bad idea for two reasons.\nYou cannot have SP in argument.  Also packet_write() may not\nlike the length of the arguments.\n\nA sequence of one argument per packet, with prefix \"argument \"\nfor future extension so that we can send other stuff if/when\nneeded, followed by a flush would be preferred.\n\n> +\t/* Now, start reading from fd[0] and spit it out to stdout */\n> +\trv = copy_fd(fd[0], 1);\n> +\tclose(fd[0]);\n> +\trv |= finish_connect(pid);\n\nIt was painful to bolt progress indicator support onto original\nupload-pack protocol, while making sure that older and newer\nclients and servers interoperate with each other.  Since this is\na new protocol, we should start with the side-band support from\nthe beginning (see upload-pack and look for use_sideband).\n\nInstead of sending the payload straight out, upload-archive side\nwould read from the underlying archiver, and send it with\none-byte prefix to say if it is a normal payload (band 1),\nmessage to stderr used to show progress indicator and error\nmessages (band 2), or error exit situation (band 3).  The client\nside here would receive the packetized data and do the reverse.\n\n> +int parse_treeish_arg(const char **argv, struct tree **tree,\n> +\t\t      const unsigned char **commit_sha1,\n> +\t\t      time_t *archive_time, const char *prefix,\n> +\t\t      const char **reason)\n> +{\n>...\n> +\tif (prefix) {\n> +\t\tunsigned char tree_sha1[20];\n> +\t\tunsigned int mode;\n> +\t\tint err;\n> +\n> +\t\terr = get_tree_entry((*tree)->object.sha1, prefix,\n> +\t\t\t\t     tree_sha1, &mode);\n> +\t\tif (err || !S_ISDIR(mode)) {\n> +\t\t\t*reason = \"current working directory is untracked\";\n> +\t\t\tgoto out;\n> +\t\t}\n> +\t\tfree(*tree);\n> +\t\t*tree = parse_tree_indirect(tree_sha1);\n> +\t}\n\nI like the simplicity of just optionally sending one subtree (or\nthe whole thing), but I think this part would be made more\nefficient if we go with \"struct tree_desc\" based interface.\n\nAlso I wonder how this interacts with the pathspec you take from\nthe command line.  Personally I think this single subtree\nsupport is good enough and limiting with pathspec is not needed.\n\n> +int parse_archive_args(int argc, const char **argv,\n> +\t\t       struct archiver_struct **ar,\n> +\t\t       const char **reason)\n> +{\n>...\n> +\t\tif (arg[0] == '-' && isdigit(arg[1]) && arg[2] == '\\0') {\n> +\t\t\tzlib_compression_level = arg[1] - '0';\n> +\t\t\tcontinue;\n> +\t\t}\n\nCommented on this part already.\n\n> +\tif (list) {\n> +\t\tif (!remote) {\n> +\t\t\tint i;\n> +\n> +\t\t\tfor (i = 0; i < ARRAY_SIZE(archivers); i++)\n> +\t\t\t\tprintf(\"%s\\n\", archivers[i].name);\n> +\t\t\texit(0);\n> +\t\t}\n\nYou do not need a different \"i\" that shadows the outer one here.\n\n> +\t(*ar)->remote = remote;\n> +\t(*ar)->prefix = prefix ? : \"\";\n\nPlease be nicer to other people by staying away from GNU\nextension \"A ? : B\", especially when A is so simple.\n"},{"id":"26415","messageId":"44FED12E.7010409@innova-card.com","threadId":"5483","inReplyTo":"7vfyf6ce29.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 1/2] Add git-archive","fromName":"Franck Bui-Huu","fromEmail":"vagabon.xyz@gmail.com","sentAt":"2006-09-06T13:46:22Z","receivedAt":"2006-09-06T13:46:22Z","isPatch":true,"sender":{"key":"vagabon.xyz@gmail.com","avatar":null},"body":"Junio C Hamano wrote:\n> \"Franck Bui-Huu\" <vagabon.xyz@gmail.com> writes:\n> \n>> git-archive is a command to make TAR and ZIP archives of a git tree.\n>> It helps prevent a proliferation of git-{format}-tree commands.\n> \n> Thanks.  I like the overall structure, at least mostly.\n> Also dropping -tree suffix from the command name is nice, short\n> and sweet.\n> \n\ngreat !\n\n> Obviously I cannot apply this patch because it is totally\n> whitespace damaged, but here are some comments.\n\n(sigh), sorry for that.\n\n> \n>> diff --git a/archive.h b/archive.h\n>> new file mode 100644\n>> index 0000000..6c69953\n>> --- /dev/null\n>> +++ b/archive.h\n>> @@ -0,0 +1,43 @@\n>> +#ifndef ARCHIVE_H\n>> +#define ARCHIVE_H\n>> +\n>> +typedef int (*write_archive_fn_t)(struct tree *tree,\n>> +\t\t\t\t  const unsigned char *commit_sha1,\n>> +\t\t\t\t  const char *prefix,\n>> +\t\t\t\t  time_t time,\n>> +\t\t\t\t  const char **pathspec);\n> \n> The type of the first argument might have to be different,\n> depending on performance analysis by Rene on struct tree vs\n> struct tree_desc.\n> \n\nOK. We'll wait for Rene.\n\n>> +typedef int (*parse_extra_args_fn_t)(int argc,\n>> +\t\t\t\t     const char **argv,\n>> +\t\t\t\t     const char **reason);\n>> +\n> \n> I do not see a way for parse_extra to record the parameter it\n> successfully parsed, other than in a source-file-global, static\n> variable.  Not a very nice design for a library, if we are\n> building one from scratch.\n> \n\nInteresting, could you explain why static variables are not nice ?\n\n> Also, you are passing \"reason\" around from everywhere, but that\n> is used by the caller to pass it to error(), so it might be\n> simpler to just call error() when you want to assign to *reason,\n> and make an error return.  The caller does not have to do\n> anything if you do that.  Your way might interact with the\n> remote protocol better, though -- I haven't thought this part\n> through yet so do not take this as a serious objection, but just\n> a comment.\n> \n\nYou might have missed my second patch:\n\n\t\t\"[PATCH 2/2] Add git-upload-archive\"\n\nBasically the server can also use 'reason' to report a failure\ndescription during NACK. I find it more useful than the simple\n\"server sent EOF\" error message.\n\n>> +struct archiver_struct {\n>> +\tconst char *name;\n>> +\twrite_archive_fn_t write_archive;\n>> +\tparse_extra_args_fn_t parse_extra;\n>> +\tconst char *remote;\n>> +\tconst char *prefix;\n>> +};\n> \n> Somehow \"struct foo_struct\" makes me feel uneasy, when I do not\n> see the reason to call it \"struct foo\".\n> \n\nno strong feeling here. I'll call it \"struct archiver\". BTW there\nare a couple of \"struct foo_struct\" in git source...\n\n> Also, the first three fields are permanent property of the\n> archiver while two are to wrap runtime arguments of one\n> particular invocation.  I would have liked...\n> \n\n'remote' case is not a generic argument that can be passed to\narchiver backends. Remember, the archiver backends only do local\noperation. They do not know about remote protocol which is part\nof git-archive command. That's the reason why I think we shouldn't\nmake this field part of arguments structure. It completely change\nthe behaviour of git-archive when it is used.\n\n>> +extern struct archiver_struct archivers[];\n> \n> ... this array to have only the former, and a separate structure\n> \"struct archive_args\" to be defined.\n> \n> \tstruct archive_args {\n>         \tconst char *remote;\n>                 const char *prefix;\n> \t};\n> \n> After parse_archive_args finds the archiver specified with\n> --format=*, it can call its parse_extra to retrieve a suitable\n> struct that has struct archive_args embedded at the beginning,\n> and then set remote and prefix on the returned structure.\n> \n\nOne bad side is that we need to malloc this embedded structure.\nTherefore we have to free this embedded structure somewhere. \n\nWe could have the following structures in archive.h, but we need\nto export all these archiver backend definitions.\n\n\tstruct tar_archive_args {\n\t\tint z_compress;\n\t};\n\n\tstruct tar_archive_args {\n\t\t[...]\n\t};\n\n\tstruct archive_args {\n\t\tconst char\t\t*prefix;\n\t\tstruct tree\t\t*tree;\n\t\tconst unsigned char\t*commit_sha1;\n\t\tconst char\t\t*prefix;\n\t\ttime_t\t\t\ttime;\n\t\tconst char\t\t**pathspec;\n\t\tunion {\n\t\t\tstruct tar_archive_args tar_args;\n\t\t\tstruct zip_archive_args zip_args;\n\t\t} u;\n\t};\n\n\tstruct archiver {\n\t\tconst char *name;\n\t\twrite_archive_fn_t write_archive;\n\t\tparse_extra_args_fn_t parse_extra;\n\t\tconst char *remote;\n\t};\n\n\ttypedef int (*write_archive_fn_t)(struct archive_args *archive_args);\n\n> Then a specific parse_extra implementation can be written like this:\n> \n> \tstatic struct tar_archive_args {\n>         \tstruct archive_args a;\n>                 int z_compress;\n>                 ...\n> \t};\n> \n> \tstruct archive_args *\n>         tar_archive_parse_extra(int ac, const char **av)\n> \t{\n>         \tstruct tar_archive_args *args = xcalloc(1, sizeof(*args));\n> \n> \t\twhile (ac--) {\n> \t\t\tconst char *arg = *++av;\n>                 \tif (arg[0] == '-' &&\n>                             '0' <= arg[1] && arg[1] <= '9')\n> \t\t\t\targs->z_compress = arg[1] - '0';\n> \t\t\t...\n> \t\t}\n> \t\treturn (struct archive_args *)args;\n> \t}\n> \n> and this can be passed to tar_archive_write_archive as an\n> argument.\n> \n>> +extern int parse_treeish_arg(const char **argv,\n>> +\t\t\t     struct tree **tree,\n>> +\t\t\t     const unsigned char **commit_sha1,\n>> +\t\t\t     time_t *archive_time,\n>> +\t\t\t     const char *prefix,\n>> +\t\t\t     const char **reason);\n>> +extern int write_tar_archive(struct tree *tree,\n>> +\t\t\t     const unsigned char *commit_sha1,\n>> +\t\t\t     const char *prefix,\n>> +\t\t\t     time_t time,\n>> +\t\t\t     const char **pathspec);\n> \n> I suspect we would want \"struct tree_desc\" based interface,\n> instead of \"struct tree\".\n> \n>> +static const char archive_usage[] = \\\n>> +\"git-archive --format=<fmt> [--prefix=<prefix>/] [-0|...|-9]\n>> <tree-ish> [path...]\";\n> \n> I do not think \"-[0-9]\" belongs to generic \"git-archive\".  It\n> does not make much sense to run compress on zip output.  More\n> like:\n> \n\nI forgot to remove that.\n\n> git-archive --format=<fmt> [--prefix=<prefix>] [format specific options] <tree-ish> [path...]\n> \n\nI forgot to change that.\n\n> \n>> +static int run_remote_archiver(struct archiver_struct *ar, int argc,\n>> +\t\t\t       const char **argv)\n>> +{\n>> +\tchar *url, buf[1024];\n>> +\tpid_t pid;\n>> +\tint fd[2];\n>> +\tint len, rv;\n>> +\n>> +\tsprintf(buf, \"git-upload-%s\", ar->name);\n> \n> Are you calling git-upload-{tar,zip,rar,...} here?\n> \n\nyes. Actually git-upload-{tar,zip,...} commands are going to be\nremoved, but git-daemon know them as a daemon service. It will\nmap these services to the generic \"git-upload-archive\" command.\nOne benefit is that we could still disable TAR format and enable\nTGZ one. Please take a look to the second patch that adds\ngit-upload-archive command.\n\n>> +\turl = strdup(ar->remote);\n>> +\tpid = git_connect(fd, url, buf);\n>> +\tif (pid < 0)\n>> +\t\treturn pid;\n>> +\n>> +\tconcat_argv(argc, argv, buf, sizeof(buf));\n>> +\tpacket_write(fd[1], \"arguments %s\\n\", buf);\n>> +\tpacket_flush(fd[1]);\n> \n> Parameter concatenation with SP is a bad idea for two reasons.\n> You cannot have SP in argument.  Also packet_write() may not\n> like the length of the arguments.\n> \n> A sequence of one argument per packet, with prefix \"argument \"\n> for future extension so that we can send other stuff if/when\n> needed, followed by a flush would be preferred.\n\nAbsolutely.\n\n> \n>> +\t/* Now, start reading from fd[0] and spit it out to stdout */\n>> +\trv = copy_fd(fd[0], 1);\n>> +\tclose(fd[0]);\n>> +\trv |= finish_connect(pid);\n> \n> It was painful to bolt progress indicator support onto original\n> upload-pack protocol, while making sure that older and newer\n> clients and servers interoperate with each other.  Since this is\n> a new protocol, we should start with the side-band support from\n> the beginning (see upload-pack and look for use_sideband).\n> \n> Instead of sending the payload straight out, upload-archive side\n> would read from the underlying archiver, and send it with\n> one-byte prefix to say if it is a normal payload (band 1),\n> message to stderr used to show progress indicator and error\n> messages (band 2), or error exit situation (band 3).  The client\n> side here would receive the packetized data and do the reverse.\n> \n\nOK, I'll take a look\n\n>> +int parse_treeish_arg(const char **argv, struct tree **tree,\n>> +\t\t      const unsigned char **commit_sha1,\n>> +\t\t      time_t *archive_time, const char *prefix,\n>> +\t\t      const char **reason)\n>> +{\n>> ...\n>> +\tif (prefix) {\n>> +\t\tunsigned char tree_sha1[20];\n>> +\t\tunsigned int mode;\n>> +\t\tint err;\n>> +\n>> +\t\terr = get_tree_entry((*tree)->object.sha1, prefix,\n>> +\t\t\t\t     tree_sha1, &mode);\n>> +\t\tif (err || !S_ISDIR(mode)) {\n>> +\t\t\t*reason = \"current working directory is untracked\";\n>> +\t\t\tgoto out;\n>> +\t\t}\n>> +\t\tfree(*tree);\n>> +\t\t*tree = parse_tree_indirect(tree_sha1);\n>> +\t}\n> \n> I like the simplicity of just optionally sending one subtree (or\n> the whole thing), but I think this part would be made more\n> efficient if we go with \"struct tree_desc\" based interface.\n> \n> Also I wonder how this interacts with the pathspec you take from\n> the command line.  Personally I think this single subtree\n> support is good enough and limiting with pathspec is not needed.\n> \n\nAs I said in a previous email, I'm new with git internals. I prefer\nlet that part to others who better have a better knowledge on the\nsubject. I'll dig into that later though...\n\n>> +int parse_archive_args(int argc, const char **argv,\n>> +\t\t       struct archiver_struct **ar,\n>> +\t\t       const char **reason)\n>> +{\n>> ...\n>> +\t\tif (arg[0] == '-' && isdigit(arg[1]) && arg[2] == '\\0') {\n>> +\t\t\tzlib_compression_level = arg[1] - '0';\n>> +\t\t\tcontinue;\n>> +\t\t}\n> \n> Commented on this part already.\n> \n\nOK\n\n>> +\tif (list) {\n>> +\t\tif (!remote) {\n>> +\t\t\tint i;\n>> +\n>> +\t\t\tfor (i = 0; i < ARRAY_SIZE(archivers); i++)\n>> +\t\t\t\tprintf(\"%s\\n\", archivers[i].name);\n>> +\t\t\texit(0);\n>> +\t\t}\n> \n> You do not need a different \"i\" that shadows the outer one here.\n\nYes.\n\n> \n>> +\t(*ar)->remote = remote;\n>> +\t(*ar)->prefix = prefix ? : \"\";\n> \n> Please be nicer to other people by staying away from GNU\n> extension \"A ? : B\", especially when A is so simple.\n> \nsure.\n\nThanks for your comments,\n\n\t\tFranck\n"},{"id":"26440","messageId":"44FF2C37.2010400@lsrfire.ath.cx","threadId":"5483","inReplyTo":"44FED12E.7010409@innova-card.com","subject":"Re: [PATCH 1/2] Add git-archive","fromName":"Rene Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2006-09-06T20:14:47Z","receivedAt":"2006-09-06T20:14:47Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Franck Bui-Huu schrieb:\n> Junio C Hamano wrote:\n>> \"Franck Bui-Huu\" <vagabon.xyz@gmail.com> writes:\n>>\n>>> git-archive is a command to make TAR and ZIP archives of a git tree.\n>>> It helps prevent a proliferation of git-{format}-tree commands.\n>> Thanks.  I like the overall structure, at least mostly.\n>> Also dropping -tree suffix from the command name is nice, short\n>> and sweet.\n>>\n> \n> great !\n> \n>> Obviously I cannot apply this patch because it is totally\n>> whitespace damaged, but here are some comments.\n> \n> (sigh), sorry for that.\n> \n>>> diff --git a/archive.h b/archive.h\n>>> new file mode 100644\n>>> index 0000000..6c69953\n>>> --- /dev/null\n>>> +++ b/archive.h\n>>> @@ -0,0 +1,43 @@\n>>> +#ifndef ARCHIVE_H\n>>> +#define ARCHIVE_H\n>>> +\n>>> +typedef int (*write_archive_fn_t)(struct tree *tree,\n>>> +\t\t\t\t  const unsigned char *commit_sha1,\n>>> +\t\t\t\t  const char *prefix,\n>>> +\t\t\t\t  time_t time,\n>>> +\t\t\t\t  const char **pathspec);\n>> The type of the first argument might have to be different,\n>> depending on performance analysis by Rene on struct tree vs\n>> struct tree_desc.\n>>\n> \n> OK. We'll wait for Rene.\n\nThe performance difference I noticed was caused by a memleak; the speed\nadvantage of a struct tree_desc based traverser is significant if you\nlook only at the traversers' performance, but it is lost in the noise\nof the \"real\" work that the payload function is doing (see my other\nmail).\n\n>>> +static int run_remote_archiver(struct archiver_struct *ar, int argc,\n>>> +\t\t\t       const char **argv)\n>>> +{\n>>> +\tchar *url, buf[1024];\n>>> +\tpid_t pid;\n>>> +\tint fd[2];\n>>> +\tint len, rv;\n>>> +\n>>> +\tsprintf(buf, \"git-upload-%s\", ar->name);\n>> Are you calling git-upload-{tar,zip,rar,...} here?\n>>\n> \n> yes. Actually git-upload-{tar,zip,...} commands are going to be\n> removed, but git-daemon know them as a daemon service. It will\n> map these services to the generic \"git-upload-archive\" command.\n> One benefit is that we could still disable TAR format and enable\n> TGZ one. Please take a look to the second patch that adds\n> git-upload-archive command.\n\nI don't think git-daemon should need to care about specific\narchivers.  Policy decisions, like disallowing certain archive types\nor compression levels, should be made in git-upload-archive.  This\nway all code regarding archive uploading is found in one place:\ngit-upload-archive.  We can keep git-upload-tar as a legacy\ninterface, but please use only git-upload-archive for the new stuff\n(and not git-upload-zip etc.).\n\n>>> +int parse_treeish_arg(const char **argv, struct tree **tree,\n>>> +\t\t      const unsigned char **commit_sha1,\n>>> +\t\t      time_t *archive_time, const char *prefix,\n>>> +\t\t      const char **reason)\n>>> +{\n>>> ...\n>>> +\tif (prefix) {\n>>> +\t\tunsigned char tree_sha1[20];\n>>> +\t\tunsigned int mode;\n>>> +\t\tint err;\n>>> +\n>>> +\t\terr = get_tree_entry((*tree)->object.sha1, prefix,\n>>> +\t\t\t\t     tree_sha1, &mode);\n>>> +\t\tif (err || !S_ISDIR(mode)) {\n>>> +\t\t\t*reason = \"current working directory is untracked\";\n>>> +\t\t\tgoto out;\n>>> +\t\t}\n>>> +\t\tfree(*tree);\n>>> +\t\t*tree = parse_tree_indirect(tree_sha1);\n>>> +\t}\n>> I like the simplicity of just optionally sending one subtree (or\n>> the whole thing), but I think this part would be made more\n>> efficient if we go with \"struct tree_desc\" based interface.\n>>\n>> Also I wonder how this interacts with the pathspec you take from\n>> the command line.  Personally I think this single subtree\n>> support is good enough and limiting with pathspec is not needed.\n\n[Note: There's potential for confusion here because we have two\ntypes of prefixes.  One is the present working directory inside the\ngit archive, the other is the one specified with --prefix=.  Here\nwe have the working directory kind of prefix.]\n\nIMHO should work like in the following example, and the code above\ncuts off the Documentation part:\n\n   $ cd Documentation\n   $ git-archive --format=tar --prefix=v1.0/ HEAD howto | tar tf -\n   v1.0/howto/\n   v1.0/howto/isolate-bugs-with-bisect.txt\n   ...\n\nI agree that simple subtree matching would be enough, at least for\nnow.\n\nRené\n"},{"id":"26441","messageId":"44FF2CF1.9020201@lsrfire.ath.cx","threadId":"5483","inReplyTo":"cda58cb80609050516v699338b9y57fd54f50c66e49e@mail.gmail.com","subject":"Re: [PATCH 1/2] Add git-archive","fromName":"Rene Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2006-09-06T20:17:53Z","receivedAt":"2006-09-06T20:17:53Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Franck Bui-Huu schrieb:\n> diff --git a/builtin-tar-tree.c b/builtin-tar-tree.c\n> index 61a4135..e0da01e 100644\n> --- a/builtin-tar-tree.c\n> +++ b/builtin-tar-tree.c\n> @@ -9,6 +9,7 @@ #include \"strbuf.h\"\n> #include \"tar.h\"\n> #include \"builtin.h\"\n> #include \"pkt-line.h\"\n> +#include \"archive.h\"\n> \n> #define RECORDSIZE    (512)\n> #define BLOCKSIZE    (RECORDSIZE * 20)\n> @@ -338,6 +339,71 @@ static int generate_tar(int argc, const\n>     return 0;\n> }\n> \n> +static int write_tar_entry(const unsigned char *sha1,\n> +                           const char *base, int baselen,\n> +                           const char *filename, unsigned mode, int stage)\n> +{\n> +    static struct strbuf path;\n> +    int filenamelen = strlen(filename);\n> +    void *buffer;\n> +    char type[20];\n> +    unsigned long size;\n> +\n> +    if (!path.alloc) {\n> +        path.buf = xmalloc(PATH_MAX);\n> +        path.alloc = PATH_MAX;\n> +        path.len = path.eof = 0;\n> +    }\n> +    if (path.alloc < baselen + filenamelen) {\n> +        free(path.buf);\n> +        path.buf = xmalloc(baselen + filenamelen);\n> +        path.alloc = baselen + filenamelen;\n> +    }\n> +    memcpy(path.buf, base, baselen);\n> +    memcpy(path.buf + baselen, filename, filenamelen);\n> +    path.len = baselen + filenamelen;\n> +    if (S_ISDIR(mode)) {\n> +        strbuf_append_string(&path, \"/\");\n> +        buffer = NULL;\n> +        size = 0;\n> +    } else {\n> +        buffer = read_sha1_file(sha1, type, &size);\n> +        if (!buffer)\n> +            die(\"cannot read %s\", sha1_to_hex(sha1));\n> +    }\n> +\n> +    write_entry(sha1, &path, mode, buffer, size);\n\nHere occurs the memory leak that I've been talking about.  buffer needs\nto be free'd.\n\n> +\n> +    return READ_TREE_RECURSIVE;\n> +}\n"},{"id":"26442","messageId":"ednb29$u0u$1@sea.gmane.org","threadId":"5483","inReplyTo":"44FF2C37.2010400@lsrfire.ath.cx","subject":"Re: [PATCH 1/2] Add git-archive","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-09-06T20:29:19Z","receivedAt":"2006-09-06T20:29:19Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Rene Scharfe wrote:\n\n> IMHO should work like in the following example, and the code above\n> cuts off the Documentation part:\n> \n>    $ cd Documentation\n>    $ git-archive --format=tar --prefix=v1.0/ HEAD howto | tar tf -\n>    v1.0/howto/\n>    v1.0/howto/isolate-bugs-with-bisect.txt\n>    ...\n> \n> I agree that simple subtree matching would be enough, at least for\n> now.\n\nWhat about\n\n     $ git-archive --format=tar --prefix=v1.0/ HEAD:Documentation/howto\n\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n"},{"id":"26446","messageId":"7vac5c7jty.fsf@assigned-by-dhcp.cox.net","threadId":"5483","inReplyTo":"44FED12E.7010409@innova-card.com","subject":"Re: [PATCH 1/2] Add git-archive","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-06T21:42:49Z","receivedAt":"2006-09-06T21:42:49Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Franck Bui-Huu <vagabon.xyz@gmail.com> writes:\n\n>>> +typedef int (*parse_extra_args_fn_t)(int argc,\n>>> +\t\t\t\t     const char **argv,\n>>> +\t\t\t\t     const char **reason);\n>>> +\n>> \n>> I do not see a way for parse_extra to record the parameter it\n>> successfully parsed, other than in a source-file-global, static\n>> variable.  Not a very nice design for a library, if we are\n>> building one from scratch.\n>\n> Interesting, could you explain why static variables are not nice ?\n\nMostly taste and a little bit of re-entrancy worries.\n\n> You might have missed my second patch:\n>\n> \t\t\"[PATCH 2/2] Add git-upload-archive\"\n>\n> Basically the server can also use 'reason' to report a failure\n> description during NACK. I find it more useful than the simple\n> \"server sent EOF\" error message.\n\nThat's a good intention, but we would also need to convey the\n\"server side found problem and died with these error() output\"\nanyway, so it would be covered either way (see how error()/die()\nmessages from git-upload-pack are given to git-fetch-pack over\nthe wire).\n\n> 'remote' case is not a generic argument that can be passed to\n> archiver backends. Remember, the archiver backends only do local\n> operation. They do not know about remote protocol which is part\n> of git-archive command. That's the reason why I think we shouldn't\n> make this field part of arguments structure.\n\nOk.  Passing that as a separate paramter would make sense.\n\n>> After parse_archive_args finds the archiver specified with\n>> --format=*, it can call its parse_extra to retrieve a suitable\n>> struct that has struct archive_args embedded at the beginning,\n>> and then set remote and prefix on the returned structure.\n>\n> One bad side is that we need to malloc this embedded structure.\n\nNot at all, if you read the example I did you would notice that\nI changed parse_extra for each backend to return this structure\nallocated for that particular backend.\n\n>>> +static int run_remote_archiver(struct archiver_struct *ar, int argc,\n>>> +\t\t\t       const char **argv)\n>>> +{\n>>> +\tchar *url, buf[1024];\n>>> +\tpid_t pid;\n>>> +\tint fd[2];\n>>> +\tint len, rv;\n>>> +\n>>> +\tsprintf(buf, \"git-upload-%s\", ar->name);\n>> \n>> Are you calling git-upload-{tar,zip,rar,...} here?\n>\n> yes. Actually git-upload-{tar,zip,...} commands are going to be\n> removed, but git-daemon know them as a daemon service.\n\nThat would break \"git-archive --remove=ssh://site/repo treeish\"\nwouldn't it?\n"},{"id":"26483","messageId":"cda58cb80609062332p356bd26bw852e31211c43d1ac@mail.gmail.com","threadId":"5483","inReplyTo":"7vac5c7jty.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 1/2] Add git-archive","fromName":"Franck Bui-Huu","fromEmail":"vagabon.xyz@gmail.com","sentAt":"2006-09-07T06:32:02Z","receivedAt":"2006-09-07T06:32:02Z","isPatch":true,"sender":{"key":"vagabon.xyz@gmail.com","avatar":null},"body":"2006/9/6, Junio C Hamano <junkio@cox.net>:\n> Franck Bui-Huu <vagabon.xyz@gmail.com> writes:\n>\n> > Interesting, could you explain why static variables are not nice ?\n>\n> Mostly taste and a little bit of re-entrancy worries.\n>\n\nOK.\n\n> > You might have missed my second patch:\n> >\n> >               \"[PATCH 2/2] Add git-upload-archive\"\n> >\n> > Basically the server can also use 'reason' to report a failure\n> > description during NACK. I find it more useful than the simple\n> > \"server sent EOF\" error message.\n>\n> That's a good intention, but we would also need to convey the\n> \"server side found problem and died with these error() output\"\n> anyway,\n\nOK.\n\n>\n> > One bad side is that we need to malloc this embedded structure.\n>\n> Not at all, if you read the example I did you would notice that\n> I changed parse_extra for each backend to return this structure\n> allocated for that particular backend.\n>\n\nsorry I wasn't clear. My point was that the structure need to be\n'mallocated'. Which funtion allocate it doesn't matter, we will need\nto free it later. That's what I tried to avoid with the alternative I\nsent you in my previous email. Do you think we could use it ?\n\n> >>> +static int run_remote_archiver(struct archiver_struct *ar, int argc,\n> >>> +                          const char **argv)\n> >>> +{\n> >>> +   char *url, buf[1024];\n> >>> +   pid_t pid;\n> >>> +   int fd[2];\n> >>> +   int len, rv;\n> >>> +\n> >>> +   sprintf(buf, \"git-upload-%s\", ar->name);\n> >>\n> >> Are you calling git-upload-{tar,zip,rar,...} here?\n> >\n> > yes. Actually git-upload-{tar,zip,...} commands are going to be\n> > removed, but git-daemon know them as a daemon service.\n>\n> That would break \"git-archive --remove=ssh://site/repo treeish\"\n> wouldn't it?\n>\n\nYes. But couldn't we make some alias like:\n\nalias git-upload-tar='git-upload-archive --format=tar'\nalias git-upload-zip='git-upload-zip --format=zip'\n\nand the same could be done if we plan to remote git-tar-tree command:\n\nalias git-tar-tree='git-archive --format=tar'\n\nThese alias would be internal to git (always defined)\n\n-- \n               Franck\n"},{"id":"26493","messageId":"7v1wqo400b.fsf@assigned-by-dhcp.cox.net","threadId":"5483","inReplyTo":"cda58cb80609062332p356bd26bw852e31211c43d1ac@mail.gmail.com","subject":"Re: [PATCH 1/2] Add git-archive","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-07T07:19:16Z","receivedAt":"2006-09-07T07:19:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Franck Bui-Huu\" <vagabon.xyz@gmail.com> writes:\n\n> sorry I wasn't clear. My point was that the structure need to be\n> 'mallocated'. Which funtion allocate it doesn't matter, we will need\n> to free it later. That's what I tried to avoid with the alternative I\n> sent you in my previous email. Do you think we could use it ?\n\nI do not think allocation and free matter much, but if you want\nto do it that way, enumerating all the possible struct in one\nplace is fine by me for this application.  After all we are not\ndefining a plug-in architecture that lets others to write their\narchive backends and load them without recompiling git-archive\nbinary.\n\n>> >>> +static int run_remote_archiver(struct archiver_struct *ar, int argc,\n>> >>> +                          const char **argv)\n>> >>> +{\n>> >>> +   char *url, buf[1024];\n>> >>> +   pid_t pid;\n>> >>> +   int fd[2];\n>> >>> +   int len, rv;\n>> >>> +\n>> >>> +   sprintf(buf, \"git-upload-%s\", ar->name);\n>> >>\n>> >> Are you calling git-upload-{tar,zip,rar,...} here?\n>> >\n>> > yes. Actually git-upload-{tar,zip,...} commands are going to be\n>> > removed, but git-daemon know them as a daemon service.\n>>\n>> That would break \"git-archive --remove=ssh://site/repo treeish\"\n>> wouldn't it?\n>\n> Yes. But couldn't we make some alias like:\n>...\n> These alias would be internal to git (always defined)\n\nYou _could_ work things around by building special cases into\nthe system, but I would rather avoid doing that unless\nnecessary.\n\nIs there a reason that \"git-upload-archive --format=tar\" is not\ndesirable at this point of the code?\n"},{"id":"26495","messageId":"44FFD00E.5040305@innova-card.com","threadId":"5483","inReplyTo":"7v1wqo400b.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 1/2] Add git-archive","fromName":"Franck Bui-Huu","fromEmail":"vagabon.xyz@gmail.com","sentAt":"2006-09-07T07:53:50Z","receivedAt":"2006-09-07T07:53:50Z","isPatch":true,"sender":{"key":"vagabon.xyz@gmail.com","avatar":null},"body":"Junio C Hamano wrote:\n> \"Franck Bui-Huu\" <vagabon.xyz@gmail.com> writes:\n>> These alias would be internal to git (always defined)\n> \n> You _could_ work things around by building special cases into\n> the system, but I would rather avoid doing that unless\n> necessary.\n> \n\nyes that was not a great idea...\n\n> Is there a reason that \"git-upload-archive --format=tar\" is not\n> desirable at this point of the code?\n> \n\nMy first intention was to enable/disable specific archive format\nthrough daemon service. But we could in an other way: send a \n\"git-upload-archive --format=tar\" request then in upload-archive\ncheck that git-upload-tar service is enabled. This service would\nexist even if git-upload-tar is not a valid command.\n\nBut Rene thinks that part should be in git-upload-archive. I dunno\nwhat is the best direction. I have used git-daemon service because\nthe service infrastucture already allow us to achieve that.\n\nI'm sorry to not make things faster. I'm new to git internals and\nunfortunately I'm busy to do some other (crap) things. I'll send\na new patchset this morning and I'll sum up what's done and what\nwe still need to do.\n\nThanks\n\t\tFranck\n"},{"id":"26498","messageId":"7vr6yo2isu.fsf@assigned-by-dhcp.cox.net","threadId":"5483","inReplyTo":"44FFD00E.5040305@innova-card.com","subject":"Re: [PATCH 1/2] Add git-archive","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-07T08:16:17Z","receivedAt":"2006-09-07T08:16:17Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Franck Bui-Huu <vagabon.xyz@gmail.com> writes:\n\n> My first intention was to enable/disable specific archive format\n> through daemon service. But we could in an other way: send a \n> \"git-upload-archive --format=tar\" request then in upload-archive\n> check that git-upload-tar service is enabled. This service would\n> exist even if git-upload-tar is not a valid command.\n>\n> But Rene thinks that part should be in git-upload-archive. I dunno\n> what is the best direction. I have used git-daemon service because\n> the service infrastucture already allow us to achieve that.\n\nHmph.  daemon_service was a nice idea but the current\nimplementation falls short by not giving finer control such as\n\"this service is Ok with such and such option but not this\noption\".  Here we ideally would want to say something like\n\"git-archive is fine with upload-tar but not upload-zip\" or \"Ok\nas long as upload-tar's numeric arg is less than 6\".  Needs some\nthought _if_ we plan to add tons of services to the daemon.\n\n> I'm sorry to not make things faster. I'm new to git internals and\n> unfortunately I'm busy to do some other (crap) things. I'll send\n> a new patchset this morning and I'll sum up what's done and what\n> we still need to do.\n\nTaking things slowly is just fine.  Otherwise _I_ would burn out\n;-).\n"},{"id":"26517","messageId":"450019C3.4030001@innova-card.com","threadId":"5483","inReplyTo":"7vr6yo2isu.fsf@assigned-by-dhcp.cox.net","subject":"Add git-archive [take #2]","fromName":"Franck Bui-Huu","fromEmail":"vagabon.xyz@gmail.com","sentAt":"2006-09-07T13:08:19Z","receivedAt":"2006-09-07T13:08:19Z","isPatch":false,"sender":{"key":"vagabon.xyz@gmail.com","avatar":null},"body":"I'm sending a new version of the patchset which allows 'git-archive'\nand 'git-upload-archive' command. I tried to take into account all\nfeedbacks made by Junio and Rene, but there are still some open points.\n\n  1/ Allow 'git-upload-archive' command to enable/disable some\n     formats. This should be done by 'git-upload-archive'.\n\n  2/ Can I remove 'git-upload-tar' command ? If so, the current\n     implementation of 'git-upload-archive' won't work with the old\n     'git-tar-tree --remote' command because the protocol needs a\n     \"argument --format\" to be passed to the server. Therefore we can\n     either modify 'git-tar-tree' or simply remove it.\n\n  3/ Should I kill 'git-zip-tree' command ? If so should I rename\n     builtin-zip-tree.c file into zip-tree.c or something else ?\n\n  4/ Progress indicator support. Junio wants to mimic upload-pack for\n     that. But it will lead in a lot of duplicated code if we don't\n     try to share code. Can we copy that code anyways and clean up\n     later ?\n\n  5/ Should we use \"struct tree_desc\" based interface for tree parsing\n     ? According to Rene it doesn't worth it as soon as you actually\n     start to do something to the trees\n\n  6/ Simple subtree matching would be enough, at least for now.\n\nDid I forgot something else ?\n\nPoint 1 seems to be important. As soons as we plug git-upload-archive,\nwe need to control what kind of formats the server deal with.\n\nPoint 2 and 3 are easy to achieve, just need a \"go wild\" authorization.\n\nPoint 4, seems to be high in priority since we don't want to deal with\nclients and servers interoperate issues.\n\nPoints 5 and 6 can be done later. These improvements won't break if\ndone after releasing.\n\n\t\tFranck\n"},{"id":"26518","messageId":"11576347251776-git-send-email-vagabon.xyz@gmail.com","threadId":"5483","inReplyTo":"450019C3.4030001@innova-card.com","subject":"[PATCH 1/4] Add git-archive","fromName":"Franck Bui-Huu","fromEmail":"vagabon.xyz@gmail.com","sentAt":"2006-09-07T13:12:02Z","receivedAt":"2006-09-07T13:12:02Z","isPatch":true,"sender":{"key":"vagabon.xyz@gmail.com","avatar":null},"body":"git-archive is a command to make TAR and ZIP archives of a git tree.\nIt helps prevent a proliferation of git-{format}-tree commands.\n\nInstead of directly calling git-{tar,zip}-tree command, it defines\na very simple API, that archiver should implement and register in\n\"git-archive.c\". This API is made up by 2 functions whose prototype\nis defined in \"archive.h\" file.\n\n - The first one is used to parse 'extra' parameters which have\n   signification only for the specific archiver. That would allow\n   different archive backends to have different kind of options.\n\n - The second one is used to ask to an archive backend to build\n   the archive given some already resolved parameters.\n\nThe main reason for making this API is to avoid using\ngit-{tar,zip}-tree commands, hence making them useless. Maybe it's\ntime for them to die ?\n\nIt also implements remote operations by defining a very simple\nprotocol: it first sends the name of the specific uploader followed\nthe repository name (git-upload-tar git://example.org/repo.git).\nThen it sends options. It's done by sending a sequence of one\nargument per packet, with prefix \"argument \", followed by a flush.\n\nThe remote protocol is implemented in \"git-archive.c\" for client\nside and is triggered by \"--remote=<repo>\" option. For example,\nto fetch a TAR archive in a remote repo, you can issue:\n\n$ git archive --format=tar --remote=git://xxx/yyy/zzz.git HEAD\n\nWe choose to not make a new command \"git-fetch-archive\" for example,\navoind one more GIT command which should be nice for users (less\ncommands to remember, keeps existing --remote option).\n\nSigned-off-by: Franck Bui-Huu <vagabon.xyz@gmail.com>\n---\n .gitignore                    |    1 \n Documentation/git-archive.txt |  100 ++++++++++++++++++\n Makefile                      |    3 -\n archive.h                     |   41 +++++++\n builtin-archive.c             |  227 +++++++++++++++++++++++++++++++++++++++++\n builtin.h                     |    1 \n generate-cmdlist.sh           |    1 \n git.c                         |    1 \n 8 files changed, 374 insertions(+), 1 deletions(-)\n\ndiff --git a/.gitignore b/.gitignore\nindex 78cb671..a3e7ca1 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -8,6 +8,7 @@ git-apply\n git-applymbox\n git-applypatch\n git-archimport\n+git-archive\n git-bisect\n git-branch\n git-cat-file\ndiff --git a/Documentation/git-archive.txt b/Documentation/git-archive.txt\nnew file mode 100644\nindex 0000000..913528d\n--- /dev/null\n+++ b/Documentation/git-archive.txt\n@@ -0,0 +1,100 @@\n+git-archive(1)\n+==============\n+\n+NAME\n+----\n+git-archive - Creates a archive of the files in the named tree\n+\n+\n+SYNOPSIS\n+--------\n+'git-archive' --format=<fmt> [--list] [--prefix=<prefix>/] [<extra>]\n+\t      [--remote=<repo>] <tree-ish> [path...]\n+\n+DESCRIPTION\n+-----------\n+Creates an archive of the specified format containing the tree\n+structure for the named tree.  If <prefix> is specified it is\n+prepended to the filenames in the archive.\n+\n+'git-archive' behaves differently when given a tree ID versus when\n+given a commit ID or tag ID.  In the first case the current time is\n+used as modification time of each file in the archive.  In the latter\n+case the commit time as recorded in the referenced commit object is\n+used instead.  Additionally the commit ID is stored in a global\n+extended pax header if the tar format is used; it can be extracted\n+using 'git-get-tar-commit-id'. In ZIP files it is stored as a file\n+comment.\n+\n+OPTIONS\n+-------\n+\n+--format=<fmt>::\n+\tFormat of the resulting archive: 'tar', 'zip'...\n+\n+--list::\n+\tShow all available formats.\n+\n+--prefix=<prefix>/::\n+\tPrepend <prefix>/ to each filename in the archive.\n+\n+<extra>::\n+\tThis can be any options that the archiver backend understand.\n+\n+--remote=<repo>::\n+\tInstead of making a tar archive from local repository,\n+\tretrieve a tar archive from a remote repository.\n+\n+<tree-ish>::\n+\tThe tree or commit to produce an archive for.\n+\n+path::\n+\tIf one or more paths are specified, include only these in the\n+\tarchive, otherwise include all files and subdirectories.\n+\n+CONFIGURATION\n+-------------\n+By default, file and directories modes are set to 0666 or 0777 in tar\n+archives.  It is possible to change this by setting the \"umask\" variable\n+in the repository configuration as follows :\n+\n+[tar]\n+        umask = 002\t;# group friendly\n+\n+The special umask value \"user\" indicates that the user's current umask\n+will be used instead. The default value remains 0, which means world\n+readable/writable files and directories.\n+\n+EXAMPLES\n+--------\n+git archive --format=tar --prefix=junk/ HEAD | (cd /var/tmp/ && tar xf -)::\n+\n+\tCreate a tar archive that contains the contents of the\n+\tlatest commit on the current branch, and extracts it in\n+\t`/var/tmp/junk` directory.\n+\n+git archive --format=tar --prefix=git-1.4.0/ v1.4.0 | gzip >git-1.4.0.tar.gz::\n+\n+\tCreate a compressed tarball for v1.4.0 release.\n+\n+git archive --format=tar --prefix=git-1.4.0/ v1.4.0{caret}\\{tree\\} | gzip >git-1.4.0.tar.gz::\n+\n+\tCreate a compressed tarball for v1.4.0 release, but without a\n+\tglobal extended pax header.\n+\n+git archive --format=zip --prefix=git-docs/ HEAD:Documentation/ > git-1.4.0-docs.zip::\n+\n+\tPut everything in the current head's Documentation/ directory\n+\tinto 'git-1.4.0-docs.zip', with the prefix 'git-docs/'.\n+\n+Author\n+------\n+Written by Franck Bui-Huu and Rene Scharfe.\n+\n+Documentation\n+--------------\n+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.\n+\n+GIT\n+---\n+Part of the gitlink:git[7] suite\ndiff --git a/Makefile b/Makefile\nindex 389daf7..51ed4dd 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -242,7 +242,7 @@ LIB_FILE=libgit.a\n XDIFF_LIB=xdiff/lib.a\n \n LIB_H = \\\n-\tblob.h cache.h commit.h csum-file.h delta.h \\\n+\tarchive.h blob.h cache.h commit.h csum-file.h delta.h \\\n \tdiff.h object.h pack.h para-walk.h pkt-line.h quote.h refs.h \\\n \trun-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \\\n \ttree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h\n@@ -267,6 +267,7 @@ LIB_OBJS = \\\n BUILTIN_OBJS = \\\n \tbuiltin-add.o \\\n \tbuiltin-apply.o \\\n+\tbuiltin-archive.o \\\n \tbuiltin-cat-file.o \\\n \tbuiltin-checkout-index.o \\\n \tbuiltin-check-ref-format.o \\\ndiff --git a/archive.h b/archive.h\nnew file mode 100644\nindex 0000000..f33398e\n--- /dev/null\n+++ b/archive.h\n@@ -0,0 +1,41 @@\n+#ifndef ARCHIVE_H\n+#define ARCHIVE_H\n+\n+#define MAX_EXTRA_ARGS\t32\n+#define MAX_ARGS\t(MAX_EXTRA_ARGS + 32)\n+\n+struct archiver_args {\n+\tconst char *base;\n+\tstruct tree *tree;\n+\tconst unsigned char *commit_sha1;\n+\ttime_t time;\n+\tconst char **pathspec;\n+\tvoid *extra;\n+};\n+\n+typedef int (*write_archive_fn_t)(struct archiver_args *);\n+\n+typedef void *(*parse_extra_args_fn_t)(int argc, const char **argv);\n+\n+struct archiver {\n+\tconst char *name;\n+\tconst char *remote;\n+\tstruct archiver_args args;\n+\twrite_archive_fn_t write_archive;\n+\tparse_extra_args_fn_t parse_extra;\n+};\n+\n+extern struct archiver archivers[];\n+\n+extern int parse_archive_args(int argc,\n+\t\t\t      const char **argv,\n+\t\t\t      struct archiver **ar);\n+\n+extern void parse_treeish_arg(const char **treeish,\n+\t\t\t      struct archiver_args *ar_args,\n+\t\t\t      const char *prefix);\n+\n+extern void parse_pathspec_arg(const char **pathspec,\n+\t\t\t       struct archiver_args *args);\n+\n+#endif\t/* ARCHIVE_H */\ndiff --git a/builtin-archive.c b/builtin-archive.c\nnew file mode 100644\nindex 0000000..6064358\n--- /dev/null\n+++ b/builtin-archive.c\n@@ -0,0 +1,227 @@\n+/*\n+ * Copyright (c) 2006 Franck Bui-Huu\n+ * Copyright (c) 2006 Rene Scharfe\n+ */\n+#include <time.h>\n+#include \"cache.h\"\n+#include \"builtin.h\"\n+#include \"archive.h\"\n+#include \"commit.h\"\n+#include \"tree-walk.h\"\n+#include \"exec_cmd.h\"\n+#include \"pkt-line.h\"\n+\n+static const char archive_usage[] = \\\n+\"git-archive --format=<fmt> [--prefix=<prefix>/] [<extra>] <tree-ish> [path...]\";\n+\n+\n+struct archiver archivers[] = { };\n+\n+\n+static int run_remote_archiver(struct archiver *ar, int argc,\n+\t\t\t       const char **argv)\n+{\n+\tchar *url, buf[1024];\n+\tint fd[2], i, len, rv;\n+\tpid_t pid;\n+\n+\tsprintf(buf, \"git-upload-archive\");\n+\n+\turl = strdup(ar->remote);\n+\tpid = git_connect(fd, url, buf);\n+\tif (pid < 0)\n+\t\treturn pid;\n+\n+\tfor (i = 1; i < argc; i++) {\n+\t\tif (!strncmp(argv[i], \"--remote=\", 9))\n+\t\t\tcontinue;\n+\t\tpacket_write(fd[1], \"argument %s\\n\", argv[i]);\n+\t}\n+\tpacket_flush(fd[1]);\n+\n+\tlen = packet_read_line(fd[0], buf, sizeof(buf));\n+\tif (!len)\n+\t\tdie(\"git-archive: expected ACK/NAK, got EOF\");\n+\tif (buf[len-1] == '\\n')\n+\t\tbuf[--len] = 0;\n+\tif (strcmp(buf, \"ACK\")) {\n+\t\tif (len > 5 && !strncmp(buf, \"NACK \", 5))\n+\t\t\tdie(\"git-archive: NACK %s\", buf + 5);\n+\t\tdie(\"git-archive: protocol error\");\n+\t}\n+\n+\tlen = packet_read_line(fd[0], buf, sizeof(buf));\n+\tif (len)\n+\t\tdie(\"git-archive: expected a flush\");\n+\n+\t/* Now, start reading from fd[0] and spit it out to stdout */\n+\trv = copy_fd(fd[0], 1);\n+\n+\tclose(fd[0]);\n+\trv |= finish_connect(pid);\n+\n+\treturn !!rv;\n+}\n+\n+static struct archiver *get_archiver(const char *name)\n+{\n+\tstruct archiver *ar = NULL;\n+\tint i;\n+\n+\tfor (i = 0; i < ARRAY_SIZE(archivers); i++) {\n+\t\tif (!strcmp(name, archivers[i].name)) {\n+\t\t\tar = &archivers[i];\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\treturn ar;\n+}\n+\n+void parse_pathspec_arg(const char **pathspec, struct archiver_args *ar_args)\n+{\n+\tar_args->pathspec = get_pathspec(ar_args->base, pathspec);\n+}\n+\n+void parse_treeish_arg(const char **argv, struct archiver_args *ar_args,\n+\t\t       const char *prefix)\n+{\n+\tconst char *name = argv[0];\n+\tconst unsigned char *commit_sha1;\n+\ttime_t archive_time;\n+\tstruct tree *tree;\n+\tstruct commit *commit;\n+\tunsigned char sha1[20];\n+\n+\tif (get_sha1(name, sha1))\n+\t\tdie(\"Not a valid object name\");\n+\n+\tcommit = lookup_commit_reference_gently(sha1, 1);\n+\tif (commit) {\n+\t\tcommit_sha1 = commit->object.sha1;\n+\t\tarchive_time = commit->date;\n+\t} else {\n+\t\tarchive_time = time(NULL);\n+\t}\n+\n+\ttree = parse_tree_indirect(sha1);\n+\tif (tree == NULL)\n+\t\tdie(\"not a tree object\");\n+\n+\tif (prefix) {\n+\t\tunsigned char tree_sha1[20];\n+\t\tunsigned int mode;\n+\t\tint err;\n+\n+\t\terr = get_tree_entry(tree->object.sha1, prefix,\n+\t\t\t\t     tree_sha1, &mode);\n+\t\tif (err || !S_ISDIR(mode))\n+\t\t\tdie(\"current working directory is untracked\");\n+\n+\t\tfree(tree);\n+\t\ttree = parse_tree_indirect(tree_sha1);\n+\t}\n+\t//free(tree);\n+\tar_args->tree = tree;\n+\tar_args->commit_sha1 = commit_sha1;\n+\tar_args->time = archive_time;\n+}\n+\n+static const char *default_parse_extra(struct archiver *ar,\n+\t\t\t\t       const char **argv)\n+{\n+\tstatic char msg[64];\n+\n+\tsnprintf(msg, sizeof(msg) - 4, \"'%s' format does not handle %s\",\n+\t\t ar->name, *argv);\n+\n+\treturn strcat(msg, \"...\");\n+}\n+\n+int parse_archive_args(int argc, const char **argv, struct archiver **ar)\n+{\n+\tconst char *extra_argv[MAX_EXTRA_ARGS];\n+\tint extra_argc = 0;\n+\tconst char *format = NULL; /* some default values */\n+\tconst char *remote = NULL;\n+\tconst char *base = \"\";\n+\tint list = 0;\n+\tint i;\n+\n+\tfor (i = 1; i < argc; i++) {\n+\t\tconst char *arg = argv[i];\n+\n+\t\tif (!strcmp(arg, \"--list\") || !strcmp(arg, \"-l\")) {\n+\t\t\tlist = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strncmp(arg, \"--format=\", 9)) {\n+\t\t\tformat = arg + 9;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strncmp(arg, \"--prefix=\", 9)) {\n+\t\t\tbase = arg + 9;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strncmp(arg, \"--remote=\", 9)) {\n+\t\t\tremote = arg + 9;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"--\")) {\n+\t\t\ti++;\n+\t\t\tbreak;\n+\t\t}\n+\t\tif (arg[0] == '-') {\n+\t\t\textra_argv[extra_argc++] = arg;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tbreak;\n+\t}\n+\tif (list) {\n+\t\tif (!remote) {\n+\t\t\tfor (i = 0; i < ARRAY_SIZE(archivers); i++)\n+\t\t\t\tprintf(\"%s\\n\", archivers[i].name);\n+\t\t\texit(0);\n+\t\t}\n+\t\tdie(\"--list and --remote are mutually exclusive\");\n+\t}\n+\tif (argc - i < 1) {\n+\t\tdie(\"%s\", archive_usage);\n+\t}\n+\tif (!format){\n+\t\tdie(\"You must specify an archive format\");\n+\t}\n+\t*ar = get_archiver(format);\n+\tif (*ar == NULL) {\n+\t\tdie(\"Unknown archive format '%s'\", format);\n+\t}\n+\tif (extra_argc) {\n+\t\tif (!(*ar)->parse_extra) {\n+\t\t\tdie(\"%s\", default_parse_extra(*ar, extra_argv));\n+\t\t}\n+\t\t(*ar)->args.extra = (*ar)->parse_extra(extra_argc, extra_argv);\n+\t}\n+\t(*ar)->remote = remote;\n+\t(*ar)->args.base = base;\n+\n+\treturn i;\n+}\n+\n+int cmd_archive(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct archiver *ar;\n+\tint tree_idx;\n+\n+\ttree_idx = parse_archive_args(argc, argv, &ar);\n+\n+\tif (ar->remote)\n+\t\treturn run_remote_archiver(ar, argc, argv);\n+\n+\tif (prefix == NULL)\n+\t\tprefix = setup_git_directory();\n+\n+\targv += tree_idx;\n+\tparse_treeish_arg(argv, &ar->args, prefix);\n+\tparse_pathspec_arg(argv + 1, &ar->args);\n+\n+\treturn ar->write_archive(&ar->args);\n+}\ndiff --git a/builtin.h b/builtin.h\nindex 8472c79..2391afb 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -15,6 +15,7 @@ extern int write_tree(unsigned char *sha\n \n extern int cmd_add(int argc, const char **argv, const char *prefix);\n extern int cmd_apply(int argc, const char **argv, const char *prefix);\n+extern int cmd_archive(int argc, const char **argv, const char *prefix);\n extern int cmd_cat_file(int argc, const char **argv, const char *prefix);\n extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);\n extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);\ndiff --git a/generate-cmdlist.sh b/generate-cmdlist.sh\nindex ec1eda2..5450918 100755\n--- a/generate-cmdlist.sh\n+++ b/generate-cmdlist.sh\n@@ -12,6 +12,7 @@ struct cmdname_help common_cmds[] = {\"\n sort <<\\EOF |\n add\n apply\n+archive\n bisect\n branch\n checkout\ndiff --git a/git.c b/git.c\nindex 82c8fee..c62c5cf 100644\n--- a/git.c\n+++ b/git.c\n@@ -218,6 +218,7 @@ static void handle_internal_command(int \n \t} commands[] = {\n \t\t{ \"add\", cmd_add, RUN_SETUP },\n \t\t{ \"apply\", cmd_apply },\n+\t\t{ \"archive\", cmd_archive },\n \t\t{ \"cat-file\", cmd_cat_file, RUN_SETUP },\n \t\t{ \"checkout-index\", cmd_checkout_index, RUN_SETUP },\n \t\t{ \"check-ref-format\", cmd_check_ref_format },\n-- \n1.4.2\n"},{"id":"26519","messageId":"11576347252834-git-send-email-vagabon.xyz@gmail.com","threadId":"5483","inReplyTo":"450019C3.4030001@innova-card.com","subject":"[PATCH 2/4] git-archive: wire up TAR format.","fromName":"Franck Bui-Huu","fromEmail":"vagabon.xyz@gmail.com","sentAt":"2006-09-07T13:12:03Z","receivedAt":"2006-09-07T13:12:03Z","isPatch":true,"sender":{"key":"vagabon.xyz@gmail.com","avatar":null},"body":"From: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>\n\nSigned-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>\nSigned-off-by: Franck Bui-Huu <vagabon.xyz@gmail.com>\n---\n archive.h          |    4 +++\n builtin-archive.c  |    4 ++-\n builtin-tar-tree.c |   67 ++++++++++++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 74 insertions(+), 1 deletions(-)\n\ndiff --git a/archive.h b/archive.h\nindex f33398e..3690c53 100644\n--- a/archive.h\n+++ b/archive.h\n@@ -37,5 +37,9 @@ extern void parse_treeish_arg(const char\n \n extern void parse_pathspec_arg(const char **pathspec,\n \t\t\t       struct archiver_args *args);\n+/*\n+ *\n+ */\n+extern int write_tar_archive(struct archiver_args *);\n \n #endif\t/* ARCHIVE_H */\ndiff --git a/builtin-archive.c b/builtin-archive.c\nindex 6064358..214ec5d 100644\n--- a/builtin-archive.c\n+++ b/builtin-archive.c\n@@ -15,7 +15,9 @@ static const char archive_usage[] = \\\n \"git-archive --format=<fmt> [--prefix=<prefix>/] [<extra>] <tree-ish> [path...]\";\n \n \n-struct archiver archivers[] = { };\n+struct archiver archivers[] = {\n+\t{ .name = \"tar\", .write_archive = write_tar_archive },\n+};\n \n \n static int run_remote_archiver(struct archiver *ar, int argc,\ndiff --git a/builtin-tar-tree.c b/builtin-tar-tree.c\nindex 61a4135..1134730 100644\n--- a/builtin-tar-tree.c\n+++ b/builtin-tar-tree.c\n@@ -9,6 +9,7 @@ #include \"strbuf.h\"\n #include \"tar.h\"\n #include \"builtin.h\"\n #include \"pkt-line.h\"\n+#include \"archive.h\"\n \n #define RECORDSIZE\t(512)\n #define BLOCKSIZE\t(RECORDSIZE * 20)\n@@ -338,6 +339,72 @@ static int generate_tar(int argc, const \n \treturn 0;\n }\n \n+static int write_tar_entry(const unsigned char *sha1,\n+                           const char *base, int baselen,\n+                           const char *filename, unsigned mode, int stage)\n+{\n+\tstatic struct strbuf path;\n+\tint filenamelen = strlen(filename);\n+\tvoid *buffer;\n+\tchar type[20];\n+\tunsigned long size;\n+\n+\tif (!path.alloc) {\n+\t\tpath.buf = xmalloc(PATH_MAX);\n+\t\tpath.alloc = PATH_MAX;\n+\t\tpath.len = path.eof = 0;\n+\t}\n+\tif (path.alloc < baselen + filenamelen) {\n+\t\tfree(path.buf);\n+\t\tpath.buf = xmalloc(baselen + filenamelen);\n+\t\tpath.alloc = baselen + filenamelen;\n+\t}\n+\tmemcpy(path.buf, base, baselen);\n+\tmemcpy(path.buf + baselen, filename, filenamelen);\n+\tpath.len = baselen + filenamelen;\n+\tif (S_ISDIR(mode)) {\n+\t\tstrbuf_append_string(&path, \"/\");\n+\t\tbuffer = NULL;\n+\t\tsize = 0;\n+\t} else {\n+\t\tbuffer = read_sha1_file(sha1, type, &size);\n+\t\tif (!buffer)\n+\t\t\tdie(\"cannot read %s\", sha1_to_hex(sha1));\n+\t}\n+\n+\twrite_entry(sha1, &path, mode, buffer, size);\n+\tfree(buffer);\n+\n+\treturn READ_TREE_RECURSIVE;\n+}\n+\n+int write_tar_archive(struct archiver_args *args)\n+{\n+\tint plen = strlen(args->base);\n+\n+\tgit_config(git_tar_config);\n+\n+\tarchive_time = args->time;\n+\n+\tif (args->commit_sha1)\n+\t\twrite_global_extended_header(args->commit_sha1);\n+\n+\tif (args->base && plen > 0 && args->base[plen - 1] == '/') {\n+\t\tchar *base = strdup(args->base);\n+\t\tint baselen = strlen(base);\n+\n+\t\twhile (baselen > 0 && base[baselen - 1] == '/')\n+\t\t\tbase[--baselen] = '\\0';\n+\t\twrite_tar_entry(args->tree->object.sha1, \"\", 0, base, 040777, 0);\n+\t\tfree(base);\n+\t}\n+\tread_tree_recursive(args->tree, args->base, plen, 0,\n+\t\t\t    args->pathspec, write_tar_entry);\n+\twrite_trailer();\n+\n+\treturn 0;\n+}\n+\n static const char *exec = \"git-upload-tar\";\n \n static int remote_tar(int argc, const char **argv)\n-- \n1.4.2\n"},{"id":"26520","messageId":"1157634725573-git-send-email-vagabon.xyz@gmail.com","threadId":"5483","inReplyTo":"450019C3.4030001@innova-card.com","subject":"[PATCH 3/4] git-archive: wire up ZIP format.","fromName":"Franck Bui-Huu","fromEmail":"vagabon.xyz@gmail.com","sentAt":"2006-09-07T13:12:04Z","receivedAt":"2006-09-07T13:12:04Z","isPatch":true,"sender":{"key":"vagabon.xyz@gmail.com","avatar":null},"body":"From: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>\n\nSigned-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>\nSigned-off-by: Franck Bui-Huu <vagabon.xyz@gmail.com>\n---\n archive.h          |    1 +\n builtin-archive.c  |    1 +\n builtin-zip-tree.c |   28 ++++++++++++++++++++++++++++\n 3 files changed, 30 insertions(+), 0 deletions(-)\n\ndiff --git a/archive.h b/archive.h\nindex 3690c53..760776d 100644\n--- a/archive.h\n+++ b/archive.h\n@@ -41,5 +41,6 @@ extern void parse_pathspec_arg(const cha\n  *\n  */\n extern int write_tar_archive(struct archiver_args *);\n+extern int write_zip_archive(struct archiver_args *);\n \n #endif\t/* ARCHIVE_H */\ndiff --git a/builtin-archive.c b/builtin-archive.c\nindex 214ec5d..fd53e9a 100644\n--- a/builtin-archive.c\n+++ b/builtin-archive.c\n@@ -17,6 +17,7 @@ static const char archive_usage[] = \\\n \n struct archiver archivers[] = {\n \t{ .name = \"tar\", .write_archive = write_tar_archive },\n+\t{ .name = \"zip\", .write_archive = write_zip_archive },\n };\n \n \ndiff --git a/builtin-zip-tree.c b/builtin-zip-tree.c\nindex a5b834d..788317c 100644\n--- a/builtin-zip-tree.c\n+++ b/builtin-zip-tree.c\n@@ -8,6 +8,7 @@ #include \"blob.h\"\n #include \"tree.h\"\n #include \"quote.h\"\n #include \"builtin.h\"\n+#include \"archive.h\"\n \n static const char zip_tree_usage[] =\n \"git-zip-tree [-0|...|-9] <tree-ish> [ <base> ]\";\n@@ -351,3 +352,30 @@ int cmd_zip_tree(int argc, const char **\n \n \treturn 0;\n }\n+\n+int write_zip_archive(struct archiver_args *args)\n+{\n+\tint plen = strlen(args->base);\n+\n+\tdos_time(&args->time, &zip_date, &zip_time);\n+\n+\tzip_dir = xmalloc(ZIP_DIRECTORY_MIN_SIZE);\n+\tzip_dir_size = ZIP_DIRECTORY_MIN_SIZE;\n+\n+\tif (args->base && plen > 0 && args->base[plen - 1] == '/') {\n+\t\tchar *base = strdup(args->base);\n+\t\tint baselen = strlen(base);\n+\n+\t\twhile (baselen > 0 && base[baselen - 1] == '/')\n+\t\t\tbase[--baselen] = '\\0';\n+\t\twrite_zip_entry(args->tree->object.sha1, \"\", 0, base, 040777, 0);\n+\t\tfree(base);\n+\t}\n+\tread_tree_recursive(args->tree, args->base, plen, 0,\n+\t\t\t    args->pathspec, write_zip_entry);\n+\twrite_zip_trailer(args->commit_sha1);\n+\n+\tfree(zip_dir);\n+\n+\treturn 0;\n+}\n-- \n1.4.2\n"},{"id":"26521","messageId":"11576347263684-git-send-email-vagabon.xyz@gmail.com","threadId":"5483","inReplyTo":"450019C3.4030001@innova-card.com","subject":"[PATCH 4/4] Add git-upload-archive","fromName":"Franck Bui-Huu","fromEmail":"vagabon.xyz@gmail.com","sentAt":"2006-09-07T13:12:05Z","receivedAt":"2006-09-07T13:12:05Z","isPatch":true,"sender":{"key":"vagabon.xyz@gmail.com","avatar":null},"body":"This command implements the git archive protocol on the server\nside. This command is not intended to be used by the end user.\nUnderlying git-archive command line options are sent over the\nprotocol from \"git-archive --remote=...\", just like upload-tar\ncurrently does with \"git-tar-tree=...\".\n\nAs for \"git-archive\" command implementation, this new command\ndoes not execute any existing \"git-{tar,zip}-tree\" but rely\non the archive API defined by \"git-archive\" patch. Hence we\nget 2 good points:\n\n - \"git-archive\" and \"git-upload-archive\" share all option\n   parsing code.\n\n - All kind of git-upload-{tar,zip} could be removed.\n\nSigned-off-by: Franck Bui-Huu <vagabon.xyz@gmail.com>\n---\n .gitignore                           |    1 \n Documentation/git-upload-archive.txt |   37 +++++++++++++++++\n Makefile                             |    1 \n builtin-upload-archive.c             |   72 ++++++++++++++++++++++++++++++++++\n builtin.h                            |    1 \n daemon.c                             |    7 +++\n git.c                                |    1 \n 7 files changed, 120 insertions(+), 0 deletions(-)\n\ndiff --git a/.gitignore b/.gitignore\nindex a3e7ca1..fda9bf2 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -119,6 +119,7 @@ git-unpack-objects\n git-update-index\n git-update-ref\n git-update-server-info\n+git-upload-archive\n git-upload-pack\n git-upload-tar\n git-var\ndiff --git a/Documentation/git-upload-archive.txt b/Documentation/git-upload-archive.txt\nnew file mode 100644\nindex 0000000..388bb53\n--- /dev/null\n+++ b/Documentation/git-upload-archive.txt\n@@ -0,0 +1,37 @@\n+git-upload-archive(1)\n+====================\n+\n+NAME\n+----\n+git-upload-archive - Send archive\n+\n+\n+SYNOPSIS\n+--------\n+'git-upload-archive' <directory>\n+\n+DESCRIPTION\n+-----------\n+Invoked by 'git-archive --remote' and sends a generated archive to the\n+other end over the git protocol.\n+\n+This command is usually not invoked directly by the end user.  The UI\n+for the protocol is on the 'git-archive' side, and the program pair\n+is meant to be used to get an archive from a remote repository.\n+\n+OPTIONS\n+-------\n+<directory>::\n+\tThe repository to get a tar archive from.\n+\n+Author\n+------\n+Written by Franck Bui-Huu.\n+\n+Documentation\n+--------------\n+Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.\n+\n+GIT\n+---\n+Part of the gitlink:git[7] suite\ndiff --git a/Makefile b/Makefile\nindex 51ed4dd..461e1d6 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -305,6 +305,7 @@ BUILTIN_OBJS = \\\n \tbuiltin-unpack-objects.o \\\n \tbuiltin-update-index.o \\\n \tbuiltin-update-ref.o \\\n+\tbuiltin-upload-archive.o \\\n \tbuiltin-upload-tar.o \\\n \tbuiltin-verify-pack.o \\\n \tbuiltin-write-tree.o \\\ndiff --git a/builtin-upload-archive.c b/builtin-upload-archive.c\nnew file mode 100644\nindex 0000000..4fad377\n--- /dev/null\n+++ b/builtin-upload-archive.c\n@@ -0,0 +1,72 @@\n+/*\n+ * Copyright (c) 2006 Franck Bui-Huu\n+ */\n+#include <time.h>\n+#include \"cache.h\"\n+#include \"builtin.h\"\n+#include \"archive.h\"\n+#include \"pkt-line.h\"\n+\n+static const char upload_archive_usage[] =\n+\t\"git-upload-archive <repo>\";\n+\n+\n+int cmd_upload_archive(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct archiver *ar;\n+\tconst char *sent_argv[MAX_ARGS];\n+\tconst char *arg_cmd = \"argument \";\n+\tchar *p, buf[4096];\n+\tint treeish_idx;\n+\tint sent_argc;\n+\tint len;\n+\n+\tif (argc != 2)\n+\t\tusage(upload_archive_usage);\n+\n+\tif (strlen(argv[1]) > sizeof(buf))\n+\t\tdie(\"insanely long repository name\");\n+\n+\tstrcpy(buf, argv[1]); /* enter-repo smudges its argument */\n+\n+\tif (!enter_repo(buf, 0))\n+\t\tdie(\"not a git archive\");\n+\n+\t/* put received options in sent_argv[] */\n+\tsent_argc = 1;\n+\tsent_argv[0] = \"git-upload-archive\";\n+\tfor (p = buf;;) {\n+\t\t/* This will die if not enough free space in buf */\n+\t\tlen = packet_read_line(0, p, (buf + sizeof buf) - p);\n+\t\tif (len == 0)\n+\t\t\tbreak;\t/* got a flush */\n+\t\tif (sent_argc > MAX_ARGS - 2)\n+\t\t\tdie(\"Too many options (>29)\");\n+\n+\t\tif (p[len-1] == '\\n') {\n+\t\t\tp[--len] = 0;\n+\t\t}\n+\t\tif (len < strlen(arg_cmd) ||\n+\t\t    strncmp(arg_cmd, p, strlen(arg_cmd)))\n+\t\t\tdie(\"'argument' token or flush expected\");\n+\n+\t\tlen -= strlen(arg_cmd);\n+\t\tmemmove(p, p + strlen(arg_cmd), len);\n+\t\tsent_argv[sent_argc++] = p;\n+\t\tp += len;\n+\t\t*p++ = 0;\n+\t}\n+\tsent_argv[sent_argc] = NULL;\n+\n+\t/* parse all options sent by the client */\n+\ttreeish_idx = parse_archive_args(sent_argc, sent_argv, &ar);\n+\n+\tparse_treeish_arg(sent_argv + treeish_idx, &ar->args, prefix);\n+\tparse_pathspec_arg(sent_argv + treeish_idx + 1, &ar->args);\n+\n+\tpacket_write(1, \"ACK\\n\");\n+\tpacket_flush(1);\n+\n+\treturn ar->write_archive(&ar->args);\n+}\n+\ndiff --git a/builtin.h b/builtin.h\nindex 2391afb..f3efb58 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -58,6 +58,7 @@ extern int cmd_zip_tree(int argc, const \n extern int cmd_unpack_objects(int argc, const char **argv, const char *prefix);\n extern int cmd_update_index(int argc, const char **argv, const char *prefix);\n extern int cmd_update_ref(int argc, const char **argv, const char *prefix);\n+extern int cmd_upload_archive(int argc, const char **argv, const char *prefix);\n extern int cmd_upload_tar(int argc, const char **argv, const char *prefix);\n extern int cmd_version(int argc, const char **argv, const char *prefix);\n extern int cmd_whatchanged(int argc, const char **argv, const char *prefix);\ndiff --git a/daemon.c b/daemon.c\nindex a4a08f3..5570878 100644\n--- a/daemon.c\n+++ b/daemon.c\n@@ -324,6 +324,12 @@ static int upload_pack(void)\n \treturn -1;\n }\n \n+static int upload_archive(void)\n+{\n+\texecl_git_cmd(\"upload-archive\", \".\", NULL);\n+\treturn -1;\n+}\n+\n static int upload_tar(void)\n {\n \texecl_git_cmd(\"upload-tar\", \".\", NULL);\n@@ -331,6 +337,7 @@ static int upload_tar(void)\n }\n \n static struct daemon_service daemon_service[] = {\n+\t{ \"upload-archive\", \"uploadarch\", upload_archive, 0, 1 },\n \t{ \"upload-pack\", \"uploadpack\", upload_pack, 1, 1 },\n \t{ \"upload-tar\", \"uploadtar\", upload_tar, 0, 1 },\n };\ndiff --git a/git.c b/git.c\nindex c62c5cf..315dc0b 100644\n--- a/git.c\n+++ b/git.c\n@@ -261,6 +261,7 @@ static void handle_internal_command(int \n \t\t{ \"unpack-objects\", cmd_unpack_objects, RUN_SETUP },\n \t\t{ \"update-index\", cmd_update_index, RUN_SETUP },\n \t\t{ \"update-ref\", cmd_update_ref, RUN_SETUP },\n+\t\t{ \"upload-archive\", cmd_upload_archive },\n \t\t{ \"upload-tar\", cmd_upload_tar },\n \t\t{ \"version\", cmd_version },\n \t\t{ \"whatchanged\", cmd_whatchanged, RUN_SETUP | USE_PAGER },\n-- \n1.4.2\n"},{"id":"26530","messageId":"cda58cb80609071026m216bcc19jcf43023381fd17e7@mail.gmail.com","threadId":"5483","inReplyTo":"450019C3.4030001@innova-card.com","subject":"Re: Add git-archive [take #2]","fromName":"Franck Bui-Huu","fromEmail":"vagabon.xyz@gmail.com","sentAt":"2006-09-07T17:26:13Z","receivedAt":"2006-09-07T17:26:13Z","isPatch":false,"sender":{"key":"vagabon.xyz@gmail.com","avatar":null},"body":"2006/9/7, Franck Bui-Huu <vagabon.xyz@gmail.com>:\n>\n>   4/ Progress indicator support. Junio wants to mimic upload-pack for\n>      that. But it will lead in a lot of duplicated code if we don't\n>      try to share code. Can we copy that code anyways and clean up\n>      later ?\n>\n\nBTW, could we move the side-band code into git_connect() function and\nadding to it a new parameter like:\n\nint git_connect(int fd[2], char *url, const char *prog, int side_band)\nor\nint git_connect(int fd[2], char *url, const char *prog, flags)\n\nHence it automatically spawns a side-band process in the client side\nand it also sends an extended option for git-daemon to ask for using\nside band for server services.\n\n-- \n               Franck\n"},{"id":"26552","messageId":"7v8xkvqjlq.fsf@assigned-by-dhcp.cox.net","threadId":"5483","inReplyTo":"450019C3.4030001@innova-card.com","subject":"Re: Add git-archive [take #2]","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-08T00:37:21Z","receivedAt":"2006-09-08T00:37:21Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Franck Bui-Huu <vagabon.xyz@gmail.com> writes:\n\n> I'm sending a new version of the patchset which allows 'git-archive'\n> and 'git-upload-archive' command. I tried to take into account all\n> feedbacks made by Junio and Rene, but there are still some open points.\n>\n>   1/ Allow 'git-upload-archive' command to enable/disable some\n>      formats. This should be done by 'git-upload-archive'.\n\nPerhaps.  I was thinking about the way how a site administrator\ncan configure such when upload-archive is spawned via git-daemon\n(for users coming from ssh and spawn an upload-archive on their\nown, it's their own process and upload-archive has no business\ndeciding what is allowed and what is forbidden).  Not very many\nclean ways I can think of unfortunately.\n\n>   2/ Can I remove 'git-upload-tar' command ?\n>   3/ Should I kill 'git-zip-tree' command ?\n\nWe do not deprecate commands that easily.  Notice we have kept\ngit-resolve for a long time (we should remove it and by now it\nshould be safe)?\n\nEspecially tar-tree --remote and upload-archive talks different\nprotocols, so it is not like not removing it is making your life\nmore difficult.  Perhaps after next release (1.4.3 or 1.5?  I\ndunno) in the new development cycle, we would start saying\n\"don't use tar-tree --remote, use archive --fmt=tar --remote\",\nand then the release after that (1.4.4 or 1.5.1?  Again I dunno)\nwe might remove it.  The same thing for zip-tree, although that\none has lived shorter so it might not be missed if we remove it\nearlier.\n\nIn any case, don't make removal of them as part of the series\nplease.  Let's make sure this new toy works well first, and then\nstart talking about removing things that have become obsolete.\n\n>   4/ Progress indicator support. Junio wants to mimic upload-pack for\n>      that. But it will lead in a lot of duplicated code if we don't\n>      try to share code. Can we copy that code anyways and clean up\n>      later ?\n\nRefactoring first is always preferred, since \"later\" tends to\ncome very late (or worse, never) for clean-up tasks than feature\nenhancements.\n\n>   5/ Should we use \"struct tree_desc\" based interface for tree parsing\n>      ? According to Rene it doesn't worth it as soon as you actually\n>      start to do something to the trees\n\nThat became a non-issue I agree.  Whichever is easier.\n"},{"id":"26557","messageId":"7vodtrnl0f.fsf@assigned-by-dhcp.cox.net","threadId":"5483","inReplyTo":"11576347251776-git-send-email-vagabon.xyz@gmail.com","subject":"Re: [PATCH 1/4] Add git-archive","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-08T02:35:12Z","receivedAt":"2006-09-08T02:35:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Franck Bui-Huu <vagabon.xyz@gmail.com> writes:\n\n> The main reason for making this API is to avoid using\n> git-{tar,zip}-tree commands, hence making them useless. Maybe it's\n> time for them to die ?\n\nThe answer is \"not yet\" and the above paragraph, at least the\nlast sentence and half, do not belong to the commit log message.\n\n> It also implements remote operations by defining a very simple\n> protocol: it first sends the name of the specific uploader followed\n> the repository name (git-upload-tar git://example.org/repo.git).\n> Then it sends options. It's done by sending a sequence of one\n> argument per packet, with prefix \"argument \", followed by a flush.\n\nI haven't looked the existing code closely recently, but my\nimpression was that if you want to make the protocol operable\nwith both git-daemon, ssh target, and local pipe, it is easier\nto make the request message exactly like you would invoke the\nremote command over the ssh connection in the target repository\n(see connect.c).  I am not sure how well the above\n\"git-upload-tar reponame\" would work.  I would have expected it\nto be \"git-upload-archive reponame\" with the first on-protocol\nparameter being \"--fmt=tar\".\n\nDoes your code work well when you run the remote archive\nfetching locally, i.e. \"git-archive --remote=../other.git\",\nI wonder?\n\n... goes on and reads the patch, notices that the protocol\ncommand is git-upload-archive with the archive location.  Which\nis GOOD.  It is just the above description is a tad stale.\n\n> diff --git a/archive.h b/archive.h\n> new file mode 100644\n> index 0000000..f33398e\n> --- /dev/null\n> +++ b/archive.h\n> @@ -0,0 +1,41 @@\n> +#ifndef ARCHIVE_H\n> +#define ARCHIVE_H\n> +\n> +#define MAX_EXTRA_ARGS\t32\n> +#define MAX_ARGS\t(MAX_EXTRA_ARGS + 32)\n> +\n> +struct archiver_args {\n> +\tconst char *base;\n> +\tstruct tree *tree;\n> +\tconst unsigned char *commit_sha1;\n> +\ttime_t time;\n> +\tconst char **pathspec;\n> +\tvoid *extra;\n> +};\n> +\n> +typedef int (*write_archive_fn_t)(struct archiver_args *);\n> +\n> +typedef void *(*parse_extra_args_fn_t)(int argc, const char **argv);\n> +\n> +struct archiver {\n> +\tconst char *name;\n> +\tconst char *remote;\n> +\tstruct archiver_args args;\n> +\twrite_archive_fn_t write_archive;\n> +\tparse_extra_args_fn_t parse_extra;\n> +};\n> +\n> +extern struct archiver archivers[];\n\nI thought the reason for archiver_args (and archiver_args.extra)\nwas because we wanted to avoid storing per invocation parameters\nin static variables, which would hamper reentrancy.  If one\nprocess is creating two archives, both format=tar, it might be\nreasonable for the code (future archiver enhancement, not your\ncurrent implementation of git-archive driver) to parse two sets\nof parameters first (to get separate archiver instances) and\ncall their write_archive, but if archivers[] list has the\nper-invocation parameter args then we are back to square one,\naren't we?\n\nReentrancy may not matter, but in any case the above archiver_args\nis not helping enough to improve the situation, I think.\n\nActually you may be able to get away by returning a copy of\narchivers[] element from get_archiver() when we need reentrancy\nin the future.  Of course the caller needs to free() it when it\nis done with it, since it is a per-invocation handle.\n\n> +void parse_treeish_arg(const char **argv, struct archiver_args *ar_args,\n> +\t\t       const char *prefix)\n> +{\n> +...\n> +\t//free(tree);\n> +\tar_args->tree = tree;\n> +\tar_args->commit_sha1 = commit_sha1;\n> +\tar_args->time = archive_time;\n> +}\n\nStray comment...\n\nOverall looks good, except you already know your issue #4 ;-).\n\nI haven't had a chance to look at connect.c code but I have a\nmild suspicion that full reuse of upload-pack code by moving\neverything into connect.c is not possible; at least the initial\nhandshake to determine if sideband is to be used is specific to\nupload-pack protocol which needed to bolt-it-on to an existing\nprotocol to support older clients and servers, and I do not\nthink we would want to carry that baggage for this new protocol.\n\nThe pipe setup code in upload-pack.c::create_pack_file() is\nquite specific to upload-pack.  I am not sure how much of it can\nbe reused by refactoring, but it may be worth a try.\n\nThe part that reads from fd 1 and 2 and to multiplex into the\nstream on the uploader side (the main while() loop in the same\ncreate_pack_file() function, and send_client_data() function),\nand the code to setup and demultiplex the bands on the\ndownloader side (setup_sideband() in fetch-clone.c) should be\nreusable as-is, I think.  They are defined as static so you\nwould need to move the code around to make them available from\nelsewhere.\n"},{"id":"26567","messageId":"45012752.4070300@innova-card.com","threadId":"5483","inReplyTo":"7v8xkvqjlq.fsf@assigned-by-dhcp.cox.net","subject":"Re: Add git-archive [take #2]","fromName":"Franck Bui-Huu","fromEmail":"vagabon.xyz@gmail.com","sentAt":"2006-09-08T08:18:26Z","receivedAt":"2006-09-08T08:18:26Z","isPatch":false,"sender":{"key":"vagabon.xyz@gmail.com","avatar":null},"body":"Junio C Hamano wrote:\n> Franck Bui-Huu <vagabon.xyz@gmail.com> writes:\n>>\n>>   2/ Can I remove 'git-upload-tar' command ?\n>>   3/ Should I kill 'git-zip-tree' command ?\n> \n> We do not deprecate commands that easily.  Notice we have kept\n> git-resolve for a long time (we should remove it and by now it\n> should be safe)?\n> \n\nheh ? I've just noticed that you removed 'git-upload-tar' from\nmaster branch (commit d9edcbd6061a392c1315ab6f3aedb9992a3c01b1).\n\nFuthermore I was thinking about 'git-zip-tree' removal because\nit's a very recent command. It shouldn't hurt to remove it now\nand make our life easier, not sure though...\n\n> Especially tar-tree --remote and upload-archive talks different\n> protocols, so it is not like not removing it is making your life\n> more difficult.  Perhaps after next release (1.4.3 or 1.5?  I\n\nsince you removed 'git-upload-tar', it would be good to remove\n'--remote' option from 'git-tar-tree' command as well. \n\n> In any case, don't make removal of them as part of the series\n> please.  Let's make sure this new toy works well first, and then\n> start talking about removing things that have become obsolete.\n> \n\nOK, I'll let you do that.\n\n\t\tFranck\n"},{"id":"26571","messageId":"edrald$87p$1@sea.gmane.org","threadId":"5483","inReplyTo":"45012752.4070300@innova-card.com","subject":"Re: Add git-archive [take #2]","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-09-08T08:47:07Z","receivedAt":"2006-09-08T08:47:07Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Franck Bui-Huu wrote:\n\n>> Especially tar-tree --remote and upload-archive talks different\n>> protocols, so it is not like not removing it is making your life\n>> more difficult.  Perhaps after next release (1.4.3 or 1.5?  I\n> \n> since you removed 'git-upload-tar', it would be good to remove\n> '--remote' option from 'git-tar-tree' command as well. \n\ngit-tar-tree --remote was talked about, so I'd rather have the removal of\nthis option was postponed.\n\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n"},{"id":"26574","messageId":"7v64fyivk0.fsf@assigned-by-dhcp.cox.net","threadId":"5483","inReplyTo":"45012752.4070300@innova-card.com","subject":"Re: Add git-archive [take #2]","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-08T08:58:39Z","receivedAt":"2006-09-08T08:58:39Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Franck Bui-Huu <vagabon.xyz@gmail.com> writes:\n\n> Junio C Hamano wrote:\n>> Franck Bui-Huu <vagabon.xyz@gmail.com> writes:\n>>>\n>>>   2/ Can I remove 'git-upload-tar' command ?\n>>>   3/ Should I kill 'git-zip-tree' command ?\n>> \n>> We do not deprecate commands that easily.  Notice we have kept\n>> git-resolve for a long time (we should remove it and by now it\n>> should be safe)?\n>\n> heh ? I've just noticed that you removed 'git-upload-tar' from\n> master branch (commit d9edcbd6061a392c1315ab6f3aedb9992a3c01b1).\n>\n> Futhermore I was thinking about 'git-zip-tree' removal because\n> it's a very recent command. It shouldn't hurt to remove it now\n> and make our life easier, not sure though...\n\nI do not think I removed upload-tar.  I removed it from daemon\nservice list and the documentation for daemon, because that part\nis a new code.\n\n\tgit tar-tree --remote=../linux-2.6/.git HEAD\n\tgit tar-tree --remote=kernel.org:git next\n\nshould still work; the former is \"from a directory next door\"\nand connect.c invokes local /bin/sh as the transport, and the\nlatter is \"ssh login to kernel.org and use ./git directory\".\n\nI recall from earlier review of your code, \"git archive\" should\nwork well over these two transports in addition to git://\nprotocol that talks with git-daemon.\n"},{"id":"26575","messageId":"45013114.1070808@innova-card.com","threadId":"5483","inReplyTo":"7vodtrnl0f.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 1/4] Add git-archive","fromName":"Franck Bui-Huu","fromEmail":"vagabon.xyz@gmail.com","sentAt":"2006-09-08T09:00:04Z","receivedAt":"2006-09-08T09:00:04Z","isPatch":true,"sender":{"key":"vagabon.xyz@gmail.com","avatar":null},"body":"Junio C Hamano wrote:\n> Franck Bui-Huu <vagabon.xyz@gmail.com> writes:\n> \n[snip]\n> \n> Does your code work well when you run the remote archive\n> fetching locally, i.e. \"git-archive --remote=../other.git\",\n> I wonder?\n> \n> ... goes on and reads the patch, notices that the protocol\n> command is git-upload-archive with the archive location.  Which\n> is GOOD.  It is just the above description is a tad stale.\n> \n\nyeah, sorry for that. I forgot to update the patch description.\n\n\n>> diff --git a/archive.h b/archive.h\n>> new file mode 100644\n>> index 0000000..f33398e\n>> --- /dev/null\n>> +++ b/archive.h\n>> @@ -0,0 +1,41 @@\n>> +#ifndef ARCHIVE_H\n>> +#define ARCHIVE_H\n>> +\n>> +#define MAX_EXTRA_ARGS\t32\n>> +#define MAX_ARGS\t(MAX_EXTRA_ARGS + 32)\n>> +\n\n[snip]\n\n> \n> I thought the reason for archiver_args (and archiver_args.extra)\n> was because we wanted to avoid storing per invocation parameters\n> in static variables, which would hamper reentrancy.  If one\n> process is creating two archives, both format=tar, it might be\n> reasonable for the code (future archiver enhancement, not your\n> current implementation of git-archive driver) to parse two sets\n> of parameters first (to get separate archiver instances) and\n> call their write_archive, but if archivers[] list has the\n> per-invocation parameter args then we are back to square one,\n> aren't we?\n> \n> Reentrancy may not matter, but in any case the above archiver_args\n> is not helping enough to improve the situation, I think.\n> \n> Actually you may be able to get away by returning a copy of\n> archivers[] element from get_archiver() when we need reentrancy\n> in the future.  Of course the caller needs to free() it when it\n> is done with it, since it is a per-invocation handle.\n> \n\nyup I did half work here. Does the following is ok now ?\n\n-- >8 --\n\nSubject: Add git-archive\n\ngit-archive is a command to make TAR and ZIP archives of a git tree.\nIt helps prevent a proliferation of git-{format}-tree commands.\n\nInstead of directly calling git-{tar,zip}-tree command, it defines\na very simple API, that archiver should implement and register in\n\"git-archive.c\". This API is made up by 2 functions whose prototype\nis defined in \"archive.h\" file.\n\n - The first one is used to parse 'extra' parameters which have\n   signification only for the specific archiver. That would allow\n   different archive backends to have different kind of options.\n\n - The second one is used to ask to an archive backend to build\n   the archive given some already resolved parameters.\n\nIt also implements remote operations by defining a very simple\nprotocol: it first sends 'git-upload-archive' followed the\nrepository name. Then it sends options. It's done by sending a\nsequence of one argument per packet, with prefix \"argument \",\nfollowed by a flush.\n\nThe remote protocol is implemented in \"git-archive.c\" for client\nside and is triggered by \"--remote=<repo>\" option. For example,\nto fetch a TAR archive in a remote repo, you can issue:\n\n$ git archive --format=tar --remote=git://xxx/yyy/zzz.git HEAD\n\nWe choose to not make a new command \"git-fetch-archive\" for example,\navoind one more GIT command which should be nice for users (less\ncommands to remember, keeps existing --remote option).\n\nSigned-off-by: Franck Bui-Huu <vagabon.xyz@gmail.com>\n---\n .gitignore                    |    1 \n Documentation/git-archive.txt |  100 ++++++++++++++++++\n Makefile                      |    3 -\n archive.h                     |   41 +++++++\n builtin-archive.c             |  225 +++++++++++++++++++++++++++++++++++++++++\n builtin.h                     |    1 \n generate-cmdlist.sh           |    1 \n git.c                         |    1 \n 8 files changed, 372 insertions(+), 1 deletions(-)\n\ndiff --git a/.gitignore b/.gitignore\nindex 78cb671..a3e7ca1 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -8,6 +8,7 @@ git-apply\n git-applymbox\n git-applypatch\n git-archimport\n+git-archive\n git-bisect\n git-branch\n git-cat-file\ndiff --git a/Documentation/git-archive.txt b/Documentation/git-archive.txt\nnew file mode 100644\nindex 0000000..913528d\n--- /dev/null\n+++ b/Documentation/git-archive.txt\n@@ -0,0 +1,100 @@\n+git-archive(1)\n+==============\n+\n+NAME\n+----\n+git-archive - Creates a archive of the files in the named tree\n+\n+\n+SYNOPSIS\n+--------\n+'git-archive' --format=<fmt> [--list] [--prefix=<prefix>/] [<extra>]\n+\t      [--remote=<repo>] <tree-ish> [path...]\n+\n+DESCRIPTION\n+-----------\n+Creates an archive of the specified format containing the tree\n+structure for the named tree.  If <prefix> is specified it is\n+prepended to the filenames in the archive.\n+\n+'git-archive' behaves differently when given a tree ID versus when\n+given a commit ID or tag ID.  In the first case the current time is\n+used as modification time of each file in the archive.  In the latter\n+case the commit time as recorded in the referenced commit object is\n+used instead.  Additionally the commit ID is stored in a global\n+extended pax header if the tar format is used; it can be extracted\n+using 'git-get-tar-commit-id'. In ZIP files it is stored as a file\n+comment.\n+\n+OPTIONS\n+-------\n+\n+--format=<fmt>::\n+\tFormat of the resulting archive: 'tar', 'zip'...\n+\n+--list::\n+\tShow all available formats.\n+\n+--prefix=<prefix>/::\n+\tPrepend <prefix>/ to each filename in the archive.\n+\n+<extra>::\n+\tThis can be any options that the archiver backend understand.\n+\n+--remote=<repo>::\n+\tInstead of making a tar archive from local repository,\n+\tretrieve a tar archive from a remote repository.\n+\n+<tree-ish>::\n+\tThe tree or commit to produce an archive for.\n+\n+path::\n+\tIf one or more paths are specified, include only these in the\n+\tarchive, otherwise include all files and subdirectories.\n+\n+CONFIGURATION\n+-------------\n+By default, file and directories modes are set to 0666 or 0777 in tar\n+archives.  It is possible to change this by setting the \"umask\" variable\n+in the repository configuration as follows :\n+\n+[tar]\n+        umask = 002\t;# group friendly\n+\n+The special umask value \"user\" indicates that the user's current umask\n+will be used instead. The default value remains 0, which means world\n+readable/writable files and directories.\n+\n+EXAMPLES\n+--------\n+git archive --format=tar --prefix=junk/ HEAD | (cd /var/tmp/ && tar xf -)::\n+\n+\tCreate a tar archive that contains the contents of the\n+\tlatest commit on the current branch, and extracts it in\n+\t`/var/tmp/junk` directory.\n+\n+git archive --format=tar --prefix=git-1.4.0/ v1.4.0 | gzip >git-1.4.0.tar.gz::\n+\n+\tCreate a compressed tarball for v1.4.0 release.\n+\n+git archive --format=tar --prefix=git-1.4.0/ v1.4.0{caret}\\{tree\\} | gzip >git-1.4.0.tar.gz::\n+\n+\tCreate a compressed tarball for v1.4.0 release, but without a\n+\tglobal extended pax header.\n+\n+git archive --format=zip --prefix=git-docs/ HEAD:Documentation/ > git-1.4.0-docs.zip::\n+\n+\tPut everything in the current head's Documentation/ directory\n+\tinto 'git-1.4.0-docs.zip', with the prefix 'git-docs/'.\n+\n+Author\n+------\n+Written by Franck Bui-Huu and Rene Scharfe.\n+\n+Documentation\n+--------------\n+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.\n+\n+GIT\n+---\n+Part of the gitlink:git[7] suite\ndiff --git a/Makefile b/Makefile\nindex 389daf7..51ed4dd 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -242,7 +242,7 @@ LIB_FILE=libgit.a\n XDIFF_LIB=xdiff/lib.a\n \n LIB_H = \\\n-\tblob.h cache.h commit.h csum-file.h delta.h \\\n+\tarchive.h blob.h cache.h commit.h csum-file.h delta.h \\\n \tdiff.h object.h pack.h para-walk.h pkt-line.h quote.h refs.h \\\n \trun-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \\\n \ttree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h\n@@ -267,6 +267,7 @@ LIB_OBJS = \\\n BUILTIN_OBJS = \\\n \tbuiltin-add.o \\\n \tbuiltin-apply.o \\\n+\tbuiltin-archive.o \\\n \tbuiltin-cat-file.o \\\n \tbuiltin-checkout-index.o \\\n \tbuiltin-check-ref-format.o \\\ndiff --git a/archive.h b/archive.h\nnew file mode 100644\nindex 0000000..24b016f\n--- /dev/null\n+++ b/archive.h\n@@ -0,0 +1,41 @@\n+#ifndef ARCHIVE_H\n+#define ARCHIVE_H\n+\n+#define MAX_EXTRA_ARGS\t32\n+#define MAX_ARGS\t(MAX_EXTRA_ARGS + 32)\n+\n+struct archiver_args {\n+\tconst char *base;\n+\tstruct tree *tree;\n+\tconst unsigned char *commit_sha1;\n+\ttime_t time;\n+\tconst char **pathspec;\n+\tvoid *extra;\n+};\n+\n+typedef int (*write_archive_fn_t)(struct archiver_args *);\n+\n+typedef void *(*parse_extra_args_fn_t)(int argc, const char **argv);\n+\n+struct archiver {\n+\tconst char *name;\n+\tconst char *remote;\n+\tstruct archiver_args args;\n+\twrite_archive_fn_t write_archive;\n+\tparse_extra_args_fn_t parse_extra;\n+};\n+\n+extern struct archiver archivers[];\n+\n+extern int parse_archive_args(int argc,\n+\t\t\t      const char **argv,\n+\t\t\t      struct archiver *ar);\n+\n+extern void parse_treeish_arg(const char **treeish,\n+\t\t\t      struct archiver_args *ar_args,\n+\t\t\t      const char *prefix);\n+\n+extern void parse_pathspec_arg(const char **pathspec,\n+\t\t\t       struct archiver_args *args);\n+\n+#endif\t/* ARCHIVE_H */\ndiff --git a/builtin-archive.c b/builtin-archive.c\nnew file mode 100644\nindex 0000000..5671cbd\n--- /dev/null\n+++ b/builtin-archive.c\n@@ -0,0 +1,225 @@\n+/*\n+ * Copyright (c) 2006 Franck Bui-Huu\n+ * Copyright (c) 2006 Rene Scharfe\n+ */\n+#include <time.h>\n+#include \"cache.h\"\n+#include \"builtin.h\"\n+#include \"archive.h\"\n+#include \"commit.h\"\n+#include \"tree-walk.h\"\n+#include \"exec_cmd.h\"\n+#include \"pkt-line.h\"\n+\n+static const char archive_usage[] = \\\n+\"git-archive --format=<fmt> [--prefix=<prefix>/] [<extra>] <tree-ish> [path...]\";\n+\n+\n+struct archiver archivers[] = { };\n+\n+\n+static int run_remote_archiver(struct archiver *ar, int argc,\n+\t\t\t       const char **argv)\n+{\n+\tchar *url, buf[1024];\n+\tint fd[2], i, len, rv;\n+\tpid_t pid;\n+\n+\tsprintf(buf, \"git-upload-archive\");\n+\n+\turl = strdup(ar->remote);\n+\tpid = git_connect(fd, url, buf);\n+\tif (pid < 0)\n+\t\treturn pid;\n+\n+\tfor (i = 1; i < argc; i++) {\n+\t\tif (!strncmp(argv[i], \"--remote=\", 9))\n+\t\t\tcontinue;\n+\t\tpacket_write(fd[1], \"argument %s\\n\", argv[i]);\n+\t}\n+\tpacket_flush(fd[1]);\n+\n+\tlen = packet_read_line(fd[0], buf, sizeof(buf));\n+\tif (!len)\n+\t\tdie(\"git-archive: expected ACK/NAK, got EOF\");\n+\tif (buf[len-1] == '\\n')\n+\t\tbuf[--len] = 0;\n+\tif (strcmp(buf, \"ACK\")) {\n+\t\tif (len > 5 && !strncmp(buf, \"NACK \", 5))\n+\t\t\tdie(\"git-archive: NACK %s\", buf + 5);\n+\t\tdie(\"git-archive: protocol error\");\n+\t}\n+\n+\tlen = packet_read_line(fd[0], buf, sizeof(buf));\n+\tif (len)\n+\t\tdie(\"git-archive: expected a flush\");\n+\n+\t/* Now, start reading from fd[0] and spit it out to stdout */\n+\trv = copy_fd(fd[0], 1);\n+\n+\tclose(fd[0]);\n+\trv |= finish_connect(pid);\n+\n+\treturn !!rv;\n+}\n+\n+static int init_archiver(const char *name, struct archiver *ar)\n+{\n+\tint rv = -1, i;\n+\n+\tfor (i = 0; i < ARRAY_SIZE(archivers); i++) {\n+\t\tif (!strcmp(name, archivers[i].name)) {\n+\t\t\tmemcpy(ar, &archivers[i], sizeof(struct archiver));\n+\t\t\trv = 0;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\treturn rv;\n+}\n+\n+void parse_pathspec_arg(const char **pathspec, struct archiver_args *ar_args)\n+{\n+\tar_args->pathspec = get_pathspec(ar_args->base, pathspec);\n+}\n+\n+void parse_treeish_arg(const char **argv, struct archiver_args *ar_args,\n+\t\t       const char *prefix)\n+{\n+\tconst char *name = argv[0];\n+\tconst unsigned char *commit_sha1;\n+\ttime_t archive_time;\n+\tstruct tree *tree;\n+\tstruct commit *commit;\n+\tunsigned char sha1[20];\n+\n+\tif (get_sha1(name, sha1))\n+\t\tdie(\"Not a valid object name\");\n+\n+\tcommit = lookup_commit_reference_gently(sha1, 1);\n+\tif (commit) {\n+\t\tcommit_sha1 = commit->object.sha1;\n+\t\tarchive_time = commit->date;\n+\t} else {\n+\t\tarchive_time = time(NULL);\n+\t}\n+\n+\ttree = parse_tree_indirect(sha1);\n+\tif (tree == NULL)\n+\t\tdie(\"not a tree object\");\n+\n+\tif (prefix) {\n+\t\tunsigned char tree_sha1[20];\n+\t\tunsigned int mode;\n+\t\tint err;\n+\n+\t\terr = get_tree_entry(tree->object.sha1, prefix,\n+\t\t\t\t     tree_sha1, &mode);\n+\t\tif (err || !S_ISDIR(mode))\n+\t\t\tdie(\"current working directory is untracked\");\n+\n+\t\tfree(tree);\n+\t\ttree = parse_tree_indirect(tree_sha1);\n+\t}\n+\tar_args->tree = tree;\n+\tar_args->commit_sha1 = commit_sha1;\n+\tar_args->time = archive_time;\n+}\n+\n+static const char *default_parse_extra(struct archiver *ar,\n+\t\t\t\t       const char **argv)\n+{\n+\tstatic char msg[64];\n+\n+\tsnprintf(msg, sizeof(msg) - 4, \"'%s' format does not handle %s\",\n+\t\t ar->name, *argv);\n+\n+\treturn strcat(msg, \"...\");\n+}\n+\n+int parse_archive_args(int argc, const char **argv, struct archiver *ar)\n+{\n+\tconst char *extra_argv[MAX_EXTRA_ARGS];\n+\tint extra_argc = 0;\n+\tconst char *format = NULL; /* some default values */\n+\tconst char *remote = NULL;\n+\tconst char *base = \"\";\n+\tint list = 0;\n+\tint i;\n+\n+\tfor (i = 1; i < argc; i++) {\n+\t\tconst char *arg = argv[i];\n+\n+\t\tif (!strcmp(arg, \"--list\") || !strcmp(arg, \"-l\")) {\n+\t\t\tlist = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strncmp(arg, \"--format=\", 9)) {\n+\t\t\tformat = arg + 9;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strncmp(arg, \"--prefix=\", 9)) {\n+\t\t\tbase = arg + 9;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strncmp(arg, \"--remote=\", 9)) {\n+\t\t\tremote = arg + 9;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"--\")) {\n+\t\t\ti++;\n+\t\t\tbreak;\n+\t\t}\n+\t\tif (arg[0] == '-') {\n+\t\t\textra_argv[extra_argc++] = arg;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tbreak;\n+\t}\n+\tif (list) {\n+\t\tif (!remote) {\n+\t\t\tfor (i = 0; i < ARRAY_SIZE(archivers); i++)\n+\t\t\t\tprintf(\"%s\\n\", archivers[i].name);\n+\t\t\texit(0);\n+\t\t}\n+\t\tdie(\"--list and --remote are mutually exclusive\");\n+\t}\n+\tif (argc - i < 1) {\n+\t\tdie(\"%s\", archive_usage);\n+\t}\n+\tif (!format){\n+\t\tdie(\"You must specify an archive format\");\n+\t}\n+\tif (init_archiver(format, ar) < 0) {\n+\t\tdie(\"Unknown archive format '%s'\", format);\n+\t}\n+\tif (extra_argc && !remote) {\n+\t\tif (!ar->parse_extra) {\n+\t\t\tdie(\"%s\", default_parse_extra(ar, extra_argv));\n+\t\t}\n+\t\tar->args.extra = ar->parse_extra(extra_argc, extra_argv);\n+\t}\n+\tar->remote = remote;\n+\tar->args.base = base;\n+\n+\treturn i;\n+}\n+\n+int cmd_archive(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct archiver ar;\n+\tint tree_idx;\n+\n+\ttree_idx = parse_archive_args(argc, argv, &ar);\n+\n+\tif (ar.remote)\n+\t\treturn run_remote_archiver(&ar, argc, argv);\n+\n+\tif (prefix == NULL)\n+\t\tprefix = setup_git_directory();\n+\n+\targv += tree_idx;\n+\tparse_treeish_arg(argv, &ar.args, prefix);\n+\tparse_pathspec_arg(argv + 1, &ar.args);\n+\n+\treturn ar.write_archive(&ar.args);\n+}\ndiff --git a/builtin.h b/builtin.h\nindex 8472c79..2391afb 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -15,6 +15,7 @@ extern int write_tree(unsigned char *sha\n \n extern int cmd_add(int argc, const char **argv, const char *prefix);\n extern int cmd_apply(int argc, const char **argv, const char *prefix);\n+extern int cmd_archive(int argc, const char **argv, const char *prefix);\n extern int cmd_cat_file(int argc, const char **argv, const char *prefix);\n extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);\n extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);\ndiff --git a/generate-cmdlist.sh b/generate-cmdlist.sh\nindex ec1eda2..5450918 100755\n--- a/generate-cmdlist.sh\n+++ b/generate-cmdlist.sh\n@@ -12,6 +12,7 @@ struct cmdname_help common_cmds[] = {\"\n sort <<\\EOF |\n add\n apply\n+archive\n bisect\n branch\n checkout\ndiff --git a/git.c b/git.c\nindex 82c8fee..c62c5cf 100644\n--- a/git.c\n+++ b/git.c\n@@ -218,6 +218,7 @@ static void handle_internal_command(int \n \t} commands[] = {\n \t\t{ \"add\", cmd_add, RUN_SETUP },\n \t\t{ \"apply\", cmd_apply },\n+\t\t{ \"archive\", cmd_archive },\n \t\t{ \"cat-file\", cmd_cat_file, RUN_SETUP },\n \t\t{ \"checkout-index\", cmd_checkout_index, RUN_SETUP },\n \t\t{ \"check-ref-format\", cmd_check_ref_format },\n-- \n1.4.2\n"},{"id":"26578","messageId":"45013B5A.8000301@innova-card.com","threadId":"5483","inReplyTo":"7v64fyivk0.fsf@assigned-by-dhcp.cox.net","subject":"Re: Add git-archive [take #2]","fromName":"Franck Bui-Huu","fromEmail":"vagabon.xyz@gmail.com","sentAt":"2006-09-08T09:43:54Z","receivedAt":"2006-09-08T09:43:54Z","isPatch":false,"sender":{"key":"vagabon.xyz@gmail.com","avatar":null},"body":"Junio C Hamano wrote:\n> \n> I do not think I removed upload-tar.  I removed it from daemon\n> service list and the documentation for daemon, because that part\n> is a new code.\n> \n> \tgit tar-tree --remote=../linux-2.6/.git HEAD\n> \tgit tar-tree --remote=kernel.org:git next\n> \n> should still work; the former is \"from a directory next door\"\n> and connect.c invokes local /bin/sh as the transport, and the\n> latter is \"ssh login to kernel.org and use ./git directory\".\n> \n\nSorry I was speaking about the git protocol. It has been included\nin master branch. But\n\n\tgit tar-tree --remote=git://anything/repo.git\n\ndoes not work anymore, does it ? Do you plan to make it work again\nwith git-upload-archive (that would need some modifications in\ngit-tar-tree --remote code) or just let the --remote option work\nfor local and ssh transport (that would be one good reason for using\nfor git-archive instead of git-tar-tree) ?\n\n> I recall from earlier review of your code, \"git archive\" should\n> work well over these two transports in addition to git://\n> protocol that talks with git-daemon.\n> \nyes\n\t\tFranck\n"},{"id":"26588","messageId":"4501D0B8.8060103@lsrfire.ath.cx","threadId":"5483","inReplyTo":"7v8xkvqjlq.fsf@assigned-by-dhcp.cox.net","subject":"Re: Add git-archive [take #2]","fromName":"Rene Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2006-09-08T20:21:12Z","receivedAt":"2006-09-08T20:21:12Z","isPatch":false,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Junio C Hamano schrieb:\n> Franck Bui-Huu <vagabon.xyz@gmail.com> writes:\n> \n>> I'm sending a new version of the patchset which allows \n>> 'git-archive' and 'git-upload-archive' command. I tried to take \n>> into account all feedbacks made by Junio and Rene, but there are \n>> still some open points.\n>> \n>> 1/ Allow 'git-upload-archive' command to enable/disable some \n>> formats. This should be done by 'git-upload-archive'.\n> \n> Perhaps.  I was thinking about the way how a site administrator can \n> configure such when upload-archive is spawned via git-daemon (for \n> users coming from ssh and spawn an upload-archive on their own, it's \n> their own process and upload-archive has no business deciding what is\n>  allowed and what is forbidden).  Not very many clean ways I can\n> think of unfortunately.\n\nMmpf, ssh is (one of the things) in my blind spot.  Do you mean a\nssh+git-shell connection?  One could argue that since this is a\nrestricted connection anyway upload-archive _has_ a right to restrict\narchive format etc., too.  On a full, unrestricted ssh connection one\ncan start git-archive directly.  I'd do that anyway because I'm used to\ndo this with tar. ;-)\n\nAnyway, I think having config options for git-upload-archive for\nrestricting formats and compression levels is as clean as we can get in\nthe absence of a way for upload-archive to detect which protocol is\nused for the current connection.  Mmh, maybe an environment variable\nwhich is set by the daemon can be used?  This is no dirtier than what\nwebservers do..\n\nRené\n"},{"id":"26590","messageId":"4501D0C5.702@lsrfire.ath.cx","threadId":"5483","inReplyTo":"45013114.1070808@innova-card.com","subject":"Re: [PATCH 1/4] Add git-archive","fromName":"Rene Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2006-09-08T20:21:25Z","receivedAt":"2006-09-08T20:21:25Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Only a few trivial comments, as I managed to catch a cold somehow and\ncan't think straight for longer than three seconds.\n\n>  .gitignore                    |    1 \n>  Documentation/git-archive.txt |  100 ++++++++++++++++++\n>  Makefile                      |    3 -\n>  archive.h                     |   41 +++++++\n>  builtin-archive.c             |  225 +++++++++++++++++++++++++++++++++++++++++\n>  builtin.h                     |    1 \n>  generate-cmdlist.sh           |    1 \n>  git.c                         |    1 \n>  8 files changed, 372 insertions(+), 1 deletions(-)\n> \n> diff --git a/.gitignore b/.gitignore\n> index 78cb671..a3e7ca1 100644\n> --- a/.gitignore\n> +++ b/.gitignore\n> @@ -8,6 +8,7 @@ git-apply\n>  git-applymbox\n>  git-applypatch\n>  git-archimport\n> +git-archive\n>  git-bisect\n>  git-branch\n>  git-cat-file\n> diff --git a/Documentation/git-archive.txt b/Documentation/git-archive.txt\n> new file mode 100644\n> index 0000000..913528d\n> --- /dev/null\n> +++ b/Documentation/git-archive.txt\n> @@ -0,0 +1,100 @@\n> +git-archive(1)\n> +==============\n> +\n> +NAME\n> +----\n> +git-archive - Creates a archive of the files in the named tree\n> +\n> +\n> +SYNOPSIS\n> +--------\n> +'git-archive' --format=<fmt> [--list] [--prefix=<prefix>/] [<extra>]\n> +\t      [--remote=<repo>] <tree-ish> [path...]\n> +\n> +DESCRIPTION\n> +-----------\n> +Creates an archive of the specified format containing the tree\n> +structure for the named tree.  If <prefix> is specified it is\n> +prepended to the filenames in the archive.\n> +\n> +'git-archive' behaves differently when given a tree ID versus when\n> +given a commit ID or tag ID.  In the first case the current time is\n> +used as modification time of each file in the archive.  In the latter\n> +case the commit time as recorded in the referenced commit object is\n> +used instead.  Additionally the commit ID is stored in a global\n> +extended pax header if the tar format is used; it can be extracted\n> +using 'git-get-tar-commit-id'. In ZIP files it is stored as a file\n> +comment.\n> +\n> +OPTIONS\n> +-------\n> +\n> +--format=<fmt>::\n> +\tFormat of the resulting archive: 'tar', 'zip'...\n> +\n> +--list::\n> +\tShow all available formats.\n> +\n> +--prefix=<prefix>/::\n> +\tPrepend <prefix>/ to each filename in the archive.\n> +\n> +<extra>::\n> +\tThis can be any options that the archiver backend understand.\n> +\n> +--remote=<repo>::\n> +\tInstead of making a tar archive from local repository,\n> +\tretrieve a tar archive from a remote repository.\n> +\n> +<tree-ish>::\n> +\tThe tree or commit to produce an archive for.\n> +\n> +path::\n> +\tIf one or more paths are specified, include only these in the\n> +\tarchive, otherwise include all files and subdirectories.\n> +\n> +CONFIGURATION\n> +-------------\n> +By default, file and directories modes are set to 0666 or 0777 in tar\n> +archives.  It is possible to change this by setting the \"umask\" variable\n> +in the repository configuration as follows :\n> +\n> +[tar]\n> +        umask = 002\t;# group friendly\n> +\n> +The special umask value \"user\" indicates that the user's current umask\n> +will be used instead. The default value remains 0, which means world\n> +readable/writable files and directories.\n> +\n> +EXAMPLES\n> +--------\n> +git archive --format=tar --prefix=junk/ HEAD | (cd /var/tmp/ && tar xf -)::\n> +\n> +\tCreate a tar archive that contains the contents of the\n> +\tlatest commit on the current branch, and extracts it in\n> +\t`/var/tmp/junk` directory.\n> +\n> +git archive --format=tar --prefix=git-1.4.0/ v1.4.0 | gzip >git-1.4.0.tar.gz::\n> +\n> +\tCreate a compressed tarball for v1.4.0 release.\n> +\n> +git archive --format=tar --prefix=git-1.4.0/ v1.4.0{caret}\\{tree\\} | gzip >git-1.4.0.tar.gz::\n> +\n> +\tCreate a compressed tarball for v1.4.0 release, but without a\n> +\tglobal extended pax header.\n> +\n> +git archive --format=zip --prefix=git-docs/ HEAD:Documentation/ > git-1.4.0-docs.zip::\n> +\n> +\tPut everything in the current head's Documentation/ directory\n> +\tinto 'git-1.4.0-docs.zip', with the prefix 'git-docs/'.\n> +\n> +Author\n> +------\n> +Written by Franck Bui-Huu and Rene Scharfe.\n> +\n> +Documentation\n> +--------------\n> +Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.\n> +\n> +GIT\n> +---\n> +Part of the gitlink:git[7] suite\n> diff --git a/Makefile b/Makefile\n> index 389daf7..51ed4dd 100644\n> --- a/Makefile\n> +++ b/Makefile\n> @@ -242,7 +242,7 @@ LIB_FILE=libgit.a\n>  XDIFF_LIB=xdiff/lib.a\n>  \n>  LIB_H = \\\n> -\tblob.h cache.h commit.h csum-file.h delta.h \\\n> +\tarchive.h blob.h cache.h commit.h csum-file.h delta.h \\\n>  \tdiff.h object.h pack.h para-walk.h pkt-line.h quote.h refs.h \\\n>  \trun-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \\\n>  \ttree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h\n> @@ -267,6 +267,7 @@ LIB_OBJS = \\\n>  BUILTIN_OBJS = \\\n>  \tbuiltin-add.o \\\n>  \tbuiltin-apply.o \\\n> +\tbuiltin-archive.o \\\n>  \tbuiltin-cat-file.o \\\n>  \tbuiltin-checkout-index.o \\\n>  \tbuiltin-check-ref-format.o \\\n> diff --git a/archive.h b/archive.h\n> new file mode 100644\n> index 0000000..24b016f\n> --- /dev/null\n> +++ b/archive.h\n> @@ -0,0 +1,41 @@\n> +#ifndef ARCHIVE_H\n> +#define ARCHIVE_H\n> +\n> +#define MAX_EXTRA_ARGS\t32\n> +#define MAX_ARGS\t(MAX_EXTRA_ARGS + 32)\n> +\n> +struct archiver_args {\n> +\tconst char *base;\n> +\tstruct tree *tree;\n> +\tconst unsigned char *commit_sha1;\n> +\ttime_t time;\n> +\tconst char **pathspec;\n> +\tvoid *extra;\n> +};\n> +\n> +typedef int (*write_archive_fn_t)(struct archiver_args *);\n> +\n> +typedef void *(*parse_extra_args_fn_t)(int argc, const char **argv);\n> +\n> +struct archiver {\n> +\tconst char *name;\n> +\tconst char *remote;\n> +\tstruct archiver_args args;\n> +\twrite_archive_fn_t write_archive;\n> +\tparse_extra_args_fn_t parse_extra;\n> +};\n> +\n> +extern struct archiver archivers[];\n> +\n> +extern int parse_archive_args(int argc,\n> +\t\t\t      const char **argv,\n> +\t\t\t      struct archiver *ar);\n> +\n> +extern void parse_treeish_arg(const char **treeish,\n> +\t\t\t      struct archiver_args *ar_args,\n> +\t\t\t      const char *prefix);\n> +\n> +extern void parse_pathspec_arg(const char **pathspec,\n> +\t\t\t       struct archiver_args *args);\n> +\n> +#endif\t/* ARCHIVE_H */\n> diff --git a/builtin-archive.c b/builtin-archive.c\n> new file mode 100644\n> index 0000000..5671cbd\n> --- /dev/null\n> +++ b/builtin-archive.c\n> @@ -0,0 +1,225 @@\n> +/*\n> + * Copyright (c) 2006 Franck Bui-Huu\n> + * Copyright (c) 2006 Rene Scharfe\n> + */\n> +#include <time.h>\n> +#include \"cache.h\"\n> +#include \"builtin.h\"\n> +#include \"archive.h\"\n> +#include \"commit.h\"\n> +#include \"tree-walk.h\"\n> +#include \"exec_cmd.h\"\n> +#include \"pkt-line.h\"\n> +\n> +static const char archive_usage[] = \\\n> +\"git-archive --format=<fmt> [--prefix=<prefix>/] [<extra>] <tree-ish> [path...]\";\n> +\n> +\n> +struct archiver archivers[] = { };\n> +\n> +\n> +static int run_remote_archiver(struct archiver *ar, int argc,\n> +\t\t\t       const char **argv)\n> +{\n> +\tchar *url, buf[1024];\n> +\tint fd[2], i, len, rv;\n> +\tpid_t pid;\n> +\n> +\tsprintf(buf, \"git-upload-archive\");\n> +\n> +\turl = strdup(ar->remote);\n\nxstrdup()\n\n> +\tpid = git_connect(fd, url, buf);\n> +\tif (pid < 0)\n> +\t\treturn pid;\n> +\n> +\tfor (i = 1; i < argc; i++) {\n> +\t\tif (!strncmp(argv[i], \"--remote=\", 9))\n> +\t\t\tcontinue;\n> +\t\tpacket_write(fd[1], \"argument %s\\n\", argv[i]);\n> +\t}\n> +\tpacket_flush(fd[1]);\n> +\n> +\tlen = packet_read_line(fd[0], buf, sizeof(buf));\n> +\tif (!len)\n> +\t\tdie(\"git-archive: expected ACK/NAK, got EOF\");\n> +\tif (buf[len-1] == '\\n')\n> +\t\tbuf[--len] = 0;\n> +\tif (strcmp(buf, \"ACK\")) {\n> +\t\tif (len > 5 && !strncmp(buf, \"NACK \", 5))\n> +\t\t\tdie(\"git-archive: NACK %s\", buf + 5);\n> +\t\tdie(\"git-archive: protocol error\");\n> +\t}\n> +\n> +\tlen = packet_read_line(fd[0], buf, sizeof(buf));\n> +\tif (len)\n> +\t\tdie(\"git-archive: expected a flush\");\n> +\n> +\t/* Now, start reading from fd[0] and spit it out to stdout */\n> +\trv = copy_fd(fd[0], 1);\n> +\n> +\tclose(fd[0]);\n> +\trv |= finish_connect(pid);\n> +\n> +\treturn !!rv;\n> +}\n> +\n> +static int init_archiver(const char *name, struct archiver *ar)\n> +{\n> +\tint rv = -1, i;\n> +\n> +\tfor (i = 0; i < ARRAY_SIZE(archivers); i++) {\n> +\t\tif (!strcmp(name, archivers[i].name)) {\n> +\t\t\tmemcpy(ar, &archivers[i], sizeof(struct archiver));\n> +\t\t\trv = 0;\n> +\t\t\tbreak;\n> +\t\t}\n> +\t}\n> +\treturn rv;\n> +}\n> +\n> +void parse_pathspec_arg(const char **pathspec, struct archiver_args *ar_args)\n> +{\n> +\tar_args->pathspec = get_pathspec(ar_args->base, pathspec);\n> +}\n> +\n> +void parse_treeish_arg(const char **argv, struct archiver_args *ar_args,\n> +\t\t       const char *prefix)\n> +{\n> +\tconst char *name = argv[0];\n> +\tconst unsigned char *commit_sha1;\n> +\ttime_t archive_time;\n> +\tstruct tree *tree;\n> +\tstruct commit *commit;\n> +\tunsigned char sha1[20];\n> +\n> +\tif (get_sha1(name, sha1))\n> +\t\tdie(\"Not a valid object name\");\n> +\n> +\tcommit = lookup_commit_reference_gently(sha1, 1);\n> +\tif (commit) {\n> +\t\tcommit_sha1 = commit->object.sha1;\n> +\t\tarchive_time = commit->date;\n> +\t} else {\n> +\t\tarchive_time = time(NULL);\n> +\t}\n> +\n> +\ttree = parse_tree_indirect(sha1);\n> +\tif (tree == NULL)\n> +\t\tdie(\"not a tree object\");\n> +\n> +\tif (prefix) {\n> +\t\tunsigned char tree_sha1[20];\n> +\t\tunsigned int mode;\n> +\t\tint err;\n> +\n> +\t\terr = get_tree_entry(tree->object.sha1, prefix,\n> +\t\t\t\t     tree_sha1, &mode);\n> +\t\tif (err || !S_ISDIR(mode))\n> +\t\t\tdie(\"current working directory is untracked\");\n> +\n> +\t\tfree(tree);\n> +\t\ttree = parse_tree_indirect(tree_sha1);\n> +\t}\n> +\tar_args->tree = tree;\n> +\tar_args->commit_sha1 = commit_sha1;\n> +\tar_args->time = archive_time;\n> +}\n> +\n> +static const char *default_parse_extra(struct archiver *ar,\n> +\t\t\t\t       const char **argv)\n> +{\n> +\tstatic char msg[64];\n> +\n> +\tsnprintf(msg, sizeof(msg) - 4, \"'%s' format does not handle %s\",\n> +\t\t ar->name, *argv);\n> +\n> +\treturn strcat(msg, \"...\");\n> +}\n> +\n> +int parse_archive_args(int argc, const char **argv, struct archiver *ar)\n> +{\n> +\tconst char *extra_argv[MAX_EXTRA_ARGS];\n> +\tint extra_argc = 0;\n> +\tconst char *format = NULL; /* some default values */\n\nThis comment does not convey any information.\n\n> +\tconst char *remote = NULL;\n> +\tconst char *base = \"\";\n> +\tint list = 0;\n> +\tint i;\n> +\n> +\tfor (i = 1; i < argc; i++) {\n> +\t\tconst char *arg = argv[i];\n> +\n> +\t\tif (!strcmp(arg, \"--list\") || !strcmp(arg, \"-l\")) {\n> +\t\t\tlist = 1;\n> +\t\t\tcontinue;\n> +\t\t}\n> +\t\tif (!strncmp(arg, \"--format=\", 9)) {\n> +\t\t\tformat = arg + 9;\n> +\t\t\tcontinue;\n> +\t\t}\n> +\t\tif (!strncmp(arg, \"--prefix=\", 9)) {\n> +\t\t\tbase = arg + 9;\n> +\t\t\tcontinue;\n> +\t\t}\n> +\t\tif (!strncmp(arg, \"--remote=\", 9)) {\n> +\t\t\tremote = arg + 9;\n> +\t\t\tcontinue;\n> +\t\t}\n> +\t\tif (!strcmp(arg, \"--\")) {\n> +\t\t\ti++;\n> +\t\t\tbreak;\n> +\t\t}\n> +\t\tif (arg[0] == '-') {\n> +\t\t\textra_argv[extra_argc++] = arg;\n\nOverrun is not checked.\n\n> +\t\t\tcontinue;\n> +\t\t}\n> +\t\tbreak;\n> +\t}\n> +\tif (list) {\n> +\t\tif (!remote) {\n> +\t\t\tfor (i = 0; i < ARRAY_SIZE(archivers); i++)\n> +\t\t\t\tprintf(\"%s\\n\", archivers[i].name);\n> +\t\t\texit(0);\n> +\t\t}\n> +\t\tdie(\"--list and --remote are mutually exclusive\");\n> +\t}\n\nNot sure if we really need a list option.  I guess it only really\nmakes sense if we have more than five formats.  I have no _strong_\nfeelings against it, though. *shrug*\n\n> +\tif (argc - i < 1) {\n> +\t\tdie(\"%s\", archive_usage);\n\nusage()\n\n> +\t}\n> +\tif (!format){\n> +\t\tdie(\"You must specify an archive format\");\n> +\t}\n> +\tif (init_archiver(format, ar) < 0) {\n> +\t\tdie(\"Unknown archive format '%s'\", format);\n> +\t}\n> +\tif (extra_argc && !remote) {\n> +\t\tif (!ar->parse_extra) {\n> +\t\t\tdie(\"%s\", default_parse_extra(ar, extra_argv));\n> +\t\t}\n> +\t\tar->args.extra = ar->parse_extra(extra_argc, extra_argv);\n> +\t}\n> +\tar->remote = remote;\n> +\tar->args.base = base;\n> +\n> +\treturn i;\n> +}\n> +\n> +int cmd_archive(int argc, const char **argv, const char *prefix)\n> +{\n> +\tstruct archiver ar;\n> +\tint tree_idx;\n> +\n> +\ttree_idx = parse_archive_args(argc, argv, &ar);\n> +\n> +\tif (ar.remote)\n> +\t\treturn run_remote_archiver(&ar, argc, argv);\n> +\n> +\tif (prefix == NULL)\n> +\t\tprefix = setup_git_directory();\n> +\n> +\targv += tree_idx;\n> +\tparse_treeish_arg(argv, &ar.args, prefix);\n> +\tparse_pathspec_arg(argv + 1, &ar.args);\n> +\n> +\treturn ar.write_archive(&ar.args);\n> +}\n> diff --git a/builtin.h b/builtin.h\n> index 8472c79..2391afb 100644\n> --- a/builtin.h\n> +++ b/builtin.h\n> @@ -15,6 +15,7 @@ extern int write_tree(unsigned char *sha\n>  \n>  extern int cmd_add(int argc, const char **argv, const char *prefix);\n>  extern int cmd_apply(int argc, const char **argv, const char *prefix);\n> +extern int cmd_archive(int argc, const char **argv, const char *prefix);\n>  extern int cmd_cat_file(int argc, const char **argv, const char *prefix);\n>  extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);\n>  extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);\n> diff --git a/generate-cmdlist.sh b/generate-cmdlist.sh\n> index ec1eda2..5450918 100755\n> --- a/generate-cmdlist.sh\n> +++ b/generate-cmdlist.sh\n> @@ -12,6 +12,7 @@ struct cmdname_help common_cmds[] = {\"\n>  sort <<\\EOF |\n>  add\n>  apply\n> +archive\n>  bisect\n>  branch\n>  checkout\n> diff --git a/git.c b/git.c\n> index 82c8fee..c62c5cf 100644\n> --- a/git.c\n> +++ b/git.c\n> @@ -218,6 +218,7 @@ static void handle_internal_command(int \n>  \t} commands[] = {\n>  \t\t{ \"add\", cmd_add, RUN_SETUP },\n>  \t\t{ \"apply\", cmd_apply },\n> +\t\t{ \"archive\", cmd_archive },\n>  \t\t{ \"cat-file\", cmd_cat_file, RUN_SETUP },\n>  \t\t{ \"checkout-index\", cmd_checkout_index, RUN_SETUP },\n>  \t\t{ \"check-ref-format\", cmd_check_ref_format },\n"},{"id":"26591","messageId":"4501D0CA.7000206@lsrfire.ath.cx","threadId":"5483","inReplyTo":"ednb29$u0u$1@sea.gmane.org","subject":"Re: [PATCH 1/2] Add git-archive","fromName":"Rene Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2006-09-08T20:21:30Z","receivedAt":"2006-09-08T20:21:30Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Jakub Narebski schrieb:\n> Rene Scharfe wrote:\n> \n>> IMHO should work like in the following example, and the code above \n>> cuts off the Documentation part:\n>> \n>> $ cd Documentation $ git-archive --format=tar --prefix=v1.0/ HEAD howto | tar tf - \n>> v1.0/howto/ \n>> v1.0/howto/isolate-bugs-with-bisect.txt ...\n>> \n>> I agree that simple subtree matching would be enough, at least for \n>> now.\n> \n> What about\n> \n> $ git-archive --format=tar --prefix=v1.0/ HEAD:Documentation/howto\n\nThat is fine, too (cutting off Documentation/howto).\n\nMy comment above was about the piece of code that handles cd'ing around in\nthe repository.  git-tar-tree ignores the current working directory -- you\nalways get the full tree put into your tar file, and you have to do the\n\"trick\" you mentioned if you want to archive only a subtree.  This is a bit\nstrange, so I think we should do it right from the start in git-archive.\n\nRené\n"},{"id":"26589","messageId":"4501D0CF.70306@lsrfire.ath.cx","threadId":"5483","inReplyTo":"11576347252834-git-send-email-vagabon.xyz@gmail.com","subject":"Re: [PATCH 2/4] git-archive: wire up TAR format.","fromName":"Rene Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2006-09-08T20:21:35Z","receivedAt":"2006-09-08T20:21:35Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Franck Bui-Huu schrieb:\n> From: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>\n> \n> Signed-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>\n> Signed-off-by: Franck Bui-Huu <vagabon.xyz@gmail.com>\n\nI did not sign off this exact patch.  I wrote and submitted the\nbuiltin-tar-tree.c part, with memory leak and all, then sent a note\non where the leak needs to be plugged.  You put it together and\nconverted it to struct archiver_args.  I'd very much have liked to\nsee a comment stating this.  Or simply just say \"based on code by\nRene\" or something.  The same is true for patch 3/4.\n\n> ---\n>  archive.h          |    4 +++\n>  builtin-archive.c  |    4 ++-\n>  builtin-tar-tree.c |   67 ++++++++++++++++++++++++++++++++++++++++++++++++++++\n>  3 files changed, 74 insertions(+), 1 deletions(-)\n> \n> diff --git a/archive.h b/archive.h\n> index f33398e..3690c53 100644\n> --- a/archive.h\n> +++ b/archive.h\n> @@ -37,5 +37,9 @@ extern void parse_treeish_arg(const char\n>  \n>  extern void parse_pathspec_arg(const char **pathspec,\n>  \t\t\t       struct archiver_args *args);\n> +/*\n> + *\n> + */\n\nEspecially I would not have signed off this invisible comment. ;)\n\nRené\n"},{"id":"26601","messageId":"7vzmdaf32s.fsf@assigned-by-dhcp.cox.net","threadId":"5483","inReplyTo":"45013B5A.8000301@innova-card.com","subject":"Re: Add git-archive [take #2]","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-08T21:42:03Z","receivedAt":"2006-09-08T21:42:03Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Franck Bui-Huu <vagabon.xyz@gmail.com> writes:\n\n> Sorry I was speaking about the git protocol. It has been included\n> in master branch.\n\nI do not think so.  The recent \"master\" history is like this\n(look at \"gitk --full-history v1.4.2..master -- daemon.c\"):\n\nd819e4e daemon: prepare for multiple services\n\nThis introduced the daemon_service[] table and run_service\nfacility.  The service table contained upload-pack only.\n\nThis was merged into \"master\" and pushed out with \n\n1efca00 Merge early part of branch 'jc/daemon'\n\nAt this point, 'jc/daemon' topic branch contained the d819e4e\nabove and this one, which added upload-tar to the service table:\n\n74c0cc2 daemon: add upload-tar service.\n\nthis was part of \"next\" but was not in \"master\".  This made\n\"tar-tree --remote\" against git-daemon usable in \"next\".  But\nbefore I pushed this out to \"master\", upload-archive design you\nand Rene were working on became a lot more promising and\nattractive.  Especially, discussion with Rene made me realize\nthat redoing upload-xxx protocol for each archiver was not a\ngood design.  So I did:\n\nd9edcbd Revert \"daemon: add upload-tar service.\"\n\non 'jc/daemon' branch.  And then that topic was merged into\n\"master\" and pushed out, because that will allow you and Rene to\nwork your patch against \"master\" which would be nicer to not\njust you but to everybody -- other people can test your patches\nbefore they hit my tree on any branch.\n\nIn other words,\n\n> \tgit tar-tree --remote=git://anything/repo.git\n\nnever worked in \"master\".  At least that is how I wanted the\ncommit ancestry graph to look like and why the above merges and\nreverts were done in the order described above.\n\nOnce git archive hits \"master\", we should announce that \"git\ntar-tree\" and \"git zip-tree\" are deprecated, and we will remove\nthem sometime later.  As part of the same announcement we could\nsay that \"git tar-tree --remote\" works with transports other\nthan git-daemon, but we do not plan to add git native transport\nsupport to it before its removal, because \"git archive\" is\npreferred method both on local and remote archiving from then\non.\n"},{"id":"26600","messageId":"7vslj2f32p.fsf@assigned-by-dhcp.cox.net","threadId":"5483","inReplyTo":"4501D0B8.8060103@lsrfire.ath.cx","subject":"Re: Add git-archive [take #2]","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-08T21:42:06Z","receivedAt":"2006-09-08T21:42:06Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Rene Scharfe <rene.scharfe@lsrfire.ath.cx> writes:\n\n> Junio C Hamano schrieb:\n>> Franck Bui-Huu <vagabon.xyz@gmail.com> writes:\n>> \n>>> 1/ Allow 'git-upload-archive' command to enable/disable some \n>>> formats. This should be done by 'git-upload-archive'.\n>> \n>> Perhaps.  I was thinking about the way how a site administrator can \n>> configure such when upload-archive is spawned via git-daemon (for \n>> users coming from ssh and spawn an upload-archive on their own, it's \n>> their own process and upload-archive has no business deciding what is\n>>  allowed and what is forbidden).  Not very many clean ways I can\n>> think of unfortunately.\n>\n> Mmpf, ssh is (one of the things) in my blind spot.  Do you mean a\n> ssh+git-shell connection?  One could argue that since this is a\n> restricted connection anyway upload-archive _has_ a right to restrict\n> archive format etc., too.  On a full, unrestricted ssh connection one\n> can start git-archive directly.  I'd do that anyway because I'm used to\n> do this with tar. ;-)\n\nAh, I was not thinking about git-shell and it might want to be\nrestrictive.\n\n> ... Mmh, maybe an environment variable\n> which is set by the daemon can be used?  This is no dirtier than what\n> webservers do..\n\nExactly my thought, except \"no dirtier\" part I did not think\nthrough but now you said it I tend to agree.\n\nSo site administrator can ask git-daemon to export some\nenvironment variable that git-upload-archive notices and\nrestrict service.  If we choose to we can add a similar facility\nto set the same environment variable to git-shell, so services\nto retricted ssh users can be limited the same way by the\nadministrator.  That sounds like a good plan; we do not have\nto do that for git-shell until somebody asks.\n"},{"id":"26602","messageId":"7vlkouf32i.fsf@assigned-by-dhcp.cox.net","threadId":"5483","inReplyTo":"4501D0CF.70306@lsrfire.ath.cx","subject":"Re: [PATCH 2/4] git-archive: wire up TAR format.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-08T21:42:13Z","receivedAt":"2006-09-08T21:42:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Rene Scharfe <rene.scharfe@lsrfire.ath.cx> writes:\n\n> I did not sign off this exact patch.  I wrote and submitted the\n> builtin-tar-tree.c part, with memory leak and all, then sent a note\n> on where the leak needs to be plugged.  You put it together and\n> converted it to struct archiver_args.  I'd very much have liked to\n> see a comment stating this.  Or simply just say \"based on code by\n> Rene\" or something.  The same is true for patch 3/4.\n>...\n\nThanks for clarification -- I also was wondering if you two were\nworking as a team exchanging drafts and the message I saw was\nthe fruit of such collaboration (like the way Johannes/Alex team\nworked on C rewrite of merge-recursive).  Otherwise the sign-off\nwas indeed inappropriate.\n\n>> +/*\n>> + *\n>> + */\n>\n> Especially I would not have signed off this invisible comment. ;)\n\nI take your response is a mild NAK.\n"},{"id":"26614","messageId":"7vk64derfd.fsf@assigned-by-dhcp.cox.net","threadId":"5483","inReplyTo":"7vlkouf32i.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 2/4] git-archive: wire up TAR format.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-09T01:53:42Z","receivedAt":"2006-09-09T01:53:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <junkio@cox.net> writes:\n\n> Rene Scharfe <rene.scharfe@lsrfire.ath.cx> writes:\n>\n>> I did not sign off this exact patch.  I wrote and submitted the\n>> builtin-tar-tree.c part, with memory leak and all, then sent a note\n>> on where the leak needs to be plugged.  You put it together and\n>> converted it to struct archiver_args.  I'd very much have liked to\n>> see a comment stating this.  Or simply just say \"based on code by\n>> Rene\" or something.  The same is true for patch 3/4.\n>>...\n>> Especially I would not have signed off this invisible comment. ;)\n>\n> I take your response is a mild NAK.\n\nJust to reduce everybody's pain, why don't I fix them up and\npush out the 4 series in \"pu\" with attribution clarification and\nreview comments from Rene in mind, so that you two can Ack them?\nAfter that they will be placed on \"next\".\n\nI needed to apply small tweaks on 1/4 (ANSI-C pedantic did not\nlike empty struct initializers) and 4/4 (the updated 1/1 needed\nthe way struct archiver is initialized and used be different\nfrom the original one) as well.\n"},{"id":"26632","messageId":"cda58cb80609090731w7c66dcfbrbababb6c38d29bf6@mail.gmail.com","threadId":"5483","inReplyTo":"4501D0C5.702@lsrfire.ath.cx","subject":"Re: [PATCH 1/4] Add git-archive","fromName":"Franck Bui-Huu","fromEmail":"vagabon.xyz@gmail.com","sentAt":"2006-09-09T14:31:06Z","receivedAt":"2006-09-09T14:31:06Z","isPatch":true,"sender":{"key":"vagabon.xyz@gmail.com","avatar":null},"body":"2006/9/8, Rene Scharfe <rene.scharfe@lsrfire.ath.cx>:\n> Only a few trivial comments, as I managed to catch a cold somehow and\n> can't think straight for longer than three seconds.\n>\n> >  .gitignore                    |    1\n> >  Documentation/git-archive.txt |  100 ++++++++++++++++++\n> >  Makefile                      |    3 -\n\n[snip]\n\n> > +\n> > +     url = strdup(ar->remote);\n>\n> xstrdup()\n>\n\nok, but need to rebase...\n\n> > +     pid = git_connect(fd, url, buf);\n> > +     if (pid < 0)\n> > +             return pid;\n> > +\n\n[snip]\n\n> > +     int extra_argc = 0;\n> > +     const char *format = NULL; /* some default values */\n>\n> This comment does not convey any information.\n>\n\nOK, I'll remove it\n\n> > +     const char *remote = NULL;\n> > +     const char *base = \"\";\n\n[snip]\n\n> > +             }\n> > +             if (arg[0] == '-') {\n> > +                     extra_argv[extra_argc++] = arg;\n>\n> Overrun is not checked.\n>\n\nIndeed, I'll fix it.\n\n> > +                     continue;\n> > +             }\n> > +             break;\n> > +     }\n> > +     if (list) {\n> > +             if (!remote) {\n> > +                     for (i = 0; i < ARRAY_SIZE(archivers); i++)\n> > +                             printf(\"%s\\n\", archivers[i].name);\n> > +                     exit(0);\n> > +             }\n> > +             die(\"--list and --remote are mutually exclusive\");\n> > +     }\n>\n> Not sure if we really need a list option.  I guess it only really\n> makes sense if we have more than five formats.  I have no _strong_\n> feelings against it, though. *shrug*\n>\n\nwell it's almost free to add it, and no need any maintenance if we add\na new archiver backend, so I would say let it.\n\n> > +     if (argc - i < 1) {\n> > +             die(\"%s\", archive_usage);\n>\n> usage()\n>\n\nok\n\n-- \n               Franck\n"},{"id":"26633","messageId":"cda58cb80609090738p239c7359pfd0be092d93ed657@mail.gmail.com","threadId":"5483","inReplyTo":"4501D0CF.70306@lsrfire.ath.cx","subject":"Re: [PATCH 2/4] git-archive: wire up TAR format.","fromName":"Franck Bui-Huu","fromEmail":"vagabon.xyz@gmail.com","sentAt":"2006-09-09T14:38:13Z","receivedAt":"2006-09-09T14:38:13Z","isPatch":true,"sender":{"key":"vagabon.xyz@gmail.com","avatar":null},"body":"2006/9/8, Rene Scharfe <rene.scharfe@lsrfire.ath.cx>:\n> Franck Bui-Huu schrieb:\n> > From: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>\n> >\n> > Signed-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>\n> > Signed-off-by: Franck Bui-Huu <vagabon.xyz@gmail.com>\n>\n> I did not sign off this exact patch.  I wrote and submitted the\n> builtin-tar-tree.c part, with memory leak and all, then sent a note\n> on where the leak needs to be plugged.  You put it together and\n> converted it to struct archiver_args.  I'd very much have liked to\n> see a comment stating this.  Or simply just say \"based on code by\n> Rene\" or something.  The same is true for patch 3/4.\n>\n\nOK I'll change that....\n\n> > ---\n> >  extern void parse_pathspec_arg(const char **pathspec,\n> >                              struct archiver_args *args);\n> > +/*\n> > + *\n> > + */\n>\n> Especially I would not have signed off this invisible comment. ;)\n>\n> René\n>\n\nand put something usefull here.\n\nThanks\n-- \n               Franck\n"},{"id":"26637","messageId":"4502D781.6030708@lsrfire.ath.cx","threadId":"5483","inReplyTo":"7vk64derfd.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 2/4] git-archive: wire up TAR format.","fromName":"Rene Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2006-09-09T15:02:25Z","receivedAt":"2006-09-09T15:02:25Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Junio C Hamano schrieb:\n> Just to reduce everybody's pain, why don't I fix them up and\n> push out the 4 series in \"pu\" with attribution clarification and\n> review comments from Rene in mind, so that you two can Ack them?\n> After that they will be placed on \"next\".\n\nThis is an excellent idea.  What you have in pu is a good base to\nadd the (few and small) missing pieces.  Consider patches 1-3 ACKed;\nI haven't looked at git-upload-archive, yet.\n\nThanks,\nRené\n"},{"id":"26636","messageId":"4502D78B.6000905@lsrfire.ath.cx","threadId":"5483","inReplyTo":"cda58cb80609090731w7c66dcfbrbababb6c38d29bf6@mail.gmail.com","subject":"Re: [PATCH 1/4] Add git-archive","fromName":"Rene Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2006-09-09T15:02:35Z","receivedAt":"2006-09-09T15:02:35Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Franck Bui-Huu schrieb:\n> 2006/9/8, Rene Scharfe <rene.scharfe@lsrfire.ath.cx>:\n>> > +     url = strdup(ar->remote);\n>>\n>> xstrdup()\n>>\n> \n> ok, but need to rebase...\n\nWhy?  On a similar note , can we use Junio's pu branch (and soon his\nnext branch) as our base for further work?\n\n>> > +     if (list) {\n>> > +             if (!remote) {\n>> > +                     for (i = 0; i < ARRAY_SIZE(archivers); i++)\n>> > +                             printf(\"%s\\n\", archivers[i].name);\n>> > +                     exit(0);\n>> > +             }\n>> > +             die(\"--list and --remote are mutually exclusive\");\n>> > +     }\n>>\n>> Not sure if we really need a list option.  I guess it only really\n>> makes sense if we have more than five formats.  I have no _strong_\n>> feelings against it, though. *shrug*\n>>\n> \n> well it's almost free to add it, and no need any maintenance if we add\n> a new archiver backend, so I would say let it.\n\nI thought a bit about it, and I can now see a good use case for --list:\nchecking the capabilities of a remote site.  Unfortunately this is\ncurrently forbidden.  Why?  git-archive --list writes to stdout, so the\nresult can be transported the same way an archive would.\n\nRené\n"},{"id":"26639","messageId":"cda58cb80609090810t6fdab535r761636e65205a0f@mail.gmail.com","threadId":"5483","inReplyTo":"7vk64derfd.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 2/4] git-archive: wire up TAR format.","fromName":"Franck Bui-Huu","fromEmail":"vagabon.xyz@gmail.com","sentAt":"2006-09-09T15:10:23Z","receivedAt":"2006-09-09T15:10:23Z","isPatch":true,"sender":{"key":"vagabon.xyz@gmail.com","avatar":null},"body":"2006/9/9, Junio C Hamano <junkio@cox.net>:\n> Junio C Hamano <junkio@cox.net> writes:\n>\n> > Rene Scharfe <rene.scharfe@lsrfire.ath.cx> writes:\n> >\n> >> I did not sign off this exact patch.  I wrote and submitted the\n> >> builtin-tar-tree.c part, with memory leak and all, then sent a note\n> >> on where the leak needs to be plugged.  You put it together and\n> >> converted it to struct archiver_args.  I'd very much have liked to\n> >> see a comment stating this.  Or simply just say \"based on code by\n> >> Rene\" or something.  The same is true for patch 3/4.\n> >>...\n> >> Especially I would not have signed off this invisible comment. ;)\n> >\n> > I take your response is a mild NAK.\n>\n> Just to reduce everybody's pain, why don't I fix them up and\n> push out the 4 series in \"pu\" with attribution clarification and\n> review comments from Rene in mind, so that you two can Ack them?\n> After that they will be placed on \"next\".\n>\n\nAlmost Acked by me, except you've missed some Rene's comments. And\nmore important I fixed an \"uninitialized variable\" bug. See patch\nbelow.\n\n> I needed to apply small tweaks on 1/4 (ANSI-C pedantic did not\n> like empty struct initializers) and 4/4 (the updated 1/1 needed\n> the way struct archiver is initialized and used be different\n> from the original one) as well.\n>\n\nthanks for that.\n\n-- >8 --\n\ndiff --git a/builtin-archive.c b/builtin-archive.c\nindex 0a02519..9b90d87 100644\n--- a/builtin-archive.c\n+++ b/builtin-archive.c\n@@ -28,7 +28,7 @@ static int run_remote_archiver(struct ar\n\n \tsprintf(buf, \"git-upload-archive\");\n\n-\turl = strdup(ar->remote);\n+\turl = xstrdup(ar->remote);\n \tpid = git_connect(fd, url, buf);\n \tif (pid < 0)\n \t\treturn pid;\n@@ -101,6 +101,7 @@ void parse_treeish_arg(const char **argv\n \t\tcommit_sha1 = commit->object.sha1;\n \t\tarchive_time = commit->date;\n \t} else {\n+\t\tcommit_sha1 = NULL;\n \t\tarchive_time = time(NULL);\n \t}\n\n@@ -141,7 +142,7 @@ int parse_archive_args(int argc, const c\n {\n \tconst char *extra_argv[MAX_EXTRA_ARGS];\n \tint extra_argc = 0;\n-\tconst char *format = NULL; /* some default values */\n+\tconst char *format = NULL;\n \tconst char *remote = NULL;\n \tconst char *base = \"\";\n \tint list = 0;\n@@ -171,6 +172,8 @@ int parse_archive_args(int argc, const c\n \t\t\tbreak;\n \t\t}\n \t\tif (arg[0] == '-') {\n+\t\t\tif (extra_argc > MAX_EXTRA_ARGS - 1)\n+\t\t\t\tdie(\"Too many extra options\");\n \t\t\textra_argv[extra_argc++] = arg;\n \t\t\tcontinue;\n \t\t}\n@@ -185,7 +188,7 @@ int parse_archive_args(int argc, const c\n \t\tdie(\"--list and --remote are mutually exclusive\");\n \t}\n \tif (argc - i < 1) {\n-\t\tdie(\"%s\", archive_usage);\n+\t\tusage(archive_usage);\n \t}\n \tif (!format){\n \t\tdie(\"You must specify an archive format\");\n"},{"id":"26641","messageId":"cda58cb80609090825o32d9f1a8jee62153b96877bd0@mail.gmail.com","threadId":"5483","inReplyTo":"4502D78B.6000905@lsrfire.ath.cx","subject":"Re: [PATCH 1/4] Add git-archive","fromName":"Franck Bui-Huu","fromEmail":"vagabon.xyz@gmail.com","sentAt":"2006-09-09T15:25:20Z","receivedAt":"2006-09-09T15:25:20Z","isPatch":true,"sender":{"key":"vagabon.xyz@gmail.com","avatar":null},"body":"2006/9/9, Rene Scharfe <rene.scharfe@lsrfire.ath.cx>:\n> Franck Bui-Huu schrieb:\n> > 2006/9/8, Rene Scharfe <rene.scharfe@lsrfire.ath.cx>:\n> >> > +     url = strdup(ar->remote);\n> >>\n> >> xstrdup()\n> >>\n> >\n> > ok, but need to rebase...\n>\n> Why?  On a similar note , can we use Junio's pu branch (and soon his\n> next branch) as our base for further work?\n>\n\nYes. I just noticed Junio's post right after my reply.\n\n> >> > +     if (list) {\n> >> > +             if (!remote) {\n> >> > +                     for (i = 0; i < ARRAY_SIZE(archivers); i++)\n> >> > +                             printf(\"%s\\n\", archivers[i].name);\n> >> > +                     exit(0);\n> >> > +             }\n> >> > +             die(\"--list and --remote are mutually exclusive\");\n> >> > +     }\n> >>\n> >> Not sure if we really need a list option.  I guess it only really\n> >> makes sense if we have more than five formats.  I have no _strong_\n> >> feelings against it, though. *shrug*\n> >>\n> >\n> > well it's almost free to add it, and no need any maintenance if we add\n> > a new archiver backend, so I would say let it.\n>\n> I thought a bit about it, and I can now see a good use case for --list:\n> checking the capabilities of a remote site.  Unfortunately this is\n> currently forbidden.  Why?  git-archive --list writes to stdout, so the\n> result can be transported the same way an archive would.\n>\n\nYes that was the main goal for this option. But then we talked about\nenable/disable formats on the server side, and adding side band\nsupport...all that points are still dark for me and I don't know how\n'--list --remote' will interact with them. So I prefered to make this\noption simple for now and not make the user to believe that doing\n\ngit archive --list --remote=...\n\nlist the capabilites of the remote side.\n\n-- \n               Franck\n"},{"id":"26646","messageId":"7v8xksx1ww.fsf@assigned-by-dhcp.cox.net","threadId":"5483","inReplyTo":"cda58cb80609090810t6fdab535r761636e65205a0f@mail.gmail.com","subject":"Re: [PATCH 2/4] git-archive: wire up TAR format.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-09T19:42:07Z","receivedAt":"2006-09-09T19:42:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Franck Bui-Huu\" <vagabon.xyz@gmail.com> writes:\n\n> Almost Acked by me, except you've missed some Rene's comments. And\n> more important I fixed an \"uninitialized variable\" bug. See patch\n> below.\n\nGaah, I swear I fixed all of these at one time in my tree but\nsomehow forgot to apply the fix-up patch while cleaning it up.\n\nBig thanks for eyeballing.\n"},{"id":"26684","messageId":"450438D9.4000706@lsrfire.ath.cx","threadId":"5483","inReplyTo":"7v8xksx1ww.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH] Use xstrdup instead of strdup in builtin-{tar,zip}-tree.c","fromName":"Rene Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2006-09-10T16:10:01Z","receivedAt":"2006-09-10T16:10:01Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Signed-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>\n---\nThis patch applies to the current 'next' branch.\n\ndiff --git a/builtin-tar-tree.c b/builtin-tar-tree.c\nindex c20eb0e..e8e492f 100644\n--- a/builtin-tar-tree.c\n+++ b/builtin-tar-tree.c\n@@ -390,7 +390,7 @@ int write_tar_archive(struct archiver_ar\n \t\twrite_global_extended_header(args->commit_sha1);\n \n \tif (args->base && plen > 0 && args->base[plen - 1] == '/') {\n-\t\tchar *base = strdup(args->base);\n+\t\tchar *base = xstrdup(args->base);\n \t\tint baselen = strlen(base);\n \n \t\twhile (baselen > 0 && base[baselen - 1] == '/')\ndiff --git a/builtin-zip-tree.c b/builtin-zip-tree.c\nindex 4e79633..fdac2bd 100644\n--- a/builtin-zip-tree.c\n+++ b/builtin-zip-tree.c\n@@ -363,7 +363,7 @@ int write_zip_archive(struct archiver_ar\n \tzip_dir_size = ZIP_DIRECTORY_MIN_SIZE;\n \n \tif (args->base && plen > 0 && args->base[plen - 1] == '/') {\n-\t\tchar *base = strdup(args->base);\n+\t\tchar *base = xstrdup(args->base);\n \t\tint baselen = strlen(base);\n \n \t\twhile (baselen > 0 && base[baselen - 1] == '/')\n"}]}