13
13
#include "remote.h"
14
14
#include "refs.h"
15
15
#include "connect.h"
16
+ #include "revision.h"
17
+ #include "diffcore.h"
18
+ #include "diff.h"
19
+
20
+ #define OPT_QUIET (1 << 0)
21
+ #define OPT_CACHED (1 << 1)
22
+ #define OPT_RECURSIVE (1 << 2)
23
+
24
+ typedef void (* each_submodule_fn )(const struct cache_entry * list_item ,
25
+ void * cb_data );
16
26
17
27
static char * get_default_remote (void )
18
28
{
@@ -219,6 +229,64 @@ static int resolve_relative_url_test(int argc, const char **argv, const char *pr
219
229
return 0 ;
220
230
}
221
231
232
+ /* the result should be freed by the caller. */
233
+ static char * get_submodule_displaypath (const char * path , const char * prefix )
234
+ {
235
+ const char * super_prefix = get_super_prefix ();
236
+
237
+ if (prefix && super_prefix ) {
238
+ BUG ("cannot have prefix '%s' and superprefix '%s'" ,
239
+ prefix , super_prefix );
240
+ } else if (prefix ) {
241
+ struct strbuf sb = STRBUF_INIT ;
242
+ char * displaypath = xstrdup (relative_path (path , prefix , & sb ));
243
+ strbuf_release (& sb );
244
+ return displaypath ;
245
+ } else if (super_prefix ) {
246
+ return xstrfmt ("%s%s" , super_prefix , path );
247
+ } else {
248
+ return xstrdup (path );
249
+ }
250
+ }
251
+
252
+ static char * compute_rev_name (const char * sub_path , const char * object_id )
253
+ {
254
+ struct strbuf sb = STRBUF_INIT ;
255
+ const char * * * d ;
256
+
257
+ static const char * describe_bare [] = { NULL };
258
+
259
+ static const char * describe_tags [] = { "--tags" , NULL };
260
+
261
+ static const char * describe_contains [] = { "--contains" , NULL };
262
+
263
+ static const char * describe_all_always [] = { "--all" , "--always" , NULL };
264
+
265
+ static const char * * describe_argv [] = { describe_bare , describe_tags ,
266
+ describe_contains ,
267
+ describe_all_always , NULL };
268
+
269
+ for (d = describe_argv ; * d ; d ++ ) {
270
+ struct child_process cp = CHILD_PROCESS_INIT ;
271
+ prepare_submodule_repo_env (& cp .env_array );
272
+ cp .dir = sub_path ;
273
+ cp .git_cmd = 1 ;
274
+ cp .no_stderr = 1 ;
275
+
276
+ argv_array_push (& cp .args , "describe" );
277
+ argv_array_pushv (& cp .args , * d );
278
+ argv_array_push (& cp .args , object_id );
279
+
280
+ if (!capture_command (& cp , & sb , 0 )) {
281
+ strbuf_strip_suffix (& sb , "\n" );
282
+ return strbuf_detach (& sb , NULL );
283
+ }
284
+ }
285
+
286
+ strbuf_release (& sb );
287
+ return NULL ;
288
+ }
289
+
222
290
struct module_list {
223
291
const struct cache_entry * * entries ;
224
292
int alloc , nr ;
@@ -328,21 +396,29 @@ static int module_list(int argc, const char **argv, const char *prefix)
328
396
return 0 ;
329
397
}
330
398
331
- static void init_submodule (const char * path , const char * prefix , int quiet )
399
+ static void for_each_listed_submodule (const struct module_list * list ,
400
+ each_submodule_fn fn , void * cb_data )
401
+ {
402
+ int i ;
403
+ for (i = 0 ; i < list -> nr ; i ++ )
404
+ fn (list -> entries [i ], cb_data );
405
+ }
406
+
407
+ struct init_cb {
408
+ const char * prefix ;
409
+ unsigned int flags ;
410
+ };
411
+
412
+ #define INIT_CB_INIT { NULL, 0 }
413
+
414
+ static void init_submodule (const char * path , const char * prefix ,
415
+ unsigned int flags )
332
416
{
333
417
const struct submodule * sub ;
334
418
struct strbuf sb = STRBUF_INIT ;
335
419
char * upd = NULL , * url = NULL , * displaypath ;
336
420
337
- if (prefix && get_super_prefix ())
338
- die ("BUG: cannot have prefix and superprefix" );
339
- else if (prefix )
340
- displaypath = xstrdup (relative_path (path , prefix , & sb ));
341
- else if (get_super_prefix ()) {
342
- strbuf_addf (& sb , "%s%s" , get_super_prefix (), path );
343
- displaypath = strbuf_detach (& sb , NULL );
344
- } else
345
- displaypath = xstrdup (path );
421
+ displaypath = get_submodule_displaypath (path , prefix );
346
422
347
423
sub = submodule_from_path (& null_oid , path );
348
424
@@ -357,17 +433,16 @@ static void init_submodule(const char *path, const char *prefix, int quiet)
357
433
* Set active flag for the submodule being initialized
358
434
*/
359
435
if (!is_submodule_active (the_repository , path )) {
360
- strbuf_reset (& sb );
361
436
strbuf_addf (& sb , "submodule.%s.active" , sub -> name );
362
437
git_config_set_gently (sb .buf , "true" );
438
+ strbuf_reset (& sb );
363
439
}
364
440
365
441
/*
366
442
* Copy url setting when it is not set yet.
367
443
* To look up the url in .git/config, we must not fall back to
368
444
* .gitmodules, so look it up directly.
369
445
*/
370
- strbuf_reset (& sb );
371
446
strbuf_addf (& sb , "submodule.%s.url" , sub -> name );
372
447
if (git_config_get_string (sb .buf , & url )) {
373
448
if (!sub -> url )
@@ -399,14 +474,14 @@ static void init_submodule(const char *path, const char *prefix, int quiet)
399
474
if (git_config_set_gently (sb .buf , url ))
400
475
die (_ ("Failed to register url for submodule path '%s'" ),
401
476
displaypath );
402
- if (!quiet )
477
+ if (!( flags & OPT_QUIET ) )
403
478
fprintf (stderr ,
404
479
_ ("Submodule '%s' (%s) registered for path '%s'\n" ),
405
480
sub -> name , url , displaypath );
406
481
}
482
+ strbuf_reset (& sb );
407
483
408
484
/* Copy "update" setting when it is not set yet */
409
- strbuf_reset (& sb );
410
485
strbuf_addf (& sb , "submodule.%s.update" , sub -> name );
411
486
if (git_config_get_string (sb .buf , & upd ) &&
412
487
sub -> update_strategy .type != SM_UPDATE_UNSPECIFIED ) {
@@ -426,12 +501,18 @@ static void init_submodule(const char *path, const char *prefix, int quiet)
426
501
free (upd );
427
502
}
428
503
504
+ static void init_submodule_cb (const struct cache_entry * list_item , void * cb_data )
505
+ {
506
+ struct init_cb * info = cb_data ;
507
+ init_submodule (list_item -> name , info -> prefix , info -> flags );
508
+ }
509
+
429
510
static int module_init (int argc , const char * * argv , const char * prefix )
430
511
{
512
+ struct init_cb info = INIT_CB_INIT ;
431
513
struct pathspec pathspec ;
432
514
struct module_list list = MODULE_LIST_INIT ;
433
515
int quiet = 0 ;
434
- int i ;
435
516
436
517
struct option module_init_options [] = {
437
518
OPT__QUIET (& quiet , N_ ("Suppress output for initializing a submodule" )),
@@ -456,8 +537,165 @@ static int module_init(int argc, const char **argv, const char *prefix)
456
537
if (!argc && git_config_get_value_multi ("submodule.active" ))
457
538
module_list_active (& list );
458
539
459
- for (i = 0 ; i < list .nr ; i ++ )
460
- init_submodule (list .entries [i ]-> name , prefix , quiet );
540
+ info .prefix = prefix ;
541
+ if (quiet )
542
+ info .flags |= OPT_QUIET ;
543
+
544
+ for_each_listed_submodule (& list , init_submodule_cb , & info );
545
+
546
+ return 0 ;
547
+ }
548
+
549
+ struct status_cb {
550
+ const char * prefix ;
551
+ unsigned int flags ;
552
+ };
553
+
554
+ #define STATUS_CB_INIT { NULL, 0 }
555
+
556
+ static void print_status (unsigned int flags , char state , const char * path ,
557
+ const struct object_id * oid , const char * displaypath )
558
+ {
559
+ if (flags & OPT_QUIET )
560
+ return ;
561
+
562
+ printf ("%c%s %s" , state , oid_to_hex (oid ), displaypath );
563
+
564
+ if (state == ' ' || state == '+' )
565
+ printf (" (%s)" , compute_rev_name (path , oid_to_hex (oid )));
566
+
567
+ printf ("\n" );
568
+ }
569
+
570
+ static int handle_submodule_head_ref (const char * refname ,
571
+ const struct object_id * oid , int flags ,
572
+ void * cb_data )
573
+ {
574
+ struct object_id * output = cb_data ;
575
+ if (oid )
576
+ oidcpy (output , oid );
577
+
578
+ return 0 ;
579
+ }
580
+
581
+ static void status_submodule (const char * path , const struct object_id * ce_oid ,
582
+ unsigned int ce_flags , const char * prefix ,
583
+ unsigned int flags )
584
+ {
585
+ char * displaypath ;
586
+ struct argv_array diff_files_args = ARGV_ARRAY_INIT ;
587
+ struct rev_info rev ;
588
+ int diff_files_result ;
589
+
590
+ if (!submodule_from_path (& null_oid , path ))
591
+ die (_ ("no submodule mapping found in .gitmodules for path '%s'" ),
592
+ path );
593
+
594
+ displaypath = get_submodule_displaypath (path , prefix );
595
+
596
+ if ((CE_STAGEMASK & ce_flags ) >> CE_STAGESHIFT ) {
597
+ print_status (flags , 'U' , path , & null_oid , displaypath );
598
+ goto cleanup ;
599
+ }
600
+
601
+ if (!is_submodule_active (the_repository , path )) {
602
+ print_status (flags , '-' , path , ce_oid , displaypath );
603
+ goto cleanup ;
604
+ }
605
+
606
+ argv_array_pushl (& diff_files_args , "diff-files" ,
607
+ "--ignore-submodules=dirty" , "--quiet" , "--" ,
608
+ path , NULL );
609
+
610
+ git_config (git_diff_basic_config , NULL );
611
+ init_revisions (& rev , prefix );
612
+ rev .abbrev = 0 ;
613
+ diff_files_args .argc = setup_revisions (diff_files_args .argc ,
614
+ diff_files_args .argv ,
615
+ & rev , NULL );
616
+ diff_files_result = run_diff_files (& rev , 0 );
617
+
618
+ if (!diff_result_code (& rev .diffopt , diff_files_result )) {
619
+ print_status (flags , ' ' , path , ce_oid ,
620
+ displaypath );
621
+ } else if (!(flags & OPT_CACHED )) {
622
+ struct object_id oid ;
623
+
624
+ if (refs_head_ref (get_submodule_ref_store (path ),
625
+ handle_submodule_head_ref , & oid ))
626
+ die (_ ("could not resolve HEAD ref inside the"
627
+ "submodule '%s'" ), path );
628
+
629
+ print_status (flags , '+' , path , & oid , displaypath );
630
+ } else {
631
+ print_status (flags , '+' , path , ce_oid , displaypath );
632
+ }
633
+
634
+ if (flags & OPT_RECURSIVE ) {
635
+ struct child_process cpr = CHILD_PROCESS_INIT ;
636
+
637
+ cpr .git_cmd = 1 ;
638
+ cpr .dir = path ;
639
+ prepare_submodule_repo_env (& cpr .env_array );
640
+
641
+ argv_array_push (& cpr .args , "--super-prefix" );
642
+ argv_array_pushf (& cpr .args , "%s/" , displaypath );
643
+ argv_array_pushl (& cpr .args , "submodule--helper" , "status" ,
644
+ "--recursive" , NULL );
645
+
646
+ if (flags & OPT_CACHED )
647
+ argv_array_push (& cpr .args , "--cached" );
648
+
649
+ if (flags & OPT_QUIET )
650
+ argv_array_push (& cpr .args , "--quiet" );
651
+
652
+ if (run_command (& cpr ))
653
+ die (_ ("failed to recurse into submodule '%s'" ), path );
654
+ }
655
+
656
+ cleanup :
657
+ argv_array_clear (& diff_files_args );
658
+ free (displaypath );
659
+ }
660
+
661
+ static void status_submodule_cb (const struct cache_entry * list_item ,
662
+ void * cb_data )
663
+ {
664
+ struct status_cb * info = cb_data ;
665
+ status_submodule (list_item -> name , & list_item -> oid , list_item -> ce_flags ,
666
+ info -> prefix , info -> flags );
667
+ }
668
+
669
+ static int module_status (int argc , const char * * argv , const char * prefix )
670
+ {
671
+ struct status_cb info = STATUS_CB_INIT ;
672
+ struct pathspec pathspec ;
673
+ struct module_list list = MODULE_LIST_INIT ;
674
+ int quiet = 0 ;
675
+
676
+ struct option module_status_options [] = {
677
+ OPT__QUIET (& quiet , N_ ("Suppress submodule status output" )),
678
+ OPT_BIT (0 , "cached" , & info .flags , N_ ("Use commit stored in the index instead of the one stored in the submodule HEAD" ), OPT_CACHED ),
679
+ OPT_BIT (0 , "recursive" , & info .flags , N_ ("recurse into nested submodules" ), OPT_RECURSIVE ),
680
+ OPT_END ()
681
+ };
682
+
683
+ const char * const git_submodule_helper_usage [] = {
684
+ N_ ("git submodule status [--quiet] [--cached] [--recursive] [<path>...]" ),
685
+ NULL
686
+ };
687
+
688
+ argc = parse_options (argc , argv , prefix , module_status_options ,
689
+ git_submodule_helper_usage , 0 );
690
+
691
+ if (module_list_compute (argc , argv , prefix , & pathspec , & list ) < 0 )
692
+ return 1 ;
693
+
694
+ info .prefix = prefix ;
695
+ if (quiet )
696
+ info .flags |= OPT_QUIET ;
697
+
698
+ for_each_listed_submodule (& list , status_submodule_cb , & info );
461
699
462
700
return 0 ;
463
701
}
@@ -1259,6 +1497,7 @@ static struct cmd_struct commands[] = {
1259
1497
{"resolve-relative-url" , resolve_relative_url , 0 },
1260
1498
{"resolve-relative-url-test" , resolve_relative_url_test , 0 },
1261
1499
{"init" , module_init , SUPPORT_SUPER_PREFIX },
1500
+ {"status" , module_status , SUPPORT_SUPER_PREFIX },
1262
1501
{"remote-branch" , resolve_remote_submodule_branch , 0 },
1263
1502
{"push-check" , push_check , 0 },
1264
1503
{"absorb-git-dirs" , absorb_git_dirs , SUPPORT_SUPER_PREFIX },
0 commit comments