Skip to content

Commit

Permalink
submodule: port submodule subcommand 'deinit' from shell to C
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
pratham-pc authored and gitster committed Jan 16, 2018
1 parent 1342476 commit 2e61273
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 54 deletions.
147 changes: 147 additions & 0 deletions builtin/submodule--helper.c
Expand Up @@ -20,6 +20,7 @@
#define OPT_QUIET (1 << 0)
#define OPT_CACHED (1 << 1)
#define OPT_RECURSIVE (1 << 2)
#define OPT_FORCE (1 << 3)

typedef void (*each_submodule_fn)(const struct cache_entry *list_item,
void *cb_data);
Expand Down Expand Up @@ -909,6 +910,151 @@ static int module_sync(int argc, const char **argv, const char *prefix)
return 0;
}

struct deinit_cb {
const char *prefix;
unsigned int flags;
};
#define DEINIT_CB_INIT { NULL, 0 }

static void deinit_submodule(const char *path, const char *prefix,
unsigned int flags)
{
const struct submodule *sub;
char *displaypath = NULL;
struct child_process cp_config = CHILD_PROCESS_INIT;
struct strbuf sb_config = STRBUF_INIT;
char *sub_git_dir = xstrfmt("%s/.git", path);

sub = submodule_from_path(&null_oid, path);

if (!sub || !sub->name)
goto cleanup;

displaypath = get_submodule_displaypath(path, prefix);

/* remove the submodule work tree (unless the user already did it) */
if (is_directory(path)) {
struct strbuf sb_rm = STRBUF_INIT;
const char *format;

/*
* protect submodules containing a .git directory
* NEEDSWORK: instead of dying, automatically call
* absorbgitdirs and (possibly) warn.
*/
if (is_directory(sub_git_dir))
die(_("Submodule work tree '%s' contains a .git "
"directory (use 'rm -rf' if you really want "
"to remove it including all of its history)"),
displaypath);

if (!(flags & OPT_FORCE)) {
struct child_process cp_rm = CHILD_PROCESS_INIT;
cp_rm.git_cmd = 1;
argv_array_pushl(&cp_rm.args, "rm", "-qn",
path, NULL);

if (run_command(&cp_rm))
die(_("Submodule work tree '%s' contains local "
"modifications; use '-f' to discard them"),
displaypath);
}

strbuf_addstr(&sb_rm, path);

if (!remove_dir_recursively(&sb_rm, 0))
format = _("Cleared directory '%s'\n");
else
format = _("Could not remove submodule work tree '%s'\n");

if (!(flags & OPT_QUIET))
printf(format, displaypath);

strbuf_release(&sb_rm);
}

if (mkdir(path, 0777))
printf(_("could not create empty submodule directory %s"),
displaypath);

cp_config.git_cmd = 1;
argv_array_pushl(&cp_config.args, "config", "--get-regexp", NULL);
argv_array_pushf(&cp_config.args, "submodule.%s\\.", sub->name);

/* remove the .git/config entries (unless the user already did it) */
if (!capture_command(&cp_config, &sb_config, 0) && sb_config.len) {
char *sub_key = xstrfmt("submodule.%s", sub->name);
/*
* remove the whole section so we have a clean state when
* the user later decides to init this submodule again
*/
git_config_rename_section_in_file(NULL, sub_key, NULL);
if (!(flags & OPT_QUIET))
printf(_("Submodule '%s' (%s) unregistered for path '%s'\n"),
sub->name, sub->url, displaypath);
free(sub_key);
}

cleanup:
free(displaypath);
free(sub_git_dir);
strbuf_release(&sb_config);
}

static void deinit_submodule_cb(const struct cache_entry *list_item,
void *cb_data)
{
struct deinit_cb *info = cb_data;
deinit_submodule(list_item->name, info->prefix, info->flags);
}

static int module_deinit(int argc, const char **argv, const char *prefix)
{
struct deinit_cb info = DEINIT_CB_INIT;
struct pathspec pathspec;
struct module_list list = MODULE_LIST_INIT;
int quiet = 0;
int force = 0;
int all = 0;

struct option module_deinit_options[] = {
OPT__QUIET(&quiet, N_("Suppress submodule status output")),
OPT__FORCE(&force, N_("Remove submodule working trees even if they contain local changes")),
OPT_BOOL(0, "all", &all, N_("Unregister all submodules")),
OPT_END()
};

const char *const git_submodule_helper_usage[] = {
N_("git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]"),
NULL
};

argc = parse_options(argc, argv, prefix, module_deinit_options,
git_submodule_helper_usage, 0);

if (all && argc) {
error("pathspec and --all are incompatible");
usage_with_options(git_submodule_helper_usage,
module_deinit_options);
}

if (!argc && !all)
die(_("Use '--all' if you really want to deinitialize all submodules"));

if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
BUG("module_list_compute should not choke on empty pathspec");

info.prefix = prefix;
if (quiet)
info.flags |= OPT_QUIET;
if (force)
info.flags |= OPT_FORCE;

for_each_listed_submodule(&list, deinit_submodule_cb, &info);

return 0;
}

static int clone_submodule(const char *path, const char *gitdir, const char *url,
const char *depth, struct string_list *reference,
int quiet, int progress)
Expand Down Expand Up @@ -1691,6 +1837,7 @@ static struct cmd_struct commands[] = {
{"status", module_status, SUPPORT_SUPER_PREFIX},
{"print-default-remote", print_default_remote, 0},
{"sync", module_sync, SUPPORT_SUPER_PREFIX},
{"deinit", module_deinit, 0},
{"remote-branch", resolve_remote_submodule_branch, 0},
{"push-check", push_check, 0},
{"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
Expand Down
55 changes: 1 addition & 54 deletions git-submodule.sh
Expand Up @@ -428,60 +428,7 @@ cmd_deinit()
shift
done

if test -n "$deinit_all" && test "$#" -ne 0
then
echo >&2 "$(eval_gettext "pathspec and --all are incompatible")"
usage
fi
if test $# = 0 && test -z "$deinit_all"
then
die "$(eval_gettext "Use '--all' if you really want to deinitialize all submodules")"
fi

{
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 "$sm_path" "$wt_prefix")

# Remove the submodule work tree (unless the user already did it)
if test -d "$sm_path"
then
# Protect submodules containing a .git directory
if test -d "$sm_path/.git"
then
die "$(eval_gettext "\
Submodule work tree '\$displaypath' contains a .git directory
(use 'rm -rf' if you really want to remove it including all of its history)")"
fi

if test -z "$force"
then
git rm -qn "$sm_path" ||
die "$(eval_gettext "Submodule work tree '\$displaypath' contains local modifications; use '-f' to discard them")"
fi
rm -rf "$sm_path" &&
say "$(eval_gettext "Cleared directory '\$displaypath'")" ||
say "$(eval_gettext "Could not remove submodule work tree '\$displaypath'")"
fi

mkdir "$sm_path" || say "$(eval_gettext "Could not create empty submodule directory '\$displaypath'")"

# Remove the .git/config entries (unless the user already did it)
if test -n "$(git config --get-regexp submodule."$name\.")"
then
# Remove the whole section so we have a clean state when
# the user later decides to init this submodule again
url=$(git config submodule."$name".url)
git config --remove-section submodule."$name" 2>/dev/null &&
say "$(eval_gettext "Submodule '\$name' (\$url) unregistered for path '\$displaypath'")"
fi
done
git ${wt_prefix:+-C "$wt_prefix"} submodule--helper deinit ${GIT_QUIET:+--quiet} ${prefix:+--prefix "$prefix"} ${force:+--force} ${deinit_all:+--all} "$@"
}

is_tip_reachable () (
Expand Down

0 comments on commit 2e61273

Please sign in to comment.