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

The same mechanism is used even for porting this submodule subcommand, as used in the ported subcommands till now. The function cmd_deinit in split up after porting into four functions: module_deinit(), for_each_listed_submodule(), deinit_submodule() and deinit_submodule_cb(). 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 Jan 15, 2018 at 02:45 UTC 2e612731b55f1a83fb5b7f4ecb9391f0cba63cb2
2 files changed +148 -54
builtin/submodule--helper.c
+147
@@ -20,6 +20,7 @@
20 #define OPT_QUIET (1 << 0)
21 #define OPT_CACHED (1 << 1)
22 #define OPT_RECURSIVE (1 << 2)
23 +#define OPT_FORCE (1 << 3)
24
25 typedef void (*each_submodule_fn)(const struct cache_entry *list_item,
26 void *cb_data);
@@ -909,6 +910,151 @@ static int module_sync(int argc, const char **argv, const char *prefix)
910 return 0;
911 }
912
913 +struct deinit_cb {
914 + const char *prefix;
915 + unsigned int flags;
916 +};
917 +#define DEINIT_CB_INIT { NULL, 0 }
918 +
919 +static void deinit_submodule(const char *path, const char *prefix,
920 + unsigned int flags)
921 +{
922 + const struct submodule *sub;
923 + char *displaypath = NULL;
924 + struct child_process cp_config = CHILD_PROCESS_INIT;
925 + struct strbuf sb_config = STRBUF_INIT;
926 + char *sub_git_dir = xstrfmt("%s/.git", path);
927 +
928 + sub = submodule_from_path(&null_oid, path);
929 +
930 + if (!sub || !sub->name)
931 + goto cleanup;
932 +
933 + displaypath = get_submodule_displaypath(path, prefix);
934 +
935 + /* remove the submodule work tree (unless the user already did it) */
936 + if (is_directory(path)) {
937 + struct strbuf sb_rm = STRBUF_INIT;
938 + const char *format;
939 +
940 + /*
941 + * protect submodules containing a .git directory
942 + * NEEDSWORK: instead of dying, automatically call
943 + * absorbgitdirs and (possibly) warn.
944 + */
945 + if (is_directory(sub_git_dir))
946 + die(_("Submodule work tree '%s' contains a .git "
947 + "directory (use 'rm -rf' if you really want "
948 + "to remove it including all of its history)"),
949 + displaypath);
950 +
951 + if (!(flags & OPT_FORCE)) {
952 + struct child_process cp_rm = CHILD_PROCESS_INIT;
953 + cp_rm.git_cmd = 1;
954 + argv_array_pushl(&cp_rm.args, "rm", "-qn",
955 + path, NULL);
956 +
957 + if (run_command(&cp_rm))
958 + die(_("Submodule work tree '%s' contains local "
959 + "modifications; use '-f' to discard them"),
960 + displaypath);
961 + }
962 +
963 + strbuf_addstr(&sb_rm, path);
964 +
965 + if (!remove_dir_recursively(&sb_rm, 0))
966 + format = _("Cleared directory '%s'\n");
967 + else
968 + format = _("Could not remove submodule work tree '%s'\n");
969 +
970 + if (!(flags & OPT_QUIET))
971 + printf(format, displaypath);
972 +
973 + strbuf_release(&sb_rm);
974 + }
975 +
976 + if (mkdir(path, 0777))
977 + printf(_("could not create empty submodule directory %s"),
978 + displaypath);
979 +
980 + cp_config.git_cmd = 1;
981 + argv_array_pushl(&cp_config.args, "config", "--get-regexp", NULL);
982 + argv_array_pushf(&cp_config.args, "submodule.%s\\.", sub->name);
983 +
984 + /* remove the .git/config entries (unless the user already did it) */
985 + if (!capture_command(&cp_config, &sb_config, 0) && sb_config.len) {
986 + char *sub_key = xstrfmt("submodule.%s", sub->name);
987 + /*
988 + * remove the whole section so we have a clean state when
989 + * the user later decides to init this submodule again
990 + */
991 + git_config_rename_section_in_file(NULL, sub_key, NULL);
992 + if (!(flags & OPT_QUIET))
993 + printf(_("Submodule '%s' (%s) unregistered for path '%s'\n"),
994 + sub->name, sub->url, displaypath);
995 + free(sub_key);
996 + }
997 +
998 +cleanup:
999 + free(displaypath);
1000 + free(sub_git_dir);
1001 + strbuf_release(&sb_config);
1002 +}
1003 +
1004 +static void deinit_submodule_cb(const struct cache_entry *list_item,
1005 + void *cb_data)
1006 +{
1007 + struct deinit_cb *info = cb_data;
1008 + deinit_submodule(list_item->name, info->prefix, info->flags);
1009 +}
1010 +
1011 +static int module_deinit(int argc, const char **argv, const char *prefix)
1012 +{
1013 + struct deinit_cb info = DEINIT_CB_INIT;
1014 + struct pathspec pathspec;
1015 + struct module_list list = MODULE_LIST_INIT;
1016 + int quiet = 0;
1017 + int force = 0;
1018 + int all = 0;
1019 +
1020 + struct option module_deinit_options[] = {
1021 + OPT__QUIET(&quiet, N_("Suppress submodule status output")),
1022 + OPT__FORCE(&force, N_("Remove submodule working trees even if they contain local changes")),
1023 + OPT_BOOL(0, "all", &all, N_("Unregister all submodules")),
1024 + OPT_END()
1025 + };
1026 +
1027 + const char *const git_submodule_helper_usage[] = {
1028 + N_("git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]"),
1029 + NULL
1030 + };
1031 +
1032 + argc = parse_options(argc, argv, prefix, module_deinit_options,
1033 + git_submodule_helper_usage, 0);
1034 +
1035 + if (all && argc) {
1036 + error("pathspec and --all are incompatible");
1037 + usage_with_options(git_submodule_helper_usage,
1038 + module_deinit_options);
1039 + }
1040 +
1041 + if (!argc && !all)
1042 + die(_("Use '--all' if you really want to deinitialize all submodules"));
1043 +
1044 + if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
1045 + BUG("module_list_compute should not choke on empty pathspec");
1046 +
1047 + info.prefix = prefix;
1048 + if (quiet)
1049 + info.flags |= OPT_QUIET;
1050 + if (force)
1051 + info.flags |= OPT_FORCE;
1052 +
1053 + for_each_listed_submodule(&list, deinit_submodule_cb, &info);
1054 +
1055 + return 0;
1056 +}
1057 +
1058 static int clone_submodule(const char *path, const char *gitdir, const char *url,
1059 const char *depth, struct string_list *reference,
1060 int quiet, int progress)
@@ -1691,6 +1837,7 @@ static struct cmd_struct commands[] = {
1837 {"status", module_status, SUPPORT_SUPER_PREFIX},
1838 {"print-default-remote", print_default_remote, 0},
1839 {"sync", module_sync, SUPPORT_SUPER_PREFIX},
1840 + {"deinit", module_deinit, 0},
1841 {"remote-branch", resolve_remote_submodule_branch, 0},
1842 {"push-check", push_check, 0},
1843 {"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
git-submodule.sh
+1 -54
@@ -428,60 +428,7 @@ cmd_deinit()
428 shift
429 done
430
431 - if test -n "$deinit_all" && test "$#" -ne 0
432 - then
433 - echo >&2 "$(eval_gettext "pathspec and --all are incompatible")"
434 - usage
435 - fi
436 - if test $# = 0 && test -z "$deinit_all"
437 - then
438 - die "$(eval_gettext "Use '--all' if you really want to deinitialize all submodules")"
439 - fi
440 -
441 - {
442 - git submodule--helper list --prefix "$wt_prefix" "$@" ||
443 - echo "#unmatched" $?
444 - } |
445 - while read -r mode sha1 stage sm_path
446 - do
447 - die_if_unmatched "$mode" "$sha1"
448 - name=$(git submodule--helper name "$sm_path") || exit
449 -
450 - displaypath=$(git submodule--helper relative-path "$sm_path" "$wt_prefix")
451 -
452 - # Remove the submodule work tree (unless the user already did it)
453 - if test -d "$sm_path"
454 - then
455 - # Protect submodules containing a .git directory
456 - if test -d "$sm_path/.git"
457 - then
458 - die "$(eval_gettext "\
459 -Submodule work tree '\$displaypath' contains a .git directory
460 -(use 'rm -rf' if you really want to remove it including all of its history)")"
461 - fi
462 -
463 - if test -z "$force"
464 - then
465 - git rm -qn "$sm_path" ||
466 - die "$(eval_gettext "Submodule work tree '\$displaypath' contains local modifications; use '-f' to discard them")"
467 - fi
468 - rm -rf "$sm_path" &&
469 - say "$(eval_gettext "Cleared directory '\$displaypath'")" ||
470 - say "$(eval_gettext "Could not remove submodule work tree '\$displaypath'")"
471 - fi
472 -
473 - mkdir "$sm_path" || say "$(eval_gettext "Could not create empty submodule directory '\$displaypath'")"
474 -
475 - # Remove the .git/config entries (unless the user already did it)
476 - if test -n "$(git config --get-regexp submodule."$name\.")"
477 - then
478 - # Remove the whole section so we have a clean state when
479 - # the user later decides to init this submodule again
480 - url=$(git config submodule."$name".url)
481 - git config --remove-section submodule."$name" 2>/dev/null &&
482 - say "$(eval_gettext "Submodule '\$name' (\$url) unregistered for path '\$displaypath'")"
483 - fi
484 - done
431 + git ${wt_prefix:+-C "$wt_prefix"} submodule--helper deinit ${GIT_QUIET:+--quiet} ${prefix:+--prefix "$prefix"} ${force:+--force} ${deinit_all:+--all} "$@"
432 }
433
434 is_tip_reachable () (