@@ -266,13 +266,22 @@ static int queue_or_write_archive_entry(const struct object_id *oid,
266266 stage , context );
267267}
268268
269+ struct extra_file_info {
270+ char * base ;
271+ struct stat stat ;
272+ };
273+
269274int write_archive_entries (struct archiver_args * args ,
270275 write_archive_entry_fn_t write_entry )
271276{
272277 struct archiver_context context ;
273278 struct unpack_trees_options opts ;
274279 struct tree_desc t ;
275280 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 ;
276285
277286 if (args -> baselen > 0 && args -> base [args -> baselen - 1 ] == '/' ) {
278287 size_t len = args -> baselen ;
@@ -318,6 +327,33 @@ int write_archive_entries(struct archiver_args *args,
318327 free (context .bottom );
319328 context .bottom = next ;
320329 }
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+
321357 return err ;
322358}
323359
@@ -457,6 +493,42 @@ static void parse_treeish_arg(const char **argv,
457493 ar_args -> time = archive_time ;
458494}
459495
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+
460532#define OPT__COMPR (s , v , h , p ) \
461533 OPT_SET_INT_F(s, NULL, v, h, p, PARSE_OPT_NONEG)
462534#define OPT__COMPR_HIDDEN (s , v , p ) \
@@ -481,6 +553,9 @@ static int parse_archive_args(int argc, const char **argv,
481553 OPT_STRING (0 , "format" , & format , N_ ("fmt" ), N_ ("archive format" )),
482554 OPT_STRING (0 , "prefix" , & base , N_ ("prefix" ),
483555 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 },
484559 OPT_STRING ('o' , "output" , & output , N_ ("file" ),
485560 N_ ("write the archive to this file" )),
486561 OPT_BOOL (0 , "worktree-attributes" , & worktree_attributes ,
@@ -515,6 +590,8 @@ static int parse_archive_args(int argc, const char **argv,
515590 die (_ ("Option --exec can only be used together with --remote" ));
516591 if (output )
517592 die (_ ("Unexpected option --output" ));
593+ if (is_remote && args -> extra_files .nr )
594+ die (_ ("Options --add-file and --remote cannot be used together" ));
518595
519596 if (!base )
520597 base = "" ;
@@ -561,11 +638,14 @@ int write_archive(int argc, const char **argv, const char *prefix,
561638{
562639 const struct archiver * ar = NULL ;
563640 struct archiver_args args ;
641+ int rc ;
564642
565643 git_config_get_bool ("uploadarchive.allowunreachable" , & remote_allow_unreachable );
566644 git_config (git_default_config , NULL );
567645
568646 args .repo = repo ;
647+ args .prefix = prefix ;
648+ string_list_init (& args .extra_files , 1 );
569649 argc = parse_archive_args (argc , argv , & ar , & args , name_hint , remote );
570650 if (!startup_info -> have_repository ) {
571651 /*
@@ -579,7 +659,11 @@ int write_archive(int argc, const char **argv, const char *prefix,
579659 parse_treeish_arg (argv , & args , prefix , remote );
580660 parse_pathspec_arg (argv + 1 , & args );
581661
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 ;
583667}
584668
585669static int match_extension (const char * filename , const char * ext )
0 commit comments