Skip to content

Commit a3daa56

Browse files
committed
Merge branch 'rs/archive-add-file' into seen
"git archive" learns the "--add-file" option to include untracked files into a snapshot from a tree-ish. * rs/archive-add-file: Makefile: use git-archive --add-file archive: add --add-file archive: read short blobs in archive.c::write_archive_entry()
2 parents 4d8ca35 + df368fa commit a3daa56

File tree

8 files changed

+203
-71
lines changed

8 files changed

+203
-71
lines changed

Documentation/git-archive.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ OPTIONS
5555
--output=<file>::
5656
Write the archive to <file> instead of stdout.
5757

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+
5864
--worktree-attributes::
5965
Look for attributes in .gitattributes files in the working tree
6066
as well (see <<ATTRIBUTES>>).

Makefile

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3102,32 +3102,29 @@ quick-install-html:
31023102
# With GNU tar, "--mode=u+rwX,og+rX,og-w" would be a good idea, for example.
31033103
TAR_DIST_EXTRA_OPTS =
31043104
GIT_TARNAME = git-$(GIT_VERSION)
3105+
GIT_ARCHIVE_EXTRA_FILES = \
3106+
--prefix=$(GIT_TARNAME)/ \
3107+
--add-file=configure \
3108+
--add-file=$(GIT_TARNAME)/version \
3109+
--prefix=$(GIT_TARNAME)/git-gui/ \
3110+
--add-file=$(GIT_TARNAME)/git-gui/version
3111+
ifdef DC_SHA1_SUBMODULE
3112+
GIT_ARCHIVE_EXTRA_FILES += \
3113+
--prefix=$(GIT_TARNAME)/sha1collisiondetection/ \
3114+
--add-file=sha1collisiondetection/LICENSE.txt \
3115+
--prefix=$(GIT_TARNAME)/sha1collisiondetection/lib/ \
3116+
--add-file=sha1collisiondetection/lib/sha1.c \
3117+
--add-file=sha1collisiondetection/lib/sha1.h \
3118+
--add-file=sha1collisiondetection/lib/ubc_check.c \
3119+
--add-file=sha1collisiondetection/lib/ubc_check.h
3120+
endif
31053121
dist: git-archive$(X) configure
3106-
./git-archive --format=tar \
3107-
--prefix=$(GIT_TARNAME)/ HEAD^{tree} > $(GIT_TARNAME).tar
31083122
@mkdir -p $(GIT_TARNAME)
3109-
@cp configure $(GIT_TARNAME)
31103123
@echo $(GIT_VERSION) > $(GIT_TARNAME)/version
31113124
@$(MAKE) -C git-gui TARDIR=../$(GIT_TARNAME)/git-gui dist-version
3112-
$(TAR) rf $(GIT_TARNAME).tar $(TAR_DIST_EXTRA_OPTS) \
3113-
$(GIT_TARNAME)/configure \
3114-
$(GIT_TARNAME)/version \
3115-
$(GIT_TARNAME)/git-gui/version
3116-
ifdef DC_SHA1_SUBMODULE
3117-
@mkdir -p $(GIT_TARNAME)/sha1collisiondetection/lib
3118-
@cp sha1collisiondetection/LICENSE.txt \
3119-
$(GIT_TARNAME)/sha1collisiondetection/
3120-
@cp sha1collisiondetection/LICENSE.txt \
3121-
$(GIT_TARNAME)/sha1collisiondetection/
3122-
@cp sha1collisiondetection/lib/sha1.[ch] \
3123-
$(GIT_TARNAME)/sha1collisiondetection/lib/
3124-
@cp sha1collisiondetection/lib/ubc_check.[ch] \
3125-
$(GIT_TARNAME)/sha1collisiondetection/lib/
3126-
$(TAR) rf $(GIT_TARNAME).tar $(TAR_DIST_EXTRA_OPTS) \
3127-
$(GIT_TARNAME)/sha1collisiondetection/LICENSE.txt \
3128-
$(GIT_TARNAME)/sha1collisiondetection/lib/sha1.[ch] \
3129-
$(GIT_TARNAME)/sha1collisiondetection/lib/ubc_check.[ch]
3130-
endif
3125+
./git-archive --format=tar \
3126+
$(GIT_ARCHIVE_EXTRA_FILES) \
3127+
--prefix=$(GIT_TARNAME)/ HEAD^{tree} > $(GIT_TARNAME).tar
31313128
@$(RM) -r $(GIT_TARNAME)
31323129
gzip -f -9 $(GIT_TARNAME).tar
31333130

archive-tar.c

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -242,13 +242,12 @@ static void write_extended_header(struct archiver_args *args,
242242
static int write_tar_entry(struct archiver_args *args,
243243
const struct object_id *oid,
244244
const char *path, size_t pathlen,
245-
unsigned int mode)
245+
unsigned int mode,
246+
void *buffer, unsigned long size)
246247
{
247248
struct ustar_header header;
248249
struct strbuf ext_header = STRBUF_INIT;
249-
unsigned int old_mode = mode;
250-
unsigned long size, size_in_header;
251-
void *buffer;
250+
unsigned long size_in_header;
252251
int err = 0;
253252

254253
memset(&header, 0, sizeof(header));
@@ -282,20 +281,6 @@ static int write_tar_entry(struct archiver_args *args,
282281
} else
283282
memcpy(header.name, path, pathlen);
284283

285-
if (S_ISREG(mode) && !args->convert &&
286-
oid_object_info(args->repo, oid, &size) == OBJ_BLOB &&
287-
size > big_file_threshold)
288-
buffer = NULL;
289-
else if (S_ISLNK(mode) || S_ISREG(mode)) {
290-
enum object_type type;
291-
buffer = object_file_to_archive(args, path, oid, old_mode, &type, &size);
292-
if (!buffer)
293-
return error(_("cannot read %s"), oid_to_hex(oid));
294-
} else {
295-
buffer = NULL;
296-
size = 0;
297-
}
298-
299284
if (S_ISLNK(mode)) {
300285
if (size > sizeof(header.linkname)) {
301286
xsnprintf(header.linkname, sizeof(header.linkname),
@@ -326,7 +311,6 @@ static int write_tar_entry(struct archiver_args *args,
326311
else
327312
err = stream_blocked(args->repo, oid);
328313
}
329-
free(buffer);
330314
return err;
331315
}
332316

archive-zip.c

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,8 @@ static int entry_is_binary(struct index_state *istate, const char *path,
285285
static int write_zip_entry(struct archiver_args *args,
286286
const struct object_id *oid,
287287
const char *path, size_t pathlen,
288-
unsigned int mode)
288+
unsigned int mode,
289+
void *buffer, unsigned long size)
289290
{
290291
struct zip_local_header header;
291292
uintmax_t offset = zip_offset;
@@ -299,10 +300,8 @@ static int write_zip_entry(struct archiver_args *args,
299300
enum zip_method method;
300301
unsigned char *out;
301302
void *deflated = NULL;
302-
void *buffer;
303303
struct git_istream *stream = NULL;
304304
unsigned long flags = 0;
305-
unsigned long size;
306305
int is_binary = -1;
307306
const char *path_without_prefix = path + args->baselen;
308307
unsigned int creator_version = 0;
@@ -328,13 +327,8 @@ static int write_zip_entry(struct archiver_args *args,
328327
method = ZIP_METHOD_STORE;
329328
attr2 = 16;
330329
out = NULL;
331-
size = 0;
332330
compressed_size = 0;
333-
buffer = NULL;
334331
} else if (S_ISREG(mode) || S_ISLNK(mode)) {
335-
enum object_type type = oid_object_info(args->repo, oid,
336-
&size);
337-
338332
method = ZIP_METHOD_STORE;
339333
attr2 = S_ISLNK(mode) ? ((mode | 0777) << 16) :
340334
(mode & 0111) ? ((mode) << 16) : 0;
@@ -343,21 +337,16 @@ static int write_zip_entry(struct archiver_args *args,
343337
if (S_ISREG(mode) && args->compression_level != 0 && size > 0)
344338
method = ZIP_METHOD_DEFLATE;
345339

346-
if (S_ISREG(mode) && type == OBJ_BLOB && !args->convert &&
347-
size > big_file_threshold) {
340+
if (!buffer) {
341+
enum object_type type;
348342
stream = open_istream(args->repo, oid, &type, &size,
349343
NULL);
350344
if (!stream)
351345
return error(_("cannot stream blob %s"),
352346
oid_to_hex(oid));
353347
flags |= ZIP_STREAM;
354-
out = buffer = NULL;
348+
out = NULL;
355349
} else {
356-
buffer = object_file_to_archive(args, path, oid, mode,
357-
&type, &size);
358-
if (!buffer)
359-
return error(_("cannot read %s"),
360-
oid_to_hex(oid));
361350
crc = crc32(crc, buffer, size);
362351
is_binary = entry_is_binary(args->repo->index,
363352
path_without_prefix,
@@ -511,7 +500,6 @@ static int write_zip_entry(struct archiver_args *args,
511500
}
512501

513502
free(deflated);
514-
free(buffer);
515503

516504
if (compressed_size > 0xffffffff || size > 0xffffffff ||
517505
offset > 0xffffffff) {

archive.c

Lines changed: 109 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,12 @@ static void format_subst(const struct commit *commit,
7070
free(to_free);
7171
}
7272

73-
void *object_file_to_archive(const struct archiver_args *args,
74-
const char *path, const struct object_id *oid,
75-
unsigned int mode, enum object_type *type,
76-
unsigned long *sizep)
73+
static void *object_file_to_archive(const struct archiver_args *args,
74+
const char *path,
75+
const struct object_id *oid,
76+
unsigned int mode,
77+
enum object_type *type,
78+
unsigned long *sizep)
7779
{
7880
void *buffer;
7981
const struct commit *commit = args->convert ? args->commit : NULL;
@@ -145,6 +147,9 @@ static int write_archive_entry(const struct object_id *oid, const char *base,
145147
write_archive_entry_fn_t write_entry = c->write_entry;
146148
int err;
147149
const char *path_without_prefix;
150+
unsigned long size;
151+
void *buffer;
152+
enum object_type type;
148153

149154
args->convert = 0;
150155
strbuf_reset(&path);
@@ -167,15 +172,27 @@ static int write_archive_entry(const struct object_id *oid, const char *base,
167172
if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
168173
if (args->verbose)
169174
fprintf(stderr, "%.*s\n", (int)path.len, path.buf);
170-
err = write_entry(args, oid, path.buf, path.len, mode);
175+
err = write_entry(args, oid, path.buf, path.len, mode, NULL, 0);
171176
if (err)
172177
return err;
173178
return (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);
174179
}
175180

176181
if (args->verbose)
177182
fprintf(stderr, "%.*s\n", (int)path.len, path.buf);
178-
return write_entry(args, oid, path.buf, path.len, mode);
183+
184+
/* Stream it? */
185+
if (S_ISREG(mode) && !args->convert &&
186+
oid_object_info(args->repo, oid, &size) == OBJ_BLOB &&
187+
size > big_file_threshold)
188+
return write_entry(args, oid, path.buf, path.len, mode, NULL, size);
189+
190+
buffer = object_file_to_archive(args, path.buf, oid, mode, &type, &size);
191+
if (!buffer)
192+
return error(_("cannot read %s"), oid_to_hex(oid));
193+
err = write_entry(args, oid, path.buf, path.len, mode, buffer, size);
194+
free(buffer);
195+
return err;
179196
}
180197

181198
static void queue_directory(const unsigned char *sha1,
@@ -249,13 +266,22 @@ static int queue_or_write_archive_entry(const struct object_id *oid,
249266
stage, context);
250267
}
251268

269+
struct extra_file_info {
270+
char *base;
271+
struct stat stat;
272+
};
273+
252274
int write_archive_entries(struct archiver_args *args,
253275
write_archive_entry_fn_t write_entry)
254276
{
255277
struct archiver_context context;
256278
struct unpack_trees_options opts;
257279
struct tree_desc t;
258280
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;
259285

260286
if (args->baselen > 0 && args->base[args->baselen - 1] == '/') {
261287
size_t len = args->baselen;
@@ -265,7 +291,7 @@ int write_archive_entries(struct archiver_args *args,
265291
if (args->verbose)
266292
fprintf(stderr, "%.*s\n", (int)len, args->base);
267293
err = write_entry(args, &args->tree->object.oid, args->base,
268-
len, 040777);
294+
len, 040777, NULL, 0);
269295
if (err)
270296
return err;
271297
}
@@ -301,6 +327,33 @@ int write_archive_entries(struct archiver_args *args,
301327
free(context.bottom);
302328
context.bottom = next;
303329
}
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+
304357
return err;
305358
}
306359

@@ -440,6 +493,42 @@ static void parse_treeish_arg(const char **argv,
440493
ar_args->time = archive_time;
441494
}
442495

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+
443532
#define OPT__COMPR(s, v, h, p) \
444533
OPT_SET_INT_F(s, NULL, v, h, p, PARSE_OPT_NONEG)
445534
#define OPT__COMPR_HIDDEN(s, v, p) \
@@ -464,6 +553,9 @@ static int parse_archive_args(int argc, const char **argv,
464553
OPT_STRING(0, "format", &format, N_("fmt"), N_("archive format")),
465554
OPT_STRING(0, "prefix", &base, N_("prefix"),
466555
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 },
467559
OPT_STRING('o', "output", &output, N_("file"),
468560
N_("write the archive to this file")),
469561
OPT_BOOL(0, "worktree-attributes", &worktree_attributes,
@@ -498,6 +590,8 @@ static int parse_archive_args(int argc, const char **argv,
498590
die(_("Option --exec can only be used together with --remote"));
499591
if (output)
500592
die(_("Unexpected option --output"));
593+
if (is_remote && args->extra_files.nr)
594+
die(_("Options --add-file and --remote cannot be used together"));
501595

502596
if (!base)
503597
base = "";
@@ -544,11 +638,14 @@ int write_archive(int argc, const char **argv, const char *prefix,
544638
{
545639
const struct archiver *ar = NULL;
546640
struct archiver_args args;
641+
int rc;
547642

548643
git_config_get_bool("uploadarchive.allowunreachable", &remote_allow_unreachable);
549644
git_config(git_default_config, NULL);
550645

551646
args.repo = repo;
647+
args.prefix = prefix;
648+
string_list_init(&args.extra_files, 1);
552649
argc = parse_archive_args(argc, argv, &ar, &args, name_hint, remote);
553650
if (!startup_info->have_repository) {
554651
/*
@@ -562,7 +659,11 @@ int write_archive(int argc, const char **argv, const char *prefix,
562659
parse_treeish_arg(argv, &args, prefix, remote);
563660
parse_pathspec_arg(argv + 1, &args);
564661

565-
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;
566667
}
567668

568669
static int match_extension(const char *filename, const char *ext)

0 commit comments

Comments
 (0)