Documentation/git-archive-tree.txt | 9 ++--- archive.h | 2 + builtin-archive-tree.c | 4 +- builtin-tar-tree.c | 63 ++++++++++++++++++++++++++++++++----- 4 files changed, 65 insertions(+), 13 deletions(-) diff --git a/Documentation/git-archive-tree.txt b/Documentation/git-archive-tree.txt index 122c482..25136d9 100644 --- a/Documentation/git-archive-tree.txt +++ b/Documentation/git-archive-tree.txt @@ -8,7 +8,7 @@ git-archive-tree - Creates a archive of the f SYNOPSIS -------- -'git-archive-tree' -f {tar|zip} [--prefix=<prefix>/] [-0|...|-9] +'git-archive-tree' -f {tar|tgz|zip} [--prefix=<prefix>/] [-0|...|-9] <tree-ish> [path...] DESCRIPTION @@ -29,7 +29,8 @@ OPTIONS ------- -f:: - Format of the resulting archive, can be either 'tar' or 'zip'. + Format of the resulting archive, can be either 'tar', 'tgz' + or 'zip'. <tree-ish>:: The tree or commit to produce an archive for. @@ -71,11 +72,11 @@ git archive -f tar --prefix=junk/ HEAD | latest commit on the current branch, and extracts it in `/var/tmp/junk` directory. -git archive -f tar --prefix=git-1.4.0/ v1.4.0 | gzip >git-1.4.0.tar.gz:: +git archive -f tgz --prefix=git-1.4.0/ v1.4.0 >git-1.4.0.tar.gz:: Create a compressed tarball for v1.4.0 release. -git archive -f tar --prefix=git-1.4.0/ v1.4.0{caret}\{tree\} | gzip >git-1.4.0.tar.gz:: +git archive -f tgz --prefix=git-1.4.0/ v1.4.0{caret}\{tree\} >git-1.4.0.tar.gz:: Create a compressed tarball for v1.4.0 release, but without a global extended pax header. diff --git a/archive.h b/archive.h index 7813962..6a03864 100644 --- a/archive.h +++ b/archive.h @@ -3,4 +3,6 @@ #include "tree.h" typedef int (*write_archive_fn_t)(struct tree *tree, const unsigned char *commit_sha1, const char *prefix, time_t time, const char **pathspec); int write_tar_archive(struct tree *tree, const unsigned char *commit_sha1, const char *prefix, time_t time, const char **pathspec); +int write_tgz_archive(struct tree *tree, const unsigned char *commit_sha1, + const char *prefix, time_t time, const char **pathspec); int write_zip_archive(struct tree *tree, const unsigned char *commit_sha1, const char *prefix, time_t time, const char **pathspec); diff --git a/builtin-archive-tree.c b/builtin-archive-tree.c index 2c6ee60..f61e26d 100644 --- a/builtin-archive-tree.c +++ b/builtin-archive-tree.c @@ -9,12 +9,14 @@ #include "tree-walk.h" #include "archive.h" static const char archive_usage[] = -"git-archive-tree -f {tar|zip} [--prefix=<prefix>/] [-0|...|-9] <tree-ish> [path...]"; +"git-archive-tree -f {tar|tgz|zip} [--prefix=<prefix>/] [-0|...|-9] <tree-ish> [path...]"; static write_archive_fn_t parse_archive_format(const char *format) { if (!strcmp(format, "tar")) return write_tar_archive; + if (!strcmp(format, "tgz")) + return write_tgz_archive; if (!strcmp(format, "zip")) return write_zip_archive; return NULL; diff --git a/builtin-tar-tree.c b/builtin-tar-tree.c index e0da01e..743d53a 100644 --- a/builtin-tar-tree.c +++ b/builtin-tar-tree.c @@ -23,6 +23,10 @@ static unsigned long offset; static time_t archive_time; static int tar_umask; +static gzFile gzstdout; + +typedef void (*blocked_write_fn)(const void *data, unsigned long size); + /* writes out the whole block, but only if it is full */ static void write_if_needed(void) { @@ -36,7 +40,7 @@ static void write_if_needed(void) * queues up writes, so that all our write(2) calls write exactly one * full block; pads writes to RECORDSIZE */ -static void write_blocked(const void *data, unsigned long size) +static void do_write_blocked(const void *data, unsigned long size) { const char *buf = data; unsigned long tail; @@ -68,19 +72,20 @@ static void write_blocked(const void *da write_if_needed(); } +static blocked_write_fn write_blocked = do_write_blocked; + /* * The end of tar archives is marked by 2*512 nul bytes and after that * follows the rest of the block (if any). */ static void write_trailer(void) { - int tail = BLOCKSIZE - offset; - memset(block + offset, 0, tail); - write_or_die(1, block, BLOCKSIZE); - if (tail < 2 * RECORDSIZE) { - memset(block, 0, offset); - write_or_die(1, block, BLOCKSIZE); - } + char zeroes[RECORDSIZE]; + memset(zeroes, 0, RECORDSIZE); + write_blocked(zeroes, RECORDSIZE); + write_blocked(zeroes, RECORDSIZE); + while (offset) + write_blocked(zeroes, RECORDSIZE); } static void strbuf_append_string(struct strbuf *sb, const char *s) @@ -404,6 +409,48 @@ int write_tar_archive(struct tree *tree, return 0; } +static void write_blocked_gzip(const void *data, unsigned long size) +{ + const char *p = data; + int written; + unsigned long tail = size % RECORDSIZE; + + while (size > 0) { + written = gzwrite(gzstdout, p, size); + if (written == 0) { + if (errno == EPIPE) + exit(0); + die("gzwrite error (%s)", strerror(errno)); + } + size -= written; + p += written; + } + + if (tail) { + z_off_t result = gzseek(gzstdout, RECORDSIZE - tail, SEEK_CUR); + if (result == -1) + die("gzseek error (%s)", strerror(errno)); + } +} + +int write_tgz_archive(struct tree *tree, const unsigned char *commit_sha1, + const char *prefix, time_t time, const char **pathspec) +{ + int result; + + gzstdout = gzdopen(1, "wb"); + if (!gzstdout) + die("zlib is unable to open stdout"); + + write_blocked = write_blocked_gzip; + result = write_tar_archive(tree, commit_sha1, prefix, time, pathspec); + + if (gzclose(gzstdout) != Z_OK) + result = 1; + + return result; +} + static const char *exec = "git-upload-tar"; static int remote_tar(int argc, const char **argv) -- VGER BF report: U 0.497484 - To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html