#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 git_default_config(var, value);
}
-static int generate_tar(int argc, const char **argv, char** envp)
+static int generate_tar(int argc, const char **argv, const char *prefix)
{
unsigned char sha1[20], tree_sha1[20];
struct commit *commit;
struct tree_desc tree;
struct strbuf current_path;
+ void *buffer;
current_path.buf = xmalloc(PATH_MAX);
current_path.alloc = PATH_MAX;
current_path.len = current_path.eof = 0;
- setup_git_directory();
git_config(git_tar_config);
switch (argc) {
} else
archive_time = time(NULL);
- tree.buf = read_object_with_reference(sha1, tree_type, &tree.size,
- tree_sha1);
+ tree.buf = buffer = read_object_with_reference(sha1, tree_type,
+ &tree.size, tree_sha1);
if (!tree.buf)
die("not a reference to a tag, commit or tree object: %s",
sha1_to_hex(sha1));
write_entry(tree_sha1, ¤t_path, 040777, NULL, 0);
traverse_tree(&tree, ¤t_path);
write_trailer();
+ free(buffer);
free(current_path.buf);
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;
return !!ret;
}
-int cmd_tar_tree(int argc, const char **argv, char **envp)
+int cmd_tar_tree(int argc, const char **argv, const char *prefix)
{
if (argc < 2)
usage(tar_tree_usage);
if (!strncmp("--remote=", argv[1], 9))
return remote_tar(argc, argv);
- return generate_tar(argc, argv, envp);
+ return generate_tar(argc, argv, prefix);
}
/* ustar header + extended global header content */
#define HEADERSIZE (2 * RECORDSIZE)
-int cmd_get_tar_commit_id(int argc, const char **argv, char **envp)
+int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
{
char buffer[HEADERSIZE];
struct ustar_header *header = (struct ustar_header *)buffer;