@@ -70,10 +70,12 @@ static void format_subst(const struct commit *commit,
70
70
free (to_free );
71
71
}
72
72
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 )
77
79
{
78
80
void * buffer ;
79
81
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,
145
147
write_archive_entry_fn_t write_entry = c -> write_entry ;
146
148
int err ;
147
149
const char * path_without_prefix ;
150
+ unsigned long size ;
151
+ void * buffer ;
152
+ enum object_type type ;
148
153
149
154
args -> convert = 0 ;
150
155
strbuf_reset (& path );
@@ -167,15 +172,27 @@ static int write_archive_entry(const struct object_id *oid, const char *base,
167
172
if (S_ISDIR (mode ) || S_ISGITLINK (mode )) {
168
173
if (args -> verbose )
169
174
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 );
171
176
if (err )
172
177
return err ;
173
178
return (S_ISDIR (mode ) ? READ_TREE_RECURSIVE : 0 );
174
179
}
175
180
176
181
if (args -> verbose )
177
182
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 ;
179
196
}
180
197
181
198
static void queue_directory (const unsigned char * sha1 ,
@@ -249,13 +266,22 @@ static int queue_or_write_archive_entry(const struct object_id *oid,
249
266
stage , context );
250
267
}
251
268
269
+ struct extra_file_info {
270
+ char * base ;
271
+ struct stat stat ;
272
+ };
273
+
252
274
int write_archive_entries (struct archiver_args * args ,
253
275
write_archive_entry_fn_t write_entry )
254
276
{
255
277
struct archiver_context context ;
256
278
struct unpack_trees_options opts ;
257
279
struct tree_desc t ;
258
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 ;
259
285
260
286
if (args -> baselen > 0 && args -> base [args -> baselen - 1 ] == '/' ) {
261
287
size_t len = args -> baselen ;
@@ -265,7 +291,7 @@ int write_archive_entries(struct archiver_args *args,
265
291
if (args -> verbose )
266
292
fprintf (stderr , "%.*s\n" , (int )len , args -> base );
267
293
err = write_entry (args , & args -> tree -> object .oid , args -> base ,
268
- len , 040777 );
294
+ len , 040777 , NULL , 0 );
269
295
if (err )
270
296
return err ;
271
297
}
@@ -301,6 +327,33 @@ int write_archive_entries(struct archiver_args *args,
301
327
free (context .bottom );
302
328
context .bottom = next ;
303
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
+
304
357
return err ;
305
358
}
306
359
@@ -440,6 +493,42 @@ static void parse_treeish_arg(const char **argv,
440
493
ar_args -> time = archive_time ;
441
494
}
442
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
+
443
532
#define OPT__COMPR (s , v , h , p ) \
444
533
OPT_SET_INT_F(s, NULL, v, h, p, PARSE_OPT_NONEG)
445
534
#define OPT__COMPR_HIDDEN (s , v , p ) \
@@ -464,6 +553,9 @@ static int parse_archive_args(int argc, const char **argv,
464
553
OPT_STRING (0 , "format" , & format , N_ ("fmt" ), N_ ("archive format" )),
465
554
OPT_STRING (0 , "prefix" , & base , N_ ("prefix" ),
466
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 },
467
559
OPT_STRING ('o' , "output" , & output , N_ ("file" ),
468
560
N_ ("write the archive to this file" )),
469
561
OPT_BOOL (0 , "worktree-attributes" , & worktree_attributes ,
@@ -498,6 +590,8 @@ static int parse_archive_args(int argc, const char **argv,
498
590
die (_ ("Option --exec can only be used together with --remote" ));
499
591
if (output )
500
592
die (_ ("Unexpected option --output" ));
593
+ if (is_remote && args -> extra_files .nr )
594
+ die (_ ("Options --add-file and --remote cannot be used together" ));
501
595
502
596
if (!base )
503
597
base = "" ;
@@ -544,11 +638,14 @@ int write_archive(int argc, const char **argv, const char *prefix,
544
638
{
545
639
const struct archiver * ar = NULL ;
546
640
struct archiver_args args ;
641
+ int rc ;
547
642
548
643
git_config_get_bool ("uploadarchive.allowunreachable" , & remote_allow_unreachable );
549
644
git_config (git_default_config , NULL );
550
645
551
646
args .repo = repo ;
647
+ args .prefix = prefix ;
648
+ string_list_init (& args .extra_files , 1 );
552
649
argc = parse_archive_args (argc , argv , & ar , & args , name_hint , remote );
553
650
if (!startup_info -> have_repository ) {
554
651
/*
@@ -562,7 +659,11 @@ int write_archive(int argc, const char **argv, const char *prefix,
562
659
parse_treeish_arg (argv , & args , prefix , remote );
563
660
parse_pathspec_arg (argv + 1 , & args );
564
661
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 ;
566
667
}
567
668
568
669
static int match_extension (const char * filename , const char * ext )
0 commit comments