1818static const char * empty_base = "" ;
1919
2020static char const * const builtin_sparse_checkout_usage [] = {
21- N_ ("git sparse-checkout (init|list|set|disable) <options>" ),
21+ N_ ("git sparse-checkout (init|list|set|add| disable) <options>" ),
2222 NULL
2323};
2424
@@ -394,6 +394,9 @@ static void strbuf_to_cone_pattern(struct strbuf *line, struct pattern_list *pl)
394394
395395 strbuf_trim_trailing_dir_sep (line );
396396
397+ if (strbuf_normalize_path (line ))
398+ die (_ ("could not normalize path %s" ), line -> buf );
399+
397400 if (!line -> len )
398401 return ;
399402
@@ -404,44 +407,24 @@ static void strbuf_to_cone_pattern(struct strbuf *line, struct pattern_list *pl)
404407}
405408
406409static char const * const builtin_sparse_checkout_set_usage [] = {
407- N_ ("git sparse-checkout set (--stdin | <patterns>)" ),
410+ N_ ("git sparse-checkout ( set|add) (--stdin | <patterns>)" ),
408411 NULL
409412};
410413
411414static struct sparse_checkout_set_opts {
412415 int use_stdin ;
413416} set_opts ;
414417
415- static int sparse_checkout_set (int argc , const char * * argv , const char * prefix )
418+ static void add_patterns_from_input (struct pattern_list * pl ,
419+ int argc , const char * * argv )
416420{
417421 int i ;
418- struct pattern_list pl ;
419- int result ;
420- int changed_config = 0 ;
421-
422- static struct option builtin_sparse_checkout_set_options [] = {
423- OPT_BOOL (0 , "stdin" , & set_opts .use_stdin ,
424- N_ ("read patterns from standard in" )),
425- OPT_END (),
426- };
427-
428- repo_read_index (the_repository );
429- require_clean_work_tree (the_repository ,
430- N_ ("set sparse-checkout patterns" ), NULL , 1 , 0 );
431-
432- memset (& pl , 0 , sizeof (pl ));
433-
434- argc = parse_options (argc , argv , prefix ,
435- builtin_sparse_checkout_set_options ,
436- builtin_sparse_checkout_set_usage ,
437- PARSE_OPT_KEEP_UNKNOWN );
438-
439422 if (core_sparse_checkout_cone ) {
440423 struct strbuf line = STRBUF_INIT ;
441424
442- hashmap_init (& pl . recursive_hashmap , pl_hashmap_cmp , NULL , 0 );
443- hashmap_init (& pl . parent_hashmap , pl_hashmap_cmp , NULL , 0 );
444- pl . use_cone_patterns = 1 ;
425+ hashmap_init (& pl -> recursive_hashmap , pl_hashmap_cmp , NULL , 0 );
426+ hashmap_init (& pl -> parent_hashmap , pl_hashmap_cmp , NULL , 0 );
427+ pl -> use_cone_patterns = 1 ;
445428
446429 if (set_opts .use_stdin ) {
447430 struct strbuf unquoted = STRBUF_INIT ;
@@ -455,15 +438,15 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
455438 strbuf_swap (& unquoted , & line );
456439 }
457440
458- strbuf_to_cone_pattern (& line , & pl );
441+ strbuf_to_cone_pattern (& line , pl );
459442 }
460443
461444 strbuf_release (& unquoted );
462445 } else {
463446 for (i = 0 ; i < argc ; i ++ ) {
464447 strbuf_setlen (& line , 0 );
465448 strbuf_addstr (& line , argv [i ]);
466- strbuf_to_cone_pattern (& line , & pl );
449+ strbuf_to_cone_pattern (& line , pl );
467450 }
468451 }
469452 } else {
@@ -473,13 +456,84 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
473456 while (!strbuf_getline (& line , stdin )) {
474457 size_t len ;
475458 char * buf = strbuf_detach (& line , & len );
476- add_pattern (buf , empty_base , 0 , & pl , 0 );
459+ add_pattern (buf , empty_base , 0 , pl , 0 );
477460 }
478461 } else {
479462 for (i = 0 ; i < argc ; i ++ )
480- add_pattern (argv [i ], empty_base , 0 , & pl , 0 );
463+ add_pattern (argv [i ], empty_base , 0 , pl , 0 );
481464 }
482465 }
466+ }
467+
468+ enum modify_type {
469+ REPLACE ,
470+ ADD ,
471+ };
472+
473+ static void add_patterns_cone_mode (int argc , const char * * argv ,
474+ struct pattern_list * pl )
475+ {
476+ struct strbuf buffer = STRBUF_INIT ;
477+ struct pattern_entry * pe ;
478+ struct hashmap_iter iter ;
479+ struct pattern_list existing ;
480+ char * sparse_filename = get_sparse_checkout_filename ();
481+
482+ add_patterns_from_input (pl , argc , argv );
483+
484+ memset (& existing , 0 , sizeof (existing ));
485+ existing .use_cone_patterns = core_sparse_checkout_cone ;
486+
487+ if (add_patterns_from_file_to_list (sparse_filename , "" , 0 ,
488+ & existing , NULL ))
489+ die (_ ("unable to load existing sparse-checkout patterns" ));
490+ free (sparse_filename );
491+
492+ hashmap_for_each_entry (& existing .recursive_hashmap , & iter , pe , ent ) {
493+ if (!hashmap_contains_parent (& pl -> recursive_hashmap ,
494+ pe -> pattern , & buffer ) ||
495+ !hashmap_contains_parent (& pl -> parent_hashmap ,
496+ pe -> pattern , & buffer )) {
497+ strbuf_reset (& buffer );
498+ strbuf_addstr (& buffer , pe -> pattern );
499+ insert_recursive_pattern (pl , & buffer );
500+ }
501+ }
502+
503+ clear_pattern_list (& existing );
504+ strbuf_release (& buffer );
505+ }
506+
507+ static void add_patterns_literal (int argc , const char * * argv ,
508+ struct pattern_list * pl )
509+ {
510+ char * sparse_filename = get_sparse_checkout_filename ();
511+ if (add_patterns_from_file_to_list (sparse_filename , "" , 0 ,
512+ pl , NULL ))
513+ die (_ ("unable to load existing sparse-checkout patterns" ));
514+ free (sparse_filename );
515+ add_patterns_from_input (pl , argc , argv );
516+ }
517+
518+ static int modify_pattern_list (int argc , const char * * argv , enum modify_type m )
519+ {
520+ int result ;
521+ int changed_config = 0 ;
522+ struct pattern_list pl ;
523+ memset (& pl , 0 , sizeof (pl ));
524+
525+ switch (m ) {
526+ case ADD :
527+ if (core_sparse_checkout_cone )
528+ add_patterns_cone_mode (argc , argv , & pl );
529+ else
530+ add_patterns_literal (argc , argv , & pl );
531+ break ;
532+
533+ case REPLACE :
534+ add_patterns_from_input (& pl , argc , argv );
535+ break ;
536+ }
483537
484538 if (!core_apply_sparse_checkout ) {
485539 set_config (MODE_ALL_PATTERNS );
@@ -496,6 +550,27 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
496550 return result ;
497551}
498552
553+ static int sparse_checkout_set (int argc , const char * * argv , const char * prefix ,
554+ enum modify_type m )
555+ {
556+ static struct option builtin_sparse_checkout_set_options [] = {
557+ OPT_BOOL (0 , "stdin" , & set_opts .use_stdin ,
558+ N_ ("read patterns from standard in" )),
559+ OPT_END (),
560+ };
561+
562+ repo_read_index (the_repository );
563+ require_clean_work_tree (the_repository ,
564+ N_ ("set sparse-checkout patterns" ), NULL , 1 , 0 );
565+
566+ argc = parse_options (argc , argv , prefix ,
567+ builtin_sparse_checkout_set_options ,
568+ builtin_sparse_checkout_set_usage ,
569+ PARSE_OPT_KEEP_UNKNOWN );
570+
571+ return modify_pattern_list (argc , argv , m );
572+ }
573+
499574static int sparse_checkout_disable (int argc , const char * * argv )
500575{
501576 struct pattern_list pl ;
@@ -544,7 +619,9 @@ int cmd_sparse_checkout(int argc, const char **argv, const char *prefix)
544619 if (!strcmp (argv [0 ], "init" ))
545620 return sparse_checkout_init (argc , argv );
546621 if (!strcmp (argv [0 ], "set" ))
547- return sparse_checkout_set (argc , argv , prefix );
622+ return sparse_checkout_set (argc , argv , prefix , REPLACE );
623+ if (!strcmp (argv [0 ], "add" ))
624+ return sparse_checkout_set (argc , argv , prefix , ADD );
548625 if (!strcmp (argv [0 ], "disable" ))
549626 return sparse_checkout_disable (argc , argv );
550627 }
0 commit comments