1313#include "remote.h"
1414#include "refs.h"
1515#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 );
1626
1727static char * get_default_remote (void )
1828{
@@ -219,6 +229,64 @@ static int resolve_relative_url_test(int argc, const char **argv, const char *pr
219229 return 0 ;
220230}
221231
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+
222290struct module_list {
223291 const struct cache_entry * * entries ;
224292 int alloc , nr ;
@@ -328,21 +396,29 @@ static int module_list(int argc, const char **argv, const char *prefix)
328396 return 0 ;
329397}
330398
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 )
332416{
333417 const struct submodule * sub ;
334418 struct strbuf sb = STRBUF_INIT ;
335419 char * upd = NULL , * url = NULL , * displaypath ;
336420
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 );
346422
347423 sub = submodule_from_path (& null_oid , path );
348424
@@ -357,17 +433,16 @@ static void init_submodule(const char *path, const char *prefix, int quiet)
357433 * Set active flag for the submodule being initialized
358434 */
359435 if (!is_submodule_active (the_repository , path )) {
360- strbuf_reset (& sb );
361436 strbuf_addf (& sb , "submodule.%s.active" , sub -> name );
362437 git_config_set_gently (sb .buf , "true" );
438+ strbuf_reset (& sb );
363439 }
364440
365441 /*
366442 * Copy url setting when it is not set yet.
367443 * To look up the url in .git/config, we must not fall back to
368444 * .gitmodules, so look it up directly.
369445 */
370- strbuf_reset (& sb );
371446 strbuf_addf (& sb , "submodule.%s.url" , sub -> name );
372447 if (git_config_get_string (sb .buf , & url )) {
373448 if (!sub -> url )
@@ -399,14 +474,14 @@ static void init_submodule(const char *path, const char *prefix, int quiet)
399474 if (git_config_set_gently (sb .buf , url ))
400475 die (_ ("Failed to register url for submodule path '%s'" ),
401476 displaypath );
402- if (!quiet )
477+ if (!( flags & OPT_QUIET ) )
403478 fprintf (stderr ,
404479 _ ("Submodule '%s' (%s) registered for path '%s'\n" ),
405480 sub -> name , url , displaypath );
406481 }
482+ strbuf_reset (& sb );
407483
408484 /* Copy "update" setting when it is not set yet */
409- strbuf_reset (& sb );
410485 strbuf_addf (& sb , "submodule.%s.update" , sub -> name );
411486 if (git_config_get_string (sb .buf , & upd ) &&
412487 sub -> update_strategy .type != SM_UPDATE_UNSPECIFIED ) {
@@ -426,12 +501,18 @@ static void init_submodule(const char *path, const char *prefix, int quiet)
426501 free (upd );
427502}
428503
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+
429510static int module_init (int argc , const char * * argv , const char * prefix )
430511{
512+ struct init_cb info = INIT_CB_INIT ;
431513 struct pathspec pathspec ;
432514 struct module_list list = MODULE_LIST_INIT ;
433515 int quiet = 0 ;
434- int i ;
435516
436517 struct option module_init_options [] = {
437518 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)
456537 if (!argc && git_config_get_value_multi ("submodule.active" ))
457538 module_list_active (& list );
458539
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 );
461699
462700 return 0 ;
463701}
@@ -1259,6 +1497,7 @@ static struct cmd_struct commands[] = {
12591497 {"resolve-relative-url" , resolve_relative_url , 0 },
12601498 {"resolve-relative-url-test" , resolve_relative_url_test , 0 },
12611499 {"init" , module_init , SUPPORT_SUPER_PREFIX },
1500+ {"status" , module_status , SUPPORT_SUPER_PREFIX },
12621501 {"remote-branch" , resolve_remote_submodule_branch , 0 },
12631502 {"push-check" , push_check , 0 },
12641503 {"absorb-git-dirs" , absorb_git_dirs , SUPPORT_SUPER_PREFIX },
0 commit comments