@@ -561,6 +561,161 @@ static int module_init(int argc, const char **argv, const char *prefix)
return 0;
}
+struct status_cb {
+ const char *prefix;
+ unsigned int quiet: 1;
+ unsigned int recursive: 1;
+ unsigned int cached: 1;
+};
+#define STATUS_CB_INIT { NULL, 0, 0, 0 }
+
+static void print_status(struct status_cb *info, char state, const char *path,
+ const struct object_id *oid, const char *displaypath)
+{
+ if (info->quiet)
+ return;
+
+ printf("%c%s %s", state, oid_to_hex(oid), displaypath);
+
+ if (state == ' ' || state == '+') {
+ struct argv_array get_rev_args = ARGV_ARRAY_INIT;
+
+ argv_array_pushl(&get_rev_args, "get-rev-name",
+ path, oid_to_hex(oid), NULL);
+ get_rev_name(get_rev_args.argc, get_rev_args.argv,
+ info->prefix);
+
+ argv_array_clear(&get_rev_args);
+ } else {
+ printf("\n");
+ }
+}
+
+static int handle_submodule_head_ref(const char *refname,
+ const struct object_id *oid, int flags,
+ void *cb_data)
+{
+ struct object_id *output = cb_data;
+ if (oid)
+ oidcpy(output, oid);
+
+ return 0;
+}
+
+static void status_submodule(const struct cache_entry *list_item, void *cb_data)
+{
+ struct status_cb *info = cb_data;
+ char *displaypath;
+ struct argv_array diff_files_args = ARGV_ARRAY_INIT;
+
+ if (!submodule_from_path(&null_oid, list_item->name))
+ die(_("no submodule mapping found in .gitmodules for path '%s'"),
+ list_item->name);
+
+ displaypath = get_submodule_displaypath(list_item->name, info->prefix);
+
+ if (ce_stage(list_item)) {
+ print_status(info, 'U', list_item->name,
+ &null_oid, displaypath);
+ goto cleanup;
+ }
+
+ if (!is_submodule_active(the_repository, list_item->name)) {
+ print_status(info, '-', list_item->name, &list_item->oid,
+ displaypath);
+ goto cleanup;
+ }
+
+ argv_array_pushl(&diff_files_args, "diff-files",
+ "--ignore-submodules=dirty", "--quiet", "--",
+ list_item->name, NULL);
+
+ if (!cmd_diff_files(diff_files_args.argc, diff_files_args.argv,
+ info->prefix)) {
+ print_status(info, ' ', list_item->name, &list_item->oid,
+ displaypath);
+ } else {
+ if (!info->cached) {
+ struct object_id oid;
+
+ if (head_ref_submodule(list_item->name,
+ handle_submodule_head_ref, &oid))
+ die(_("could not resolve HEAD ref inside the"
+ "submodule '%s'"), list_item->name);
+
+ print_status(info, '+', list_item->name, &oid,
+ displaypath);
+ } else {
+ print_status(info, '+', list_item->name,
+ &list_item->oid, displaypath);
+ }
+ }
+
+ if (info->recursive) {
+ struct child_process cpr = CHILD_PROCESS_INIT;
+
+ cpr.git_cmd = 1;
+ cpr.dir = list_item->name;
+ prepare_submodule_repo_env(&cpr.env_array);
+
+ argv_array_pushl(&cpr.args, "--super-prefix", displaypath,
+ "submodule--helper", "status", "--recursive",
+ NULL);
+
+ if (info->cached)
+ argv_array_push(&cpr.args, "--cached");
+
+ if (info->quiet)
+ argv_array_push(&cpr.args, "--quiet");
+
+ if (run_command(&cpr))
+ die(_("failed to recurse into submodule '%s'"),
+ list_item->name);
+ }
+
+cleanup:
+ argv_array_clear(&diff_files_args);
+ free(displaypath);
+}
+
+static int module_status(int argc, const char **argv, const char *prefix)
+{
+ struct status_cb info = STATUS_CB_INIT;
+ struct pathspec pathspec;
+ struct module_list list = MODULE_LIST_INIT;
+ int quiet = 0;
+ int cached = 0;
+ int recursive = 0;
+
+ struct option module_status_options[] = {
+ OPT__QUIET(&quiet, N_("Suppress submodule status output")),
+ OPT_BOOL(0, "cached", &cached, N_("Use commit stored in the index instead of the one stored in the submodule HEAD")),
+ OPT_BOOL(0, "recursive", &recursive, N_("Recurse into nested submodules")),
+ OPT_END()
+ };
+
+ const char *const git_submodule_helper_usage[] = {
+ N_("git submodule status [--quiet] [--cached] [--recursive] [<path>]"),
+ NULL
+ };
+
+ argc = parse_options(argc, argv, prefix, module_status_options,
+ git_submodule_helper_usage, 0);
+
+ if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
+ return 1;
+
+ info.prefix = prefix;
+ info.quiet = !!quiet;
+ info.recursive = !!recursive;
+ info.cached = !!cached;
+
+ gitmodules_config();
+ for_each_listed_submodule(&list, status_submodule, &info);
+
+ return 0;
+}
+
static int module_name(int argc, const char **argv, const char *prefix)
{
const struct submodule *sub;@@ -1307,6 +1462,7 @@ static struct cmd_struct commands[] = {
{"resolve-relative-url-test", resolve_relative_url_test, 0},
{"get-rev-name", get_rev_name, 0},
{"init", module_init, SUPPORT_SUPER_PREFIX},
+ {"status", module_status, SUPPORT_SUPER_PREFIX},
{"remote-branch", resolve_remote_submodule_branch, 0},
{"push-check", push_check, 0},
{"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},