#include "tar.h"
#include "builtin.h"
#include "pkt-line.h"
+#include "archive.h"
#define RECORDSIZE (512)
#define BLOCKSIZE (RECORDSIZE * 20)
static const char tar_tree_usage[] =
-"git-tar-tree [--remote=<repo>] <ent> [basedir]";
+"git-tar-tree [--remote=<repo>] <tree-ish> [basedir]";
static char block[BLOCKSIZE];
static unsigned long offset;
static time_t archive_time;
static int tar_umask;
-
-/* tries hard to write, either succeeds or dies in the attempt */
-static void reliable_write(const void *data, unsigned long size)
-{
- const char *buf = data;
-
- while (size > 0) {
- long ret = xwrite(1, buf, size);
- if (ret < 0) {
- if (errno == EPIPE)
- exit(0);
- die("git-tar-tree: %s", strerror(errno));
- } else if (!ret) {
- die("git-tar-tree: disk full?");
- }
- size -= ret;
- buf += ret;
- }
-}
+static int verbose;
/* writes out the whole block, but only if it is full */
static void write_if_needed(void)
{
if (offset == BLOCKSIZE) {
- reliable_write(block, BLOCKSIZE);
+ write_or_die(1, block, BLOCKSIZE);
offset = 0;
}
}
write_if_needed();
}
while (size >= BLOCKSIZE) {
- reliable_write(buf, BLOCKSIZE);
+ write_or_die(1, buf, BLOCKSIZE);
size -= BLOCKSIZE;
buf += BLOCKSIZE;
}
{
int tail = BLOCKSIZE - offset;
memset(block + offset, 0, tail);
- reliable_write(block, BLOCKSIZE);
+ write_or_die(1, block, BLOCKSIZE);
if (tail < 2 * RECORDSIZE) {
memset(block, 0, offset);
- reliable_write(block, BLOCKSIZE);
+ write_or_die(1, block, BLOCKSIZE);
}
}
mode = 0100666;
sprintf(header.name, "%s.paxheader", sha1_to_hex(sha1));
} else {
+ if (verbose)
+ fprintf(stderr, "%.*s\n", path->len, path->buf);
if (S_ISDIR(mode)) {
*header.typeflag = TYPEFLAG_DIR;
mode = (mode | 0777) & ~tar_umask;
}
}
-int git_tar_config(const char *var, const char *value)
+static int git_tar_config(const char *var, const char *value)
{
if (!strcmp(var, "tar.umask")) {
if (!strcmp(value, "user")) {
return 0;
}
+static int write_tar_entry(const unsigned char *sha1,
+ const char *base, int baselen,
+ const char *filename, unsigned mode, int stage)
+{
+ static struct strbuf path;
+ int filenamelen = strlen(filename);
+ void *buffer;
+ char type[20];
+ unsigned long size;
+
+ if (!path.alloc) {
+ path.buf = xmalloc(PATH_MAX);
+ path.alloc = PATH_MAX;
+ path.len = path.eof = 0;
+ }
+ if (path.alloc < baselen + filenamelen) {
+ free(path.buf);
+ path.buf = xmalloc(baselen + filenamelen);
+ path.alloc = baselen + filenamelen;
+ }
+ memcpy(path.buf, base, baselen);
+ memcpy(path.buf + baselen, filename, filenamelen);
+ path.len = baselen + filenamelen;
+ if (S_ISDIR(mode)) {
+ strbuf_append_string(&path, "/");
+ buffer = NULL;
+ size = 0;
+ } else {
+ buffer = read_sha1_file(sha1, type, &size);
+ if (!buffer)
+ die("cannot read %s", sha1_to_hex(sha1));
+ }
+
+ write_entry(sha1, &path, mode, buffer, size);
+ free(buffer);
+
+ return READ_TREE_RECURSIVE;
+}
+
+int write_tar_archive(struct archiver_args *args)
+{
+ int plen = strlen(args->base);
+
+ git_config(git_tar_config);
+
+ archive_time = args->time;
+ verbose = args->verbose;
+
+ if (args->commit_sha1)
+ write_global_extended_header(args->commit_sha1);
+
+ if (args->base && plen > 0 && args->base[plen - 1] == '/') {
+ char *base = xstrdup(args->base);
+ int baselen = strlen(base);
+
+ while (baselen > 0 && base[baselen - 1] == '/')
+ base[--baselen] = '\0';
+ write_tar_entry(args->tree->object.sha1, "", 0, base, 040777, 0);
+ free(base);
+ }
+ read_tree_recursive(args->tree, args->base, plen, 0,
+ args->pathspec, write_tar_entry);
+ write_trailer();
+
+ return 0;
+}
+
static const char *exec = "git-upload-tar";
static int remote_tar(int argc, const char **argv)
usage(tar_tree_usage);
/* --remote=<repo> */
- url = strdup(argv[1]+9);
+ url = xstrdup(argv[1]+9);
pid = git_connect(fd, url, exec);
if (pid < 0)
return 1;