submodule: port submodule subcommand 'status' from shell to C

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.couder@gmail.com> Mentored-by: Stefan Beller <sbeller@google.com> Signed-off-by: Prathamesh Chavan <pc44800@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Prathamesh Chavan committed Oct 6, 2017 at 18:54 UTC a9f8a37584a6d3e4702ce115476bebda20d871d3
2 files changed +199 -60
builtin/submodule--helper.c
+198
@@ -13,8 +13,13 @@
13 #include "remote.h"
14 #include "refs.h"
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);
@@ -245,6 +250,44 @@ static char *get_submodule_displaypath(const char *path, const char *prefix)
250 }
251 }
252
253 +static char *compute_rev_name(const char *sub_path, const char* object_id)
254 +{
255 + struct strbuf sb = STRBUF_INIT;
256 + const char ***d;
257 +
258 + static const char *describe_bare[] = { NULL };
259 +
260 + static const char *describe_tags[] = { "--tags", NULL };
261 +
262 + static const char *describe_contains[] = { "--contains", NULL };
263 +
264 + static const char *describe_all_always[] = { "--all", "--always", NULL };
265 +
266 + static const char **describe_argv[] = { describe_bare, describe_tags,
267 + describe_contains,
268 + describe_all_always, NULL };
269 +
270 + for (d = describe_argv; *d; d++) {
271 + struct child_process cp = CHILD_PROCESS_INIT;
272 + prepare_submodule_repo_env(&cp.env_array);
273 + cp.dir = sub_path;
274 + cp.git_cmd = 1;
275 + cp.no_stderr = 1;
276 +
277 + argv_array_push(&cp.args, "describe");
278 + argv_array_pushv(&cp.args, *d);
279 + argv_array_push(&cp.args, object_id);
280 +
281 + if (!capture_command(&cp, &sb, 0)) {
282 + strbuf_strip_suffix(&sb, "\n");
283 + return strbuf_detach(&sb, NULL);
284 + }
285 + }
286 +
287 + strbuf_release(&sb);
288 + return NULL;
289 +}
290 +
291 struct module_list {
292 const struct cache_entry **entries;
293 int alloc, nr;
@@ -504,6 +547,160 @@ static int module_init(int argc, const char **argv, const char *prefix)
547 return 0;
548 }
549
550 +struct status_cb {
551 + const char *prefix;
552 + unsigned int flags;
553 +};
554 +
555 +#define STATUS_CB_INIT { NULL, 0 }
556 +
557 +static void print_status(unsigned int flags, char state, const char *path,
558 + const struct object_id *oid, const char *displaypath)
559 +{
560 + if (flags & OPT_QUIET)
561 + return;
562 +
563 + printf("%c%s %s", state, oid_to_hex(oid), displaypath);
564 +
565 + if (state == ' ' || state == '+')
566 + printf(" (%s)", compute_rev_name(path, oid_to_hex(oid)));
567 +
568 + printf("\n");
569 +}
570 +
571 +static int handle_submodule_head_ref(const char *refname,
572 + const struct object_id *oid, int flags,
573 + void *cb_data)
574 +{
575 + struct object_id *output = cb_data;
576 + if (oid)
577 + oidcpy(output, oid);
578 +
579 + return 0;
580 +}
581 +
582 +static void status_submodule(const char *path, const struct object_id *ce_oid,
583 + unsigned int ce_flags, const char *prefix,
584 + unsigned int flags)
585 +{
586 + char *displaypath;
587 + struct argv_array diff_files_args = ARGV_ARRAY_INIT;
588 + struct rev_info rev;
589 + int diff_files_result;
590 +
591 + if (!submodule_from_path(&null_oid, path))
592 + die(_("no submodule mapping found in .gitmodules for path '%s'"),
593 + path);
594 +
595 + displaypath = get_submodule_displaypath(path, prefix);
596 +
597 + if ((CE_STAGEMASK & ce_flags) >> CE_STAGESHIFT) {
598 + print_status(flags, 'U', path, &null_oid, displaypath);
599 + goto cleanup;
600 + }
601 +
602 + if (!is_submodule_active(the_repository, path)) {
603 + print_status(flags, '-', path, ce_oid, displaypath);
604 + goto cleanup;
605 + }
606 +
607 + argv_array_pushl(&diff_files_args, "diff-files",
608 + "--ignore-submodules=dirty", "--quiet", "--",
609 + path, NULL);
610 +
611 + git_config(git_diff_basic_config, NULL);
612 + init_revisions(&rev, prefix);
613 + rev.abbrev = 0;
614 + diff_files_args.argc = setup_revisions(diff_files_args.argc,
615 + diff_files_args.argv,
616 + &rev, NULL);
617 + diff_files_result = run_diff_files(&rev, 0);
618 +
619 + if (!diff_result_code(&rev.diffopt, diff_files_result)) {
620 + print_status(flags, ' ', path, ce_oid,
621 + displaypath);
622 + } else if (!(flags & OPT_CACHED)) {
623 + struct object_id oid;
624 +
625 + if (refs_head_ref(get_submodule_ref_store(path),
626 + handle_submodule_head_ref, &oid))
627 + die(_("could not resolve HEAD ref inside the"
628 + "submodule '%s'"), path);
629 +
630 + print_status(flags, '+', path, &oid, displaypath);
631 + } else {
632 + print_status(flags, '+', path, ce_oid, displaypath);
633 + }
634 +
635 + if (flags & OPT_RECURSIVE) {
636 + struct child_process cpr = CHILD_PROCESS_INIT;
637 +
638 + cpr.git_cmd = 1;
639 + cpr.dir = path;
640 + prepare_submodule_repo_env(&cpr.env_array);
641 +
642 + argv_array_push(&cpr.args, "--super-prefix");
643 + argv_array_pushf(&cpr.args, "%s/", displaypath);
644 + argv_array_pushl(&cpr.args, "submodule--helper", "status",
645 + "--recursive", NULL);
646 +
647 + if (flags & OPT_CACHED)
648 + argv_array_push(&cpr.args, "--cached");
649 +
650 + if (flags & OPT_QUIET)
651 + argv_array_push(&cpr.args, "--quiet");
652 +
653 + if (run_command(&cpr))
654 + die(_("failed to recurse into submodule '%s'"), path);
655 + }
656 +
657 +cleanup:
658 + argv_array_clear(&diff_files_args);
659 + free(displaypath);
660 +}
661 +
662 +static void status_submodule_cb(const struct cache_entry *list_item,
663 + void *cb_data)
664 +{
665 + struct status_cb *info = cb_data;
666 + status_submodule(list_item->name, &list_item->oid, list_item->ce_flags,
667 + info->prefix, info->flags);
668 +}
669 +
670 +static int module_status(int argc, const char **argv, const char *prefix)
671 +{
672 + struct status_cb info = STATUS_CB_INIT;
673 + struct pathspec pathspec;
674 + struct module_list list = MODULE_LIST_INIT;
675 + int quiet = 0;
676 +
677 + struct option module_status_options[] = {
678 + OPT__QUIET(&quiet, N_("Suppress submodule status output")),
679 + OPT_BIT(0, "cached", &info.flags, N_("Use commit stored in the index instead of the one stored in the submodule HEAD"), OPT_CACHED),
680 + OPT_BIT(0, "recursive", &info.flags, N_("recurse into nested submodules"), OPT_RECURSIVE),
681 + OPT_END()
682 + };
683 +
684 + const char *const git_submodule_helper_usage[] = {
685 + N_("git submodule status [--quiet] [--cached] [--recursive] [<path>...]"),
686 + NULL
687 + };
688 +
689 + argc = parse_options(argc, argv, prefix, module_status_options,
690 + git_submodule_helper_usage, 0);
691 +
692 + if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
693 + return 1;
694 +
695 + info.prefix = prefix;
696 + if (quiet)
697 + info.flags |= OPT_QUIET;
698 +
699 + for_each_listed_submodule(&list, status_submodule_cb, &info);
700 +
701 + return 0;
702 +}
703 +
704 static int module_name(int argc, const char **argv, const char *prefix)
705 {
706 const struct submodule *sub;
@@ -1301,6 +1498,7 @@ static struct cmd_struct commands[] = {
1498 {"resolve-relative-url", resolve_relative_url, 0},
1499 {"resolve-relative-url-test", resolve_relative_url_test, 0},
1500 {"init", module_init, SUPPORT_SUPER_PREFIX},
1501 + {"status", module_status, SUPPORT_SUPER_PREFIX},
1502 {"remote-branch", resolve_remote_submodule_branch, 0},
1503 {"push-check", push_check, 0},
1504 {"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
git-submodule.sh
+1 -60
@@ -758,18 +758,6 @@ cmd_update()
758 }
759 }
760
761 -set_name_rev () {
762 - revname=$( (
763 - sanitize_submodule_env
764 - cd "$1" && {
765 - git describe "$2" 2>/dev/null ||
766 - git describe --tags "$2" 2>/dev/null ||
767 - git describe --contains "$2" 2>/dev/null ||
768 - git describe --all --always "$2"
769 - }
770 - ) )
771 - test -z "$revname" || revname=" ($revname)"
772 -}
761 #
762 # Show commit summary for submodules in index or working tree
763 #
@@ -1016,54 +1004,7 @@ cmd_status()
1004 shift
1005 done
1006
1019 - {
1020 - git submodule--helper list --prefix "$wt_prefix" "$@" ||
1021 - echo "#unmatched" $?
1022 - } |
1023 - while read -r mode sha1 stage sm_path
1024 - do
1025 - die_if_unmatched "$mode" "$sha1"
1026 - name=$(git submodule--helper name "$sm_path") || exit
1027 - displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
1028 - if test "$stage" = U
1029 - then
1030 - say "U$sha1 $displaypath"
1031 - continue
1032 - fi
1033 - if ! git submodule--helper is-active "$sm_path" ||
1034 - {
1035 - ! test -d "$sm_path"/.git &&
1036 - ! test -f "$sm_path"/.git
1037 - }
1038 - then
1039 - say "-$sha1 $displaypath"
1040 - continue;
1041 - fi
1042 - if git diff-files --ignore-submodules=dirty --quiet -- "$sm_path"
1043 - then
1044 - set_name_rev "$sm_path" "$sha1"
1045 - say " $sha1 $displaypath$revname"
1046 - else
1047 - if test -z "$cached"
1048 - then
1049 - sha1=$(sanitize_submodule_env; cd "$sm_path" && git rev-parse --verify HEAD)
1050 - fi
1051 - set_name_rev "$sm_path" "$sha1"
1052 - say "+$sha1 $displaypath$revname"
1053 - fi
1054 -
1055 - if test -n "$recursive"
1056 - then
1057 - (
1058 - prefix="$displaypath/"
1059 - sanitize_submodule_env
1060 - wt_prefix=
1061 - cd "$sm_path" &&
1062 - eval cmd_status
1063 - ) ||
1064 - die "$(eval_gettext "Failed to recurse into submodule path '\$sm_path'")"
1065 - fi
1066 - done
1007 + git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper status ${GIT_QUIET:+--quiet} ${cached:+--cached} ${recursive:+--recursive} "$@"
1008 }
1009 #
1010 # Sync remote urls for submodules