diff --git a/archive.c b/archive.c
index 0bca9ca..368e7e8 100644
--- a/archive.c
+++ b/archive.c
@@ -25,6 +25,12 @@ static const struct archiver {
{ "zip", write_zip_archive, USES_ZLIB_COMPRESSION },
};
+struct extra_file {
+ char *path;
+ int fd;
+ struct extra_file *next;
+};
+
static void format_subst(const struct commit *commit,
const char *src, size_t len,
struct strbuf *buf)@@ -147,12 +153,29 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,
return err;
}
+static int write_extra_file(struct extra_file *extra_file,
+ struct archiver_args *args,
+ write_archive_entry_fn_t write_entry)
+{
+ struct strbuf buf = STRBUF_INIT;
+ int err;
+
+ if (strbuf_read(&buf, extra_file->fd, 0) < 0)
+ return error("cannot read extra file: %s", extra_file->path);
+ close(extra_file->fd);
+ err = write_entry(args, null_sha1, extra_file->path,
+ strlen(extra_file->path), 0100644, buf.buf, buf.len);
+ strbuf_release(&buf);
+ return err;
+}
+
int write_archive_entries(struct archiver_args *args,
write_archive_entry_fn_t write_entry)
{
struct archiver_context context;
struct unpack_trees_options opts;
struct tree_desc t;
+ struct extra_file *extra_file = args->extra_files;
int err;
if (args->baselen > 0 && args->base[args->baselen - 1] == '/') {@@ -191,6 +214,12 @@ int write_archive_entries(struct archiver_args *args,
args->pathspec, write_archive_entry, &context);
if (err == READ_TREE_RECURSIVE)
err = 0;
+
+ while (!err && extra_file) {
+ err = write_extra_file(extra_file, args, write_entry);
+ extra_file = extra_file->next;
+ }
+
return err;
}
@@ -265,11 +294,41 @@ static void parse_treeish_arg(const char **argv,
{ OPTION_SET_INT, (s), NULL, (v), NULL, "", \
PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_HIDDEN, NULL, (p) }
+static int extra_files_cb(const struct option *opt, const char *arg, int unset)
+{
+ struct archiver_args *args = opt->value;
+ struct extra_file *e = xmalloc(sizeof(struct extra_file));
+ struct strbuf path = STRBUF_INIT;
+ const char *last_slash, *prefix = args->base;
+
+ e->fd = open(arg, O_RDONLY);
+ if (e->fd == -1)
+ die_errno("Could not open file '%s'", arg);
+
+ if (prefix)
+ strbuf_addstr(&path, prefix);
+ last_slash = strrchr(arg, '/');
+ strbuf_addstr(&path, last_slash ? last_slash + 1 : arg);
+ e->path = strbuf_detach(&path, NULL);
+
+ e->next = NULL;
+
+ if (args->extra_files) {
+ struct extra_file *prev = args->extra_files;
+ while (prev->next)
+ prev = prev->next;
+ prev->next = e;
+ } else {
+ args->extra_files = e;
+ }
+
+ return 0;
+}
+
static int parse_archive_args(int argc, const char **argv,
const struct archiver **ar, struct archiver_args *args)
{
const char *format = "tar";
- const char *base = NULL;
const char *remote = NULL;
const char *exec = NULL;
const char *output = NULL;@@ -281,10 +340,13 @@ static int parse_archive_args(int argc, const char **argv,
struct option opts[] = {
OPT_GROUP(""),
OPT_STRING(0, "format", &format, "fmt", "archive format"),
- OPT_STRING(0, "prefix", &base, "prefix",
+ OPT_STRING(0, "prefix", &args->base, "prefix",
"prepend prefix to each pathname in the archive"),
OPT_STRING(0, "output", &output, "file",
"write the archive to this file"),
+ { OPTION_CALLBACK, 0, "add-file", args, "file",
+ "add file to the archive, using prefix as path",
+ PARSE_OPT_NONEG, extra_files_cb },
OPT_BOOLEAN(0, "worktree-attributes", &worktree_attributes,
"read .gitattributes in working directory"),
OPT__VERBOSE(&verbose),@@ -318,8 +380,8 @@ static int parse_archive_args(int argc, const char **argv,
if (output)
die("Unexpected option --output");
- if (!base)
- base = "";
+ if (!args->base)
+ args->base = "";
if (list) {
for (i = 0; i < ARRAY_SIZE(archivers); i++)@@ -344,8 +406,7 @@ static int parse_archive_args(int argc, const char **argv,
}
}
args->verbose = verbose;
- args->base = base;
- args->baselen = strlen(base);
+ args->baselen = strlen(args->base);
args->worktree_attributes = worktree_attributes;
return argc;
@@ -357,6 +418,7 @@ int write_archive(int argc, const char **argv, const char *prefix,
const struct archiver *ar = NULL;
struct archiver_args args;
+ memset(&args, 0, sizeof(struct archiver_args));
argc = parse_archive_args(argc, argv, &ar, &args);
if (setup_prefix && prefix == NULL)
prefix = setup_git_directory();
diff --git a/archive.h b/archive.h
index 038ac35..a88bdbb 100644
--- a/archive.h
+++ b/archive.h
@@ -1,6 +1,8 @@
#ifndef ARCHIVE_H
#define ARCHIVE_H
+struct extra_file;
+
struct archiver_args {
const char *base;
size_t baselen;@@ -11,6 +13,7 @@ struct archiver_args {
const char **pathspec;
unsigned int verbose : 1;
unsigned int worktree_attributes : 1;
+ struct extra_file *extra_files;
int compression_level;
};