|
20 | 20 | #define OPT_QUIET (1 << 0)
|
21 | 21 | #define OPT_CACHED (1 << 1)
|
22 | 22 | #define OPT_RECURSIVE (1 << 2)
|
| 23 | +#define OPT_FORCE (1 << 3) |
23 | 24 |
|
24 | 25 | typedef void (*each_submodule_fn)(const struct cache_entry *list_item,
|
25 | 26 | void *cb_data);
|
@@ -909,6 +910,151 @@ static int module_sync(int argc, const char **argv, const char *prefix)
|
909 | 910 | return 0;
|
910 | 911 | }
|
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 | + |
912 | 1058 | static int clone_submodule(const char *path, const char *gitdir, const char *url,
|
913 | 1059 | const char *depth, struct string_list *reference,
|
914 | 1060 | int quiet, int progress)
|
@@ -1691,6 +1837,7 @@ static struct cmd_struct commands[] = {
|
1691 | 1837 | {"status", module_status, SUPPORT_SUPER_PREFIX},
|
1692 | 1838 | {"print-default-remote", print_default_remote, 0},
|
1693 | 1839 | {"sync", module_sync, SUPPORT_SUPER_PREFIX},
|
| 1840 | + {"deinit", module_deinit, 0}, |
1694 | 1841 | {"remote-branch", resolve_remote_submodule_branch, 0},
|
1695 | 1842 | {"push-check", push_check, 0},
|
1696 | 1843 | {"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
|
|
0 commit comments