Skip to content

Commit 237a1d1

Browse files
dschogitster
authored andcommitted
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 <[email protected]> [jc: tweaked <path> handling] Signed-off-by: Junio C Hamano <[email protected]>
1 parent 23f2356 commit 237a1d1

File tree

3 files changed

+82
-20
lines changed

3 files changed

+82
-20
lines changed

Documentation/git-archive.txt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ OPTIONS
5151
--prefix=<prefix>/::
5252
Prepend <prefix>/ to paths in the archive. Can be repeated; its
5353
rightmost value is used for all tracked files. See below which
54-
value gets used by `--add-file`.
54+
value gets used by `--add-file` and `--add-virtual-file`.
5555

5656
-o <file>::
5757
--output=<file>::
@@ -63,6 +63,17 @@ OPTIONS
6363
concatenating the value of the last `--prefix` option (if any)
6464
before this `--add-file` and the basename of <file>.
6565

66+
--add-virtual-file=<path>:<content>::
67+
Add the specified contents to the archive. Can be repeated to add
68+
multiple files. The path of the file in the archive is built
69+
by concatenating the value of the last `--prefix` option (if any)
70+
before this `--add-virtual-file` and `<path>`.
71+
+
72+
The `<path>` cannot contain any colon, the file mode is limited to
73+
a regular file, and the option may be subject to platform-dependent
74+
command-line limits. For non-trivial cases, write an untracked file
75+
and use `--add-file` instead.
76+
6677
--worktree-attributes::
6778
Look for attributes in .gitattributes files in the working tree
6879
as well (see <<ATTRIBUTES>>).

archive.c

Lines changed: 58 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ static int queue_or_write_archive_entry(const struct object_id *oid,
263263
struct extra_file_info {
264264
char *base;
265265
struct stat stat;
266+
void *content;
266267
};
267268

268269
int write_archive_entries(struct archiver_args *args,
@@ -331,19 +332,27 @@ int write_archive_entries(struct archiver_args *args,
331332

332333
put_be64(fake_oid.hash, i + 1);
333334

334-
strbuf_reset(&path_in_archive);
335-
if (info->base)
336-
strbuf_addstr(&path_in_archive, info->base);
337-
strbuf_addstr(&path_in_archive, basename(path));
338-
339-
strbuf_reset(&content);
340-
if (strbuf_read_file(&content, path, info->stat.st_size) < 0)
341-
err = error_errno(_("cannot read '%s'"), path);
342-
else
343-
err = write_entry(args, &fake_oid, path_in_archive.buf,
344-
path_in_archive.len,
335+
if (!info->content) {
336+
strbuf_reset(&path_in_archive);
337+
if (info->base)
338+
strbuf_addstr(&path_in_archive, info->base);
339+
strbuf_addstr(&path_in_archive, basename(path));
340+
341+
strbuf_reset(&content);
342+
if (strbuf_read_file(&content, path, info->stat.st_size) < 0)
343+
err = error_errno(_("cannot read '%s'"), path);
344+
else
345+
err = write_entry(args, &fake_oid, path_in_archive.buf,
346+
path_in_archive.len,
347+
canon_mode(info->stat.st_mode),
348+
content.buf, content.len);
349+
} else {
350+
err = write_entry(args, &fake_oid,
351+
path, strlen(path),
345352
canon_mode(info->stat.st_mode),
346-
content.buf, content.len);
353+
info->content, info->stat.st_size);
354+
}
355+
347356
if (err)
348357
break;
349358
}
@@ -493,6 +502,7 @@ static void extra_file_info_clear(void *util, const char *str)
493502
{
494503
struct extra_file_info *info = util;
495504
free(info->base);
505+
free(info->content);
496506
free(info);
497507
}
498508

@@ -514,14 +524,40 @@ static int add_file_cb(const struct option *opt, const char *arg, int unset)
514524
if (!arg)
515525
return -1;
516526

517-
path = prefix_filename(args->prefix, arg);
518-
item = string_list_append_nodup(&args->extra_files, path);
519-
item->util = info = xmalloc(sizeof(*info));
527+
info = xmalloc(sizeof(*info));
520528
info->base = xstrdup_or_null(base);
521-
if (stat(path, &info->stat))
522-
die(_("File not found: %s"), path);
523-
if (!S_ISREG(info->stat.st_mode))
524-
die(_("Not a regular file: %s"), path);
529+
530+
if (!strcmp(opt->long_name, "add-file")) {
531+
path = prefix_filename(args->prefix, arg);
532+
if (stat(path, &info->stat))
533+
die(_("File not found: %s"), path);
534+
if (!S_ISREG(info->stat.st_mode))
535+
die(_("Not a regular file: %s"), path);
536+
info->content = NULL; /* read the file later */
537+
} else if (!strcmp(opt->long_name, "add-virtual-file")) {
538+
const char *colon = strchr(arg, ':');
539+
char *p;
540+
541+
if (!colon)
542+
die(_("missing colon: '%s'"), arg);
543+
544+
p = xstrndup(arg, colon - arg);
545+
if (!args->prefix)
546+
path = p;
547+
else {
548+
path = prefix_filename(args->prefix, p);
549+
free(p);
550+
}
551+
memset(&info->stat, 0, sizeof(info->stat));
552+
info->stat.st_mode = S_IFREG | 0644;
553+
info->content = xstrdup(colon + 1);
554+
info->stat.st_size = strlen(info->content);
555+
} else {
556+
BUG("add_file_cb() called for %s", opt->long_name);
557+
}
558+
item = string_list_append_nodup(&args->extra_files, path);
559+
item->util = info;
560+
525561
return 0;
526562
}
527563

@@ -554,6 +590,9 @@ static int parse_archive_args(int argc, const char **argv,
554590
{ OPTION_CALLBACK, 0, "add-file", args, N_("file"),
555591
N_("add untracked file to archive"), 0, add_file_cb,
556592
(intptr_t)&base },
593+
{ OPTION_CALLBACK, 0, "add-virtual-file", args,
594+
N_("path:content"), N_("add untracked file to archive"), 0,
595+
add_file_cb, (intptr_t)&base },
557596
OPT_STRING('o', "output", &output, N_("file"),
558597
N_("write the archive to this file")),
559598
OPT_BOOL(0, "worktree-attributes", &worktree_attributes,

t/t5003-archive-zip.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,18 @@ test_expect_success 'git archive --format=zip --add-file' '
206206
check_zip with_untracked
207207
check_added with_untracked untracked untracked
208208

209+
test_expect_success UNZIP 'git archive --format=zip --add-virtual-file' '
210+
git archive --format=zip >with_file_with_content.zip \
211+
--add-virtual-file=hello:world $EMPTY_TREE &&
212+
test_when_finished "rm -rf tmp-unpack" &&
213+
mkdir tmp-unpack && (
214+
cd tmp-unpack &&
215+
"$GIT_UNZIP" ../with_file_with_content.zip &&
216+
test_path_is_file hello &&
217+
test world = $(cat hello)
218+
)
219+
'
220+
209221
test_expect_success 'git archive --format=zip --add-file twice' '
210222
echo untracked >untracked &&
211223
git archive --format=zip --prefix=one/ --add-file=untracked \

0 commit comments

Comments
 (0)