@matheusbsbs / matheus-bagliano / commits / 2947a7930d
Fork 0

archive: add --add-file

Allow users to append non-tracked files. This simplifies the generation of source packages with a few extra files, e.g. containing version information. They get the same access times and user information as tracked files. Signed-off-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

René Scharfe committed Sep 19, 2020 at 23:23 UTC 2947a7930d2864cfbc3f9815959cd6539e2ea9ad
5 files changed +150 -1
Documentation/git-archive.txt
+6
@@ -55,6 +55,12 @@ OPTIONS
55 --output=<file>::
56 Write the archive to <file> instead of stdout.
57
58 +--add-file=<file>::
59 + Add a non-tracked file to the archive. Can be repeated to add
60 + multiple files. The path of the file in the archive is built
61 + by concatenating the value for `--prefix` (if any) and the
62 + basename of <file>.
63 +
64 --worktree-attributes::
65 Look for attributes in .gitattributes files in the working tree
66 as well (see <<ATTRIBUTES>>).
archive.c
+85 -1
@@ -266,6 +266,11 @@ static int queue_or_write_archive_entry(const struct object_id *oid,
266 stage, context);
267 }
268
269 +struct extra_file_info {
270 + char *base;
271 + struct stat stat;
272 +};
273 +
274 int write_archive_entries(struct archiver_args *args,
275 write_archive_entry_fn_t write_entry)
276 {
@@ -273,6 +278,10 @@ int write_archive_entries(struct archiver_args *args,
278 struct unpack_trees_options opts;
279 struct tree_desc t;
280 int err;
281 + struct strbuf path_in_archive = STRBUF_INIT;
282 + struct strbuf content = STRBUF_INIT;
283 + struct object_id fake_oid = null_oid;
284 + int i;
285
286 if (args->baselen > 0 && args->base[args->baselen - 1] == '/') {
287 size_t len = args->baselen;
@@ -318,6 +327,33 @@ int write_archive_entries(struct archiver_args *args,
327 free(context.bottom);
328 context.bottom = next;
329 }
330 +
331 + for (i = 0; i < args->extra_files.nr; i++) {
332 + struct string_list_item *item = args->extra_files.items + i;
333 + char *path = item->string;
334 + struct extra_file_info *info = item->util;
335 +
336 + put_be64(fake_oid.hash, i + 1);
337 +
338 + strbuf_reset(&path_in_archive);
339 + if (info->base)
340 + strbuf_addstr(&path_in_archive, info->base);
341 + strbuf_addstr(&path_in_archive, basename(path));
342 +
343 + strbuf_reset(&content);
344 + if (strbuf_read_file(&content, path, info->stat.st_size) < 0)
345 + err = error_errno(_("could not read '%s'"), path);
346 + else
347 + err = write_entry(args, &fake_oid, path_in_archive.buf,
348 + path_in_archive.len,
349 + info->stat.st_mode,
350 + content.buf, content.len);
351 + if (err)
352 + break;
353 + }
354 + strbuf_release(&path_in_archive);
355 + strbuf_release(&content);
356 +
357 return err;
358 }
359
@@ -457,6 +493,42 @@ static void parse_treeish_arg(const char **argv,
493 ar_args->time = archive_time;
494 }
495
496 +static void extra_file_info_clear(void *util, const char *str)
497 +{
498 + struct extra_file_info *info = util;
499 + free(info->base);
500 + free(info);
501 +}
502 +
503 +static int add_file_cb(const struct option *opt, const char *arg, int unset)
504 +{
505 + struct archiver_args *args = opt->value;
506 + const char **basep = (const char **)opt->defval;
507 + const char *base = *basep;
508 + char *path;
509 + struct string_list_item *item;
510 + struct extra_file_info *info;
511 +
512 + if (unset) {
513 + string_list_clear_func(&args->extra_files,
514 + extra_file_info_clear);
515 + return 0;
516 + }
517 +
518 + if (!arg)
519 + return -1;
520 +
521 + path = prefix_filename(args->prefix, arg);
522 + item = string_list_append_nodup(&args->extra_files, path);
523 + item->util = info = xmalloc(sizeof(*info));
524 + info->base = xstrdup_or_null(base);
525 + if (stat(path, &info->stat))
526 + die(_("File not found: %s"), path);
527 + if (!S_ISREG(info->stat.st_mode))
528 + die(_("Not a regular file: %s"), path);
529 + return 0;
530 +}
531 +
532 #define OPT__COMPR(s, v, h, p) \
533 OPT_SET_INT_F(s, NULL, v, h, p, PARSE_OPT_NONEG)
534 #define OPT__COMPR_HIDDEN(s, v, p) \
@@ -481,6 +553,9 @@ static int parse_archive_args(int argc, const char **argv,
553 OPT_STRING(0, "format", &format, N_("fmt"), N_("archive format")),
554 OPT_STRING(0, "prefix", &base, N_("prefix"),
555 N_("prepend prefix to each pathname in the archive")),
556 + { OPTION_CALLBACK, 0, "add-file", args, N_("file"),
557 + N_("add untracked file to archive"), 0, add_file_cb,
558 + (intptr_t)&base },
559 OPT_STRING('o', "output", &output, N_("file"),
560 N_("write the archive to this file")),
561 OPT_BOOL(0, "worktree-attributes", &worktree_attributes,
@@ -515,6 +590,8 @@ static int parse_archive_args(int argc, const char **argv,
590 die(_("Option --exec can only be used together with --remote"));
591 if (output)
592 die(_("Unexpected option --output"));
593 + if (is_remote && args->extra_files.nr)
594 + die(_("Options --add-file and --remote cannot be used together"));
595
596 if (!base)
597 base = "";
@@ -561,11 +638,14 @@ int write_archive(int argc, const char **argv, const char *prefix,
638 {
639 const struct archiver *ar = NULL;
640 struct archiver_args args;
641 + int rc;
642
643 git_config_get_bool("uploadarchive.allowunreachable", &remote_allow_unreachable);
644 git_config(git_default_config, NULL);
645
646 args.repo = repo;
647 + args.prefix = prefix;
648 + string_list_init(&args.extra_files, 1);
649 argc = parse_archive_args(argc, argv, &ar, &args, name_hint, remote);
650 if (!startup_info->have_repository) {
651 /*
@@ -579,7 +659,11 @@ int write_archive(int argc, const char **argv, const char *prefix,
659 parse_treeish_arg(argv, &args, prefix, remote);
660 parse_pathspec_arg(argv + 1, &args);
661
582 - return ar->write_archive(ar, &args);
662 + rc = ar->write_archive(ar, &args);
663 +
664 + string_list_clear_func(&args.extra_files, extra_file_info_clear);
665 +
666 + return rc;
667 }
668
669 static int match_extension(const char *filename, const char *ext)
archive.h
+2
@@ -9,6 +9,7 @@ struct repository;
9 struct archiver_args {
10 struct repository *repo;
11 const char *refname;
12 + const char *prefix;
13 const char *base;
14 size_t baselen;
15 struct tree *tree;
@@ -20,6 +21,7 @@ struct archiver_args {
21 unsigned int worktree_attributes : 1;
22 unsigned int convert : 1;
23 int compression_level;
24 + struct string_list extra_files;
25 };
26
27 /* main api */
t/t5000-tar-tree.sh
+29
@@ -94,6 +94,16 @@ check_tar() {
94 '
95 }
96
97 +check_added() {
98 + dir=$1
99 + path_in_fs=$2
100 + path_in_archive=$3
101 +
102 + test_expect_success " validate extra file $path_in_archive" '
103 + diff -r $path_in_fs $dir/$path_in_archive
104 + '
105 +}
106 +
107 test_expect_success 'setup' '
108 test_oid_cache <<-EOF
109 obj sha1:19f9c8273ec45a8938e6999cb59b3ff66739902a
@@ -164,6 +174,25 @@ test_expect_success 'git-archive --prefix=olde-' '
174
175 check_tar with_olde-prefix olde-
176
177 +test_expect_success 'git archive --add-file' '
178 + echo untracked >untracked &&
179 + git archive --add-file=untracked HEAD >with_untracked.tar
180 +'
181 +
182 +check_tar with_untracked
183 +check_added with_untracked untracked untracked
184 +
185 +test_expect_success 'git archive --add-file twice' '
186 + echo untracked >untracked &&
187 + git archive --prefix=one/ --add-file=untracked \
188 + --prefix=two/ --add-file=untracked \
189 + --prefix= HEAD >with_untracked2.tar
190 +'
191 +
192 +check_tar with_untracked2
193 +check_added with_untracked2 untracked one/untracked
194 +check_added with_untracked2 untracked two/untracked
195 +
196 test_expect_success 'git archive on large files' '
197 test_config core.bigfilethreshold 1 &&
198 git archive HEAD >b3.tar &&
t/t5003-archive-zip.sh
+28
@@ -72,6 +72,16 @@ check_zip() {
72 "
73 }
74
75 +check_added() {
76 + dir=$1
77 + path_in_fs=$2
78 + path_in_archive=$3
79 +
80 + test_expect_success UNZIP " validate extra file $path_in_archive" '
81 + diff -r $path_in_fs $dir/$path_in_archive
82 + '
83 +}
84 +
85 test_expect_success \
86 'populate workdir' \
87 'mkdir a &&
@@ -188,4 +198,22 @@ test_expect_success 'git archive --format=zip on large files' '
198
199 check_zip large-compressed
200
201 +test_expect_success 'git archive --format=zip --add-file' '
202 + echo untracked >untracked &&
203 + git archive --format=zip --add-file=untracked HEAD >with_untracked.zip
204 +'
205 +
206 +check_zip with_untracked
207 +check_added with_untracked untracked untracked
208 +
209 +test_expect_success 'git archive --format=zip --add-file twice' '
210 + echo untracked >untracked &&
211 + git archive --format=zip --prefix=one/ --add-file=untracked \
212 + --prefix=two/ --add-file=untracked \
213 + --prefix= HEAD >with_untracked2.zip
214 +'
215 +check_zip with_untracked2
216 +check_added with_untracked2 untracked one/untracked
217 +check_added with_untracked2 untracked two/untracked
218 +
219 test_done