1
0
mirror of https://github.com/git/git.git synced 2024-09-28 20:51:42 +02:00

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:
Johannes Schindelin 2022-05-28 16:11:12 -07:00 committed by Junio C Hamano
parent 23f2356fd9
commit 237a1d138c
3 changed files with 81 additions and 19 deletions

@ -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>>).

@ -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 \