diff options
Diffstat (limited to 'builtin-archive.c')
| -rw-r--r-- | builtin-archive.c | 263 | 
1 files changed, 263 insertions, 0 deletions
| diff --git a/builtin-archive.c b/builtin-archive.c new file mode 100644 index 0000000000..8ea6cb1efc --- /dev/null +++ b/builtin-archive.c @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2006 Franck Bui-Huu + * Copyright (c) 2006 Rene Scharfe + */ +#include "cache.h" +#include "builtin.h" +#include "archive.h" +#include "commit.h" +#include "tree-walk.h" +#include "exec_cmd.h" +#include "pkt-line.h" +#include "sideband.h" + +static const char archive_usage[] = \ +"git-archive --format=<fmt> [--prefix=<prefix>/] [--verbose] [<extra>] <tree-ish> [path...]"; + +static struct archiver_desc +{ +	const char *name; +	write_archive_fn_t write_archive; +	parse_extra_args_fn_t parse_extra; +} archivers[] = { +	{ "tar", write_tar_archive, NULL }, +	{ "zip", write_zip_archive, parse_extra_zip_args }, +}; + +static int run_remote_archiver(const char *remote, int argc, +			       const char **argv) +{ +	char *url, buf[LARGE_PACKET_MAX]; +	int fd[2], i, len, rv; +	pid_t pid; +	const char *exec = "git-upload-archive"; +	int exec_at = 0; + +	for (i = 1; i < argc; i++) { +		const char *arg = argv[i]; +		if (!prefixcmp(arg, "--exec=")) { +			if (exec_at) +				die("multiple --exec specified"); +			exec = arg + 7; +			exec_at = i; +			break; +		} +	} + +	url = xstrdup(remote); +	pid = git_connect(fd, url, exec); +	if (pid < 0) +		return pid; + +	for (i = 1; i < argc; i++) { +		if (i == exec_at) +			continue; +		packet_write(fd[1], "argument %s\n", argv[i]); +	} +	packet_flush(fd[1]); + +	len = packet_read_line(fd[0], buf, sizeof(buf)); +	if (!len) +		die("git-archive: expected ACK/NAK, got EOF"); +	if (buf[len-1] == '\n') +		buf[--len] = 0; +	if (strcmp(buf, "ACK")) { +		if (len > 5 && !prefixcmp(buf, "NACK ")) +			die("git-archive: NACK %s", buf + 5); +		die("git-archive: protocol error"); +	} + +	len = packet_read_line(fd[0], buf, sizeof(buf)); +	if (len) +		die("git-archive: expected a flush"); + +	/* Now, start reading from fd[0] and spit it out to stdout */ +	rv = recv_sideband("archive", fd[0], 1, 2); +	close(fd[0]); +	close(fd[1]); +	rv |= finish_connect(pid); + +	return !!rv; +} + +static int init_archiver(const char *name, struct archiver *ar) +{ +	int rv = -1, i; + +	for (i = 0; i < ARRAY_SIZE(archivers); i++) { +		if (!strcmp(name, archivers[i].name)) { +			memset(ar, 0, sizeof(*ar)); +			ar->name = archivers[i].name; +			ar->write_archive = archivers[i].write_archive; +			ar->parse_extra = archivers[i].parse_extra; +			rv = 0; +			break; +		} +	} +	return rv; +} + +void parse_pathspec_arg(const char **pathspec, struct archiver_args *ar_args) +{ +	ar_args->pathspec = get_pathspec(ar_args->base, pathspec); +} + +void parse_treeish_arg(const char **argv, struct archiver_args *ar_args, +		       const char *prefix) +{ +	const char *name = argv[0]; +	const unsigned char *commit_sha1; +	time_t archive_time; +	struct tree *tree; +	struct commit *commit; +	unsigned char sha1[20]; + +	if (get_sha1(name, sha1)) +		die("Not a valid object name"); + +	commit = lookup_commit_reference_gently(sha1, 1); +	if (commit) { +		commit_sha1 = commit->object.sha1; +		archive_time = commit->date; +	} else { +		commit_sha1 = NULL; +		archive_time = time(NULL); +	} + +	tree = parse_tree_indirect(sha1); +	if (tree == NULL) +		die("not a tree object"); + +	if (prefix) { +		unsigned char tree_sha1[20]; +		unsigned int mode; +		int err; + +		err = get_tree_entry(tree->object.sha1, prefix, +				     tree_sha1, &mode); +		if (err || !S_ISDIR(mode)) +			die("current working directory is untracked"); + +		tree = parse_tree_indirect(tree_sha1); +	} +	ar_args->tree = tree; +	ar_args->commit_sha1 = commit_sha1; +	ar_args->time = archive_time; +} + +int parse_archive_args(int argc, const char **argv, struct archiver *ar) +{ +	const char *extra_argv[MAX_EXTRA_ARGS]; +	int extra_argc = 0; +	const char *format = NULL; /* might want to default to "tar" */ +	const char *base = ""; +	int verbose = 0; +	int i; + +	for (i = 1; i < argc; i++) { +		const char *arg = argv[i]; + +		if (!strcmp(arg, "--list") || !strcmp(arg, "-l")) { +			for (i = 0; i < ARRAY_SIZE(archivers); i++) +				printf("%s\n", archivers[i].name); +			exit(0); +		} +		if (!strcmp(arg, "--verbose") || !strcmp(arg, "-v")) { +			verbose = 1; +			continue; +		} +		if (!prefixcmp(arg, "--format=")) { +			format = arg + 9; +			continue; +		} +		if (!prefixcmp(arg, "--prefix=")) { +			base = arg + 9; +			continue; +		} +		if (!strcmp(arg, "--")) { +			i++; +			break; +		} +		if (arg[0] == '-') { +			if (extra_argc > MAX_EXTRA_ARGS - 1) +				die("Too many extra options"); +			extra_argv[extra_argc++] = arg; +			continue; +		} +		break; +	} + +	/* We need at least one parameter -- tree-ish */ +	if (argc - 1 < i) +		usage(archive_usage); +	if (!format) +		die("You must specify an archive format"); +	if (init_archiver(format, ar) < 0) +		die("Unknown archive format '%s'", format); + +	if (extra_argc) { +		if (!ar->parse_extra) +			die("'%s' format does not handle %s", +			    ar->name, extra_argv[0]); +		ar->args.extra = ar->parse_extra(extra_argc, extra_argv); +	} +	ar->args.verbose = verbose; +	ar->args.base = base; + +	return i; +} + +static const char *extract_remote_arg(int *ac, const char **av) +{ +	int ix, iy, cnt = *ac; +	int no_more_options = 0; +	const char *remote = NULL; + +	for (ix = iy = 1; ix < cnt; ix++) { +		const char *arg = av[ix]; +		if (!strcmp(arg, "--")) +			no_more_options = 1; +		if (!no_more_options) { +			if (!prefixcmp(arg, "--remote=")) { +				if (remote) +					die("Multiple --remote specified"); +				remote = arg + 9; +				continue; +			} +			if (arg[0] != '-') +				no_more_options = 1; +		} +		if (ix != iy) +			av[iy] = arg; +		iy++; +	} +	if (remote) { +		av[--cnt] = NULL; +		*ac = cnt; +	} +	return remote; +} + +int cmd_archive(int argc, const char **argv, const char *prefix) +{ +	struct archiver ar; +	int tree_idx; +	const char *remote = NULL; + +	remote = extract_remote_arg(&argc, argv); +	if (remote) +		return run_remote_archiver(remote, argc, argv); + +	setvbuf(stderr, NULL, _IOLBF, BUFSIZ); + +	memset(&ar, 0, sizeof(ar)); +	tree_idx = parse_archive_args(argc, argv, &ar); +	if (prefix == NULL) +		prefix = setup_git_directory(); + +	argv += tree_idx; +	parse_treeish_arg(argv, &ar.args, prefix); +	parse_pathspec_arg(argv + 1, &ar.args); + +	return ar.write_archive(&ar.args); +} | 
