Skip to content

Commit

Permalink
archive: optionally add "virtual" files
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
dscho authored and gitster committed May 31, 2022
1 parent 23f2356 commit 237a1d1
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 20 deletions.
13 changes: 12 additions & 1 deletion Documentation/git-archive.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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>::
Expand All @@ -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>>).
Expand Down
77 changes: 58 additions & 19 deletions archive.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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));

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,
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,
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;
}
Expand Down Expand Up @@ -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);
}

Expand All @@ -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;
}

Expand Down Expand Up @@ -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,
Expand Down
12 changes: 12 additions & 0 deletions t/t5003-archive-zip.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down

0 comments on commit 237a1d1

Please sign in to comment.