archive: optionally add "virtual" files
With the `--add-virtual-file=<path>:<content>` option, `git archive` now supports use cases where relatively trivial files need to be added that do not exist on disk. This will allow us to generate `.zip` files with generated content, without having to add said content to the object database and without having to write it out to disk. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> [jc: tweaked <path> handling] Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
23f2356fd9
commit
237a1d138c
@ -51,7 +51,7 @@ OPTIONS
|
||||
--prefix=<prefix>/::
|
||||
Prepend <prefix>/ to paths in the archive. Can be repeated; its
|
||||
rightmost value is used for all tracked files. See below which
|
||||
value gets used by `--add-file`.
|
||||
value gets used by `--add-file` and `--add-virtual-file`.
|
||||
|
||||
-o <file>::
|
||||
--output=<file>::
|
||||
@ -63,6 +63,17 @@ OPTIONS
|
||||
concatenating the value of the last `--prefix` option (if any)
|
||||
before this `--add-file` and the basename of <file>.
|
||||
|
||||
--add-virtual-file=<path>:<content>::
|
||||
Add the specified contents to the archive. Can be repeated to add
|
||||
multiple files. The path of the file in the archive is built
|
||||
by concatenating the value of the last `--prefix` option (if any)
|
||||
before this `--add-virtual-file` and `<path>`.
|
||||
+
|
||||
The `<path>` cannot contain any colon, the file mode is limited to
|
||||
a regular file, and the option may be subject to platform-dependent
|
||||
command-line limits. For non-trivial cases, write an untracked file
|
||||
and use `--add-file` instead.
|
||||
|
||||
--worktree-attributes::
|
||||
Look for attributes in .gitattributes files in the working tree
|
||||
as well (see <<ATTRIBUTES>>).
|
||||
|
75
archive.c
75
archive.c
@ -263,6 +263,7 @@ static int queue_or_write_archive_entry(const struct object_id *oid,
|
||||
struct extra_file_info {
|
||||
char *base;
|
||||
struct stat stat;
|
||||
void *content;
|
||||
};
|
||||
|
||||
int write_archive_entries(struct archiver_args *args,
|
||||
@ -331,19 +332,27 @@ int write_archive_entries(struct archiver_args *args,
|
||||
|
||||
put_be64(fake_oid.hash, i + 1);
|
||||
|
||||
strbuf_reset(&path_in_archive);
|
||||
if (info->base)
|
||||
strbuf_addstr(&path_in_archive, info->base);
|
||||
strbuf_addstr(&path_in_archive, basename(path));
|
||||
if (!info->content) {
|
||||
strbuf_reset(&path_in_archive);
|
||||
if (info->base)
|
||||
strbuf_addstr(&path_in_archive, info->base);
|
||||
strbuf_addstr(&path_in_archive, basename(path));
|
||||
|
||||
strbuf_reset(&content);
|
||||
if (strbuf_read_file(&content, path, info->stat.st_size) < 0)
|
||||
err = error_errno(_("cannot read '%s'"), path);
|
||||
else
|
||||
err = write_entry(args, &fake_oid, path_in_archive.buf,
|
||||
path_in_archive.len,
|
||||
strbuf_reset(&content);
|
||||
if (strbuf_read_file(&content, path, info->stat.st_size) < 0)
|
||||
err = error_errno(_("cannot read '%s'"), path);
|
||||
else
|
||||
err = write_entry(args, &fake_oid, path_in_archive.buf,
|
||||
path_in_archive.len,
|
||||
canon_mode(info->stat.st_mode),
|
||||
content.buf, content.len);
|
||||
} else {
|
||||
err = write_entry(args, &fake_oid,
|
||||
path, strlen(path),
|
||||
canon_mode(info->stat.st_mode),
|
||||
content.buf, content.len);
|
||||
info->content, info->stat.st_size);
|
||||
}
|
||||
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
@ -493,6 +502,7 @@ static void extra_file_info_clear(void *util, const char *str)
|
||||
{
|
||||
struct extra_file_info *info = util;
|
||||
free(info->base);
|
||||
free(info->content);
|
||||
free(info);
|
||||
}
|
||||
|
||||
@ -514,14 +524,40 @@ static int add_file_cb(const struct option *opt, const char *arg, int unset)
|
||||
if (!arg)
|
||||
return -1;
|
||||
|
||||
path = prefix_filename(args->prefix, arg);
|
||||
item = string_list_append_nodup(&args->extra_files, path);
|
||||
item->util = info = xmalloc(sizeof(*info));
|
||||
info = xmalloc(sizeof(*info));
|
||||
info->base = xstrdup_or_null(base);
|
||||
if (stat(path, &info->stat))
|
||||
die(_("File not found: %s"), path);
|
||||
if (!S_ISREG(info->stat.st_mode))
|
||||
die(_("Not a regular file: %s"), path);
|
||||
|
||||
if (!strcmp(opt->long_name, "add-file")) {
|
||||
path = prefix_filename(args->prefix, arg);
|
||||
if (stat(path, &info->stat))
|
||||
die(_("File not found: %s"), path);
|
||||
if (!S_ISREG(info->stat.st_mode))
|
||||
die(_("Not a regular file: %s"), path);
|
||||
info->content = NULL; /* read the file later */
|
||||
} else if (!strcmp(opt->long_name, "add-virtual-file")) {
|
||||
const char *colon = strchr(arg, ':');
|
||||
char *p;
|
||||
|
||||
if (!colon)
|
||||
die(_("missing colon: '%s'"), arg);
|
||||
|
||||
p = xstrndup(arg, colon - arg);
|
||||
if (!args->prefix)
|
||||
path = p;
|
||||
else {
|
||||
path = prefix_filename(args->prefix, p);
|
||||
free(p);
|
||||
}
|
||||
memset(&info->stat, 0, sizeof(info->stat));
|
||||
info->stat.st_mode = S_IFREG | 0644;
|
||||
info->content = xstrdup(colon + 1);
|
||||
info->stat.st_size = strlen(info->content);
|
||||
} else {
|
||||
BUG("add_file_cb() called for %s", opt->long_name);
|
||||
}
|
||||
item = string_list_append_nodup(&args->extra_files, path);
|
||||
item->util = info;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -554,6 +590,9 @@ static int parse_archive_args(int argc, const char **argv,
|
||||
{ OPTION_CALLBACK, 0, "add-file", args, N_("file"),
|
||||
N_("add untracked file to archive"), 0, add_file_cb,
|
||||
(intptr_t)&base },
|
||||
{ OPTION_CALLBACK, 0, "add-virtual-file", args,
|
||||
N_("path:content"), N_("add untracked file to archive"), 0,
|
||||
add_file_cb, (intptr_t)&base },
|
||||
OPT_STRING('o', "output", &output, N_("file"),
|
||||
N_("write the archive to this file")),
|
||||
OPT_BOOL(0, "worktree-attributes", &worktree_attributes,
|
||||
|
@ -206,6 +206,18 @@ test_expect_success 'git archive --format=zip --add-file' '
|
||||
check_zip with_untracked
|
||||
check_added with_untracked untracked untracked
|
||||
|
||||
test_expect_success UNZIP 'git archive --format=zip --add-virtual-file' '
|
||||
git archive --format=zip >with_file_with_content.zip \
|
||||
--add-virtual-file=hello:world $EMPTY_TREE &&
|
||||
test_when_finished "rm -rf tmp-unpack" &&
|
||||
mkdir tmp-unpack && (
|
||||
cd tmp-unpack &&
|
||||
"$GIT_UNZIP" ../with_file_with_content.zip &&
|
||||
test_path_is_file hello &&
|
||||
test world = $(cat hello)
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'git archive --format=zip --add-file twice' '
|
||||
echo untracked >untracked &&
|
||||
git archive --format=zip --prefix=one/ --add-file=untracked \
|
||||
|
Loading…
Reference in New Issue
Block a user