This aims to make git-submodule 'status' a built-in. Hence, the function
cmd_status() is ported from shell to C. This is done by introducing
four functions: module_status(), submodule_status_cb(),
submodule_status() and print_status().

The function module_status() acts as the front-end of the subcommand.
It parses subcommand's options and then calls the function
module_list_compute() for computing the list of submodules. Then
this functions calls for_each_listed_submodule() looping through the
list obtained.

Then for_each_listed_submodule() calls submodule_status_cb() for each of
the submodule in its list. The function submodule_status_cb() calls
submodule_status() after passing appropriate arguments to the funciton.
Function submodule_status() is responsible for generating the status
each submodule it is called for, and then calls print_status().

Finally, the function print_status() handles the printing of submodule's
status.

Function set_name_rev() is also ported from git-submodule to the
submodule--helper builtin function compute_rev_name(), which now
generates the value of the revision name as required.

Mentored-by: Christian Couder <christian.cou...@gmail.com>
Mentored-by: Stefan Beller <sbel...@google.com>
Signed-off-by: Prathamesh Chavan <pc44...@gmail.com>
---
 builtin/submodule--helper.c | 207 ++++++++++++++++++++++++++++++++++++++++++++
 git-submodule.sh            |  61 +------------
 2 files changed, 208 insertions(+), 60 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 20a1ef868..50d38fc20 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -13,8 +13,13 @@
 #include "remote.h"
 #include "refs.h"
 #include "connect.h"
+#include "revision.h"
+#include "diffcore.h"
+#include "diff.h"
 
 #define CB_OPT_QUIET           (1<<0)
+#define CB_OPT_CACHED          (1<<1)
+#define CB_OPT_RECURSIVE       (1<<2)
 
 typedef void (*each_submodule_fn)(const struct cache_entry *list_item,
                                  void *cb_data);
@@ -245,6 +250,53 @@ static char *get_submodule_displaypath(const char *path, 
const char *prefix)
        }
 }
 
+static char *compute_rev_name(const char *sub_path, const char* object_id)
+{
+       struct strbuf sb = STRBUF_INIT;
+       const char ***d;
+
+       static const char *describe_bare[] = {
+               NULL
+       };
+
+       static const char *describe_tags[] = {
+               "--tags", NULL
+       };
+
+       static const char *describe_contains[] = {
+               "--contains", NULL
+       };
+
+       static const char *describe_all_always[] = {
+               "--all", "--always", NULL
+       };
+
+       static const char **describe_argv[] = {
+               describe_bare, describe_tags, describe_contains,
+               describe_all_always, NULL
+       };
+
+       for (d = describe_argv; *d; d++) {
+               struct child_process cp = CHILD_PROCESS_INIT;
+               prepare_submodule_repo_env(&cp.env_array);
+               cp.dir = sub_path;
+               cp.git_cmd = 1;
+               cp.no_stderr = 1;
+
+               argv_array_push(&cp.args, "describe");
+               argv_array_pushv(&cp.args, *d);
+               argv_array_push(&cp.args, object_id);
+
+               if (!capture_command(&cp, &sb, 0)) {
+                       strbuf_strip_suffix(&sb, "\n");
+                       return strbuf_detach(&sb, NULL);
+               }
+       }
+
+       strbuf_release(&sb);
+       return NULL;
+}
+
 struct module_list {
        const struct cache_entry **entries;
        int alloc, nr;
@@ -503,6 +555,160 @@ static int module_init(int argc, const char **argv, const 
char *prefix)
        return 0;
 }
 
+struct status_cb {
+       const char *prefix;
+       unsigned int cb_flags;
+};
+#define STATUS_CB_INIT { NULL, 0 }
+
+static void print_status(unsigned int flags, char state, const char *path,
+                        const struct object_id *oid, const char *displaypath)
+{
+       if (flags & CB_OPT_QUIET)
+               return;
+
+       printf("%c%s %s", state, oid_to_hex(oid), displaypath);
+
+       if (state == ' ' || state == '+')
+               printf(" (%s)", compute_rev_name(path, oid_to_hex(oid)));
+
+       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 char *path, const struct object_id *ce_oid,
+                            unsigned int ce_flags, const char *prefix,
+                            unsigned int cb_flags)
+{
+       char *displaypath;
+       struct argv_array diff_files_args = ARGV_ARRAY_INIT;
+       struct rev_info rev;
+       int diff_files_result;
+
+       if (!submodule_from_path(&null_oid, path))
+               die(_("no submodule mapping found in .gitmodules for path 
'%s'"),
+                     path);
+
+       displaypath = get_submodule_displaypath(path, prefix);
+
+       if ((CE_STAGEMASK & ce_flags) >> CE_STAGESHIFT) {
+               print_status(cb_flags, 'U', path, &null_oid, displaypath);
+               goto cleanup;
+       }
+
+       if (!is_submodule_active(the_repository, path)) {
+               print_status(cb_flags, '-', path, ce_oid, displaypath);
+               goto cleanup;
+       }
+
+       argv_array_pushl(&diff_files_args, "diff-files",
+                        "--ignore-submodules=dirty", "--quiet", "--",
+                        path, NULL);
+
+       git_config(git_diff_basic_config, NULL);
+       init_revisions(&rev, prefix);
+       rev.abbrev = 0;
+       precompose_argv(diff_files_args.argc, diff_files_args.argv);
+       diff_files_args.argc = setup_revisions(diff_files_args.argc,
+                                              diff_files_args.argv,
+                                              &rev, NULL);
+       diff_files_result = run_diff_files(&rev, 0);
+
+       if (!diff_result_code(&rev.diffopt, diff_files_result)) {
+               print_status(cb_flags, ' ', path, ce_oid,
+                            displaypath);
+       } else if (!(cb_flags & CB_OPT_CACHED)) {
+               struct object_id oid;
+
+               if (refs_head_ref(get_submodule_ref_store(path),
+                                 handle_submodule_head_ref, &oid))
+                       die(_("could not resolve HEAD ref inside the"
+                             "submodule '%s'"), path);
+
+               print_status(cb_flags, '+', path, &oid, displaypath);
+       } else {
+               print_status(cb_flags, '+', path, ce_oid, displaypath);
+       }
+
+       if (cb_flags & CB_OPT_RECURSIVE) {
+               struct child_process cpr = CHILD_PROCESS_INIT;
+
+               cpr.git_cmd = 1;
+               cpr.dir = path;
+               prepare_submodule_repo_env(&cpr.env_array);
+
+               argv_array_push(&cpr.args, "--super-prefix");
+               argv_array_pushf(&cpr.args, "%s/", displaypath);
+               argv_array_pushl(&cpr.args, "submodule--helper", "status",
+                                "--recursive", NULL);
+
+               if (cb_flags & CB_OPT_CACHED)
+                       argv_array_push(&cpr.args, "--cached");
+
+               if (cb_flags & CB_OPT_QUIET)
+                       argv_array_push(&cpr.args, "--quiet");
+
+               if (run_command(&cpr))
+                       die(_("failed to recurse into submodule '%s'"), path);
+       }
+
+cleanup:
+       argv_array_clear(&diff_files_args);
+       free(displaypath);
+}
+
+static void status_submodule_cb(const struct cache_entry *list_item,
+                               void *cb_data)
+{
+       struct status_cb *info = cb_data;
+       status_submodule(list_item->name, &list_item->oid, list_item->ce_flags,
+                        info->prefix, info->cb_flags);
+}
+
+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;
+
+       struct option module_status_options[] = {
+               OPT__QUIET(&quiet, N_("Suppress submodule status output")),
+               OPT_BIT(0, "cached", &info.cb_flags, N_("Use commit stored in 
the index instead of the one stored in the submodule HEAD"), CB_OPT_CACHED),
+               OPT_BIT(0, "recursive", &info.cb_flags, N_("recurse into nested 
submodules"), CB_OPT_RECURSIVE),
+               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;
+       if (quiet)
+               info.cb_flags |= CB_OPT_QUIET;
+
+       for_each_listed_submodule(&list, status_submodule_cb, &info);
+
+       return 0;
+}
+
 static int module_name(int argc, const char **argv, const char *prefix)
 {
        const struct submodule *sub;
@@ -1300,6 +1506,7 @@ static struct cmd_struct commands[] = {
        {"resolve-relative-url", resolve_relative_url, 0},
        {"resolve-relative-url-test", resolve_relative_url_test, 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},
diff --git a/git-submodule.sh b/git-submodule.sh
index 66d1ae8ef..156255a9e 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -758,18 +758,6 @@ cmd_update()
        }
 }
 
-set_name_rev () {
-       revname=$( (
-               sanitize_submodule_env
-               cd "$1" && {
-                       git describe "$2" 2>/dev/null ||
-                       git describe --tags "$2" 2>/dev/null ||
-                       git describe --contains "$2" 2>/dev/null ||
-                       git describe --all --always "$2"
-               }
-       ) )
-       test -z "$revname" || revname=" ($revname)"
-}
 #
 # Show commit summary for submodules in index or working tree
 #
@@ -1016,54 +1004,7 @@ cmd_status()
                shift
        done
 
-       {
-               git submodule--helper list --prefix "$wt_prefix" "$@" ||
-               echo "#unmatched" $?
-       } |
-       while read -r mode sha1 stage sm_path
-       do
-               die_if_unmatched "$mode" "$sha1"
-               name=$(git submodule--helper name "$sm_path") || exit
-               displaypath=$(git submodule--helper relative-path 
"$prefix$sm_path" "$wt_prefix")
-               if test "$stage" = U
-               then
-                       say "U$sha1 $displaypath"
-                       continue
-               fi
-               if ! git submodule--helper is-active "$sm_path" ||
-               {
-                       ! test -d "$sm_path"/.git &&
-                       ! test -f "$sm_path"/.git
-               }
-               then
-                       say "-$sha1 $displaypath"
-                       continue;
-               fi
-               if git diff-files --ignore-submodules=dirty --quiet -- 
"$sm_path"
-               then
-                       set_name_rev "$sm_path" "$sha1"
-                       say " $sha1 $displaypath$revname"
-               else
-                       if test -z "$cached"
-                       then
-                               sha1=$(sanitize_submodule_env; cd "$sm_path" && 
git rev-parse --verify HEAD)
-                       fi
-                       set_name_rev "$sm_path" "$sha1"
-                       say "+$sha1 $displaypath$revname"
-               fi
-
-               if test -n "$recursive"
-               then
-                       (
-                               prefix="$displaypath/"
-                               sanitize_submodule_env
-                               wt_prefix=
-                               cd "$sm_path" &&
-                               eval cmd_status
-                       ) ||
-                       die "$(eval_gettext "Failed to recurse into submodule 
path '\$sm_path'")"
-               fi
-       done
+       git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} 
submodule--helper status ${GIT_QUIET:+--quiet} ${cached:+--cached} 
${recursive:+--recursive} "$@"
 }
 #
 # Sync remote urls for submodules
-- 
2.13.0

Reply via email to