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 () (