[GSoC][PATCH 06/13] 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 three functions: module_deinit(), for_each_submodule_list() and deinit_submodule(). Mentored-by: Christian CouderMentored-by: Stefan Beller Signed-off-by: Prathamesh Chavan --- builtin/submodule--helper.c | 148 git-submodule.sh| 55 +--- 2 files changed, 149 insertions(+), 54 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 82f1aed87..02787e4a4 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -915,6 +915,153 @@ static int module_sync(int argc, const char **argv, const char *prefix) return 0; } +struct deinit_cb { + const char *prefix; + unsigned int quiet: 1; + unsigned int force: 1; + unsigned int all: 1; +}; +#define DEINIT_CB_INIT { NULL, 0, 0, 0 } + +static void deinit_submodule(const struct cache_entry *list_item, +void *cb_data) +{ + struct deinit_cb *info = cb_data; + 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", list_item->name); + mode_t mode = 0777; + + sub = submodule_from_path(null_sha1, list_item->name); + + if (!sub || !sub->name) + goto cleanup; + + displaypath = get_submodule_displaypath(list_item->name, info->prefix); + + /* remove the submodule work tree (unless the user already did it) */ + if (is_directory(list_item->name)) { + struct stat st; + /* +* protect submodules containing a .git directory +* NEEDSWORK: automatically call absorbgitdirs before +* warning/die. +*/ + 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 (!info->force) { + struct child_process cp_rm = CHILD_PROCESS_INIT; + cp_rm.git_cmd = 1; + argv_array_pushl(_rm.args, "rm", "-qn", +list_item->name, NULL); + + if (run_command(_rm)) + die(_("Submodule work tree '%s' contains local " + "modifications; use '-f' to discard them"), + displaypath); + } + + if (!lstat(list_item->name, )) { + struct strbuf sb_rm = STRBUF_INIT; + const char *format; + + strbuf_addstr(_rm, list_item->name); + + if (!remove_dir_recursively(_rm, 0)) + format = _("Cleared directory '%s'\n"); + else + format = _("Could not remove submodule work tree '%s'\n"); + + if (!info->quiet) + printf(format, displaypath); + + mode = st.st_mode; + + strbuf_release(_rm); + } + } + + if (mkdir(list_item->name, mode)) + die_errno(_("could not create empty submodule directory %s"), + displaypath); + + cp_config.git_cmd = 1; + argv_array_pushl(_config.args, "config", "--get-regexp", NULL); + argv_array_pushf(_config.args, "submodule.%s\\.", sub->name); + + /* remove the .git/config entries (unless the user already did it) */ + if (!capture_command(_config, _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 (!info->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(_config); +} + +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
Re: [GSoC][PATCH 06/13] submodule: port submodule subcommand 'deinit' from shell to C
On Tue, Aug 1, 2017 at 3:12 AM, Stefan Bellerwrote: > On Mon, Jul 31, 2017 at 1:56 PM, Prathamesh Chavan wrote: >> 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 three >> functions: module_deinit(), for_each_submodule_list() and >> deinit_submodule(). >> >> Mentored-by: Christian Couder >> Mentored-by: Stefan Beller >> Signed-off-by: Prathamesh Chavan >> --- >> In this new version, the following changes have been made: >> * In the function deinit_submodule, since the test is_git_directory() >> adds an additional condition, instead is_directory() is used to check >> if "sm_path/.git" is a directory. > > Thanks for writing these patches. > I wonder if (some of) these notes are best put into the code > as a comment such as > > /* NEEDSWORK: convert to is_submodule_active */ > > such that people reading this code later realize that checking > for a directory may not be the "correct" thing, but a thing which > was easy to express using shell. > >> +struct deinit_cb { >> + const char *prefix; >> + unsigned int quiet: 1; >> + unsigned int force: 1; >> + unsigned int all: 1; > > The value 'all' seems to be unused, i.e. we assign it but never read it? > >> +}; >> +#define DEINIT_CB_INIT { NULL, 0, 0, 0 } >> + >> +static void deinit_submodule(const struct cache_entry *list_item, >> +void *cb_data) >> +{ >> + struct deinit_cb *info = cb_data; >> + 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", list_item->name); >> + mode_t mode = 0777; >> + >> + sub = submodule_from_path(null_sha1, list_item->name); >> + >> + if (!sub || !sub->name) >> + goto cleanup; >> + >> + displaypath = get_submodule_displaypath(list_item->name, >> info->prefix); >> + >> + /* remove the submodule work tree (unless the user already did it) */ >> + if (is_directory(list_item->name)) { >> + struct stat st; >> + /* protect submodules containing a .git directory */ > > Here may a good place to put: > /* NEEDSWORK: automatically call absorbgitdirs before warning/die. */ > (It was not in the shell version, so feel free to ignore) > >> + if (!info->force) { >> + struct child_process cp_rm = CHILD_PROCESS_INIT; >> + cp_rm.git_cmd = 1; >> + argv_array_pushl(_rm.args, "rm", "-qn", >> +list_item->name, NULL); > > A bug that exists in the shell version as well as here: > What if the submodule has the name '--cached', which happens > to be a valid argument for git-rm? > > The call to git-rm would die claiming that the is missing, > as the file name was miss-interpreted as another flag. > > To solve this problem we would insert a '--' after the options, > before the file name to state that the last argument is a . > > Not sure if we want to fix the bug while we're here or if we rather > want to add > > /* NEEDSWORK: add '--' to confirm argument */ > IMO, I would first port the subcommand, and add an additional comment for pointing the bug out. And then later, we may have a bug-fix patch in this series of patch itself for tackling this bug out. Thanks, Prathamesh Chavan
Re: [GSoC][PATCH 06/13] submodule: port submodule subcommand 'deinit' from shell to C
On Mon, Jul 31, 2017 at 1:56 PM, Prathamesh Chavanwrote: > 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 three > functions: module_deinit(), for_each_submodule_list() and > deinit_submodule(). > > Mentored-by: Christian Couder > Mentored-by: Stefan Beller > Signed-off-by: Prathamesh Chavan > --- > In this new version, the following changes have been made: > * In the function deinit_submodule, since the test is_git_directory() > adds an additional condition, instead is_directory() is used to check > if "sm_path/.git" is a directory. Thanks for writing these patches. I wonder if (some of) these notes are best put into the code as a comment such as /* NEEDSWORK: convert to is_submodule_active */ such that people reading this code later realize that checking for a directory may not be the "correct" thing, but a thing which was easy to express using shell. > +struct deinit_cb { > + const char *prefix; > + unsigned int quiet: 1; > + unsigned int force: 1; > + unsigned int all: 1; The value 'all' seems to be unused, i.e. we assign it but never read it? > +}; > +#define DEINIT_CB_INIT { NULL, 0, 0, 0 } > + > +static void deinit_submodule(const struct cache_entry *list_item, > +void *cb_data) > +{ > + struct deinit_cb *info = cb_data; > + 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", list_item->name); > + mode_t mode = 0777; > + > + sub = submodule_from_path(null_sha1, list_item->name); > + > + if (!sub || !sub->name) > + goto cleanup; > + > + displaypath = get_submodule_displaypath(list_item->name, > info->prefix); > + > + /* remove the submodule work tree (unless the user already did it) */ > + if (is_directory(list_item->name)) { > + struct stat st; > + /* protect submodules containing a .git directory */ Here may a good place to put: /* NEEDSWORK: automatically call absorbgitdirs before warning/die. */ (It was not in the shell version, so feel free to ignore) > + if (!info->force) { > + struct child_process cp_rm = CHILD_PROCESS_INIT; > + cp_rm.git_cmd = 1; > + argv_array_pushl(_rm.args, "rm", "-qn", > +list_item->name, NULL); A bug that exists in the shell version as well as here: What if the submodule has the name '--cached', which happens to be a valid argument for git-rm? The call to git-rm would die claiming that the is missing, as the file name was miss-interpreted as another flag. To solve this problem we would insert a '--' after the options, before the file name to state that the last argument is a . Not sure if we want to fix the bug while we're here or if we rather want to add /* NEEDSWORK: add '--' to confirm argument */ > + if (mkdir(list_item->name, mode)) > + die(_("could not create empty submodule directory %s"), > + displaypath); In the shell version we used the shell mkdir, which on failure would print the error to stderr on its own. The added "|| say" is just to clarify the bigger picture. mkdir in C doesn't print the actual error (e.g. no diskspace, permissions) so we have to do it. Use 'die_errno' instead as then the reason will be given as well. > + cp_config.git_cmd = 1; > + argv_array_pushl(_config.args, "config", "--get-regexp", NULL); > + argv_array_pushf(_config.args, "submodule.%s\\.", sub->name); It would be nice if the commit message could give hints as why a literal translation was chosen here and not e.g. one of the builtin config calls, as that would omit spawning a process.
[GSoC][PATCH 06/13] 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 three functions: module_deinit(), for_each_submodule_list() and deinit_submodule(). Mentored-by: Christian CouderMentored-by: Stefan Beller Signed-off-by: Prathamesh Chavan --- In this new version, the following changes have been made: * In the function deinit_submodule, since the test is_git_directory() adds an additional condition, instead is_directory() is used to check if "sm_path/.git" is a directory. * since it was possible in the previous path that the value st.st_mode passed to the function mkdir contained a garbage value, instead we intrduce a mode_t variable mode, initially containing a default mode value '0777'. This is what the default of mode is set in case, that the value is not set after the lstat call. builtin/submodule--helper.c | 144 git-submodule.sh| 55 + 2 files changed, 145 insertions(+), 54 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 91945337f..f642f9889 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -913,6 +913,149 @@ static int module_sync(int argc, const char **argv, const char *prefix) return 0; } +struct deinit_cb { + const char *prefix; + unsigned int quiet: 1; + unsigned int force: 1; + unsigned int all: 1; +}; +#define DEINIT_CB_INIT { NULL, 0, 0, 0 } + +static void deinit_submodule(const struct cache_entry *list_item, +void *cb_data) +{ + struct deinit_cb *info = cb_data; + 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", list_item->name); + mode_t mode = 0777; + + sub = submodule_from_path(null_sha1, list_item->name); + + if (!sub || !sub->name) + goto cleanup; + + displaypath = get_submodule_displaypath(list_item->name, info->prefix); + + /* remove the submodule work tree (unless the user already did it) */ + if (is_directory(list_item->name)) { + struct stat st; + /* protect submodules containing a .git directory */ + 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 (!info->force) { + struct child_process cp_rm = CHILD_PROCESS_INIT; + cp_rm.git_cmd = 1; + argv_array_pushl(_rm.args, "rm", "-qn", +list_item->name, NULL); + + if (run_command(_rm)) + die(_("Submodule work tree '%s' contains local " + "modifications; use '-f' to discard them"), + displaypath); + } + + if (!lstat(list_item->name, )) { + struct strbuf sb_rm = STRBUF_INIT; + const char *format; + + strbuf_addstr(_rm, list_item->name); + + if (!remove_dir_recursively(_rm, 0)) + format = _("Cleared directory '%s'\n"); + else + format = _("Could not remove submodule work tree '%s'\n"); + + if (!info->quiet) + printf(format, displaypath); + + mode = st.st_mode; + + strbuf_release(_rm); + } + } + + if (mkdir(list_item->name, mode)) + die(_("could not create empty submodule directory %s"), + displaypath); + + cp_config.git_cmd = 1; + argv_array_pushl(_config.args, "config", "--get-regexp", NULL); + argv_array_pushf(_config.args, "submodule.%s\\.", sub->name); + + /* remove the .git/config entries (unless the user already did it) */ + if (!capture_command(_config, _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 (!info->quiet) + printf(_("Submodule '%s' (%s) unregistered
Re: [GSoC][PATCH 06/13] submodule: port submodule subcommand 'deinit' from shell to C
On 07/25, Prathamesh Chavan wrote: > 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 three > functions: module_deinit(), for_each_submodule_list() and > deinit_submodule(). > > Mentored-by: Christian Couder> Mentored-by: Stefan Beller > Signed-off-by: Prathamesh Chavan > --- > builtin/submodule--helper.c | 141 > > git-submodule.sh| 55 + > 2 files changed, 142 insertions(+), 54 deletions(-) > > diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c > index 2d1d3984d..5e84fc42d 100644 > --- a/builtin/submodule--helper.c > +++ b/builtin/submodule--helper.c > @@ -910,6 +910,146 @@ static int module_sync(int argc, const char **argv, > const char *prefix) > return 0; > } > > +struct deinit_cb { > + const char *prefix; > + unsigned int quiet: 1; > + unsigned int force: 1; > + unsigned int all: 1; > +}; > +#define DEINIT_CB_INIT { NULL, 0, 0, 0 } > + > +static void deinit_submodule(const struct cache_entry *list_item, > + void *cb_data) > +{ > + struct deinit_cb *info = cb_data; > + 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", list_item->name); > + struct stat st; > + > + sub = submodule_from_path(null_sha1, list_item->name); > + > + if (!sub || !sub->name) > + goto cleanup; > + > + displaypath = get_submodule_displaypath(list_item->name, info->prefix); > + > + /* remove the submodule work tree (unless the user already did it) */ > + if (is_directory(list_item->name)) { > + /* protect submodules containing a .git directory */ > + if (is_git_directory(sub_git_dir)) This may be too strict of a test. The original code simply checks if 'submodule/.git' is a directory and dies if it is. This adds additional checks ensuring it is a gitdir. If we want to have a straight conversion from the shell code we should have this only check if it is a directory. > + 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 (!info->force) { > + struct child_process cp_rm = CHILD_PROCESS_INIT; > + cp_rm.git_cmd = 1; > + argv_array_pushl(_rm.args, "rm", "-qn", > + list_item->name, NULL); > + > + if (run_command(_rm)) > + die(_("Submodule work tree '%s' contains local " > + "modifications; use '-f' to discard > them"), > + displaypath); > + } > + > + if (!lstat(list_item->name, )) { What's the purpose of the lstat call here? > + struct strbuf sb_rm = STRBUF_INIT; > + const char *format; > + > + strbuf_addstr(_rm, list_item->name); > + > + if (!remove_dir_recursively(_rm, 0)) > + format = _("Cleared directory '%s'\n"); > + else > + format = _("Could not remove submodule work > tree '%s'\n"); > + > + if (!info->quiet) > + printf(format, displaypath); > + > + strbuf_release(_rm); > + } > + } > + > + if (mkdir(list_item->name, st.st_mode)) What should the mode be when making the empty directory? Right now you have the potential for it to be garbage as 'st' has the potential of not being set by the lstat call above (since it happens inside the above if statement). Also we may not want it to depend on what the permissions were set as on the filesystem assuming the user didn't already remove the submodule themselves. > + die(_("could not create empty submodule directory %s"), > + displaypath); > + > + cp_config.git_cmd = 1; > + argv_array_pushl(_config.args, "config", "--get-regexp", NULL); > + argv_array_pushf(_config.args, "submodule.%s\\.", sub->name); > + > + /* remove the .git/config entries (unless the user already did it) */ > + if (!capture_command(_config, _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
[GSoC][PATCH 06/13] 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 three functions: module_deinit(), for_each_submodule_list() and deinit_submodule(). Mentored-by: Christian CouderMentored-by: Stefan Beller Signed-off-by: Prathamesh Chavan --- builtin/submodule--helper.c | 141 git-submodule.sh| 55 + 2 files changed, 142 insertions(+), 54 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 2d1d3984d..5e84fc42d 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -910,6 +910,146 @@ static int module_sync(int argc, const char **argv, const char *prefix) return 0; } +struct deinit_cb { + const char *prefix; + unsigned int quiet: 1; + unsigned int force: 1; + unsigned int all: 1; +}; +#define DEINIT_CB_INIT { NULL, 0, 0, 0 } + +static void deinit_submodule(const struct cache_entry *list_item, +void *cb_data) +{ + struct deinit_cb *info = cb_data; + 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", list_item->name); + struct stat st; + + sub = submodule_from_path(null_sha1, list_item->name); + + if (!sub || !sub->name) + goto cleanup; + + displaypath = get_submodule_displaypath(list_item->name, info->prefix); + + /* remove the submodule work tree (unless the user already did it) */ + if (is_directory(list_item->name)) { + /* protect submodules containing a .git directory */ + if (is_git_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 (!info->force) { + struct child_process cp_rm = CHILD_PROCESS_INIT; + cp_rm.git_cmd = 1; + argv_array_pushl(_rm.args, "rm", "-qn", +list_item->name, NULL); + + if (run_command(_rm)) + die(_("Submodule work tree '%s' contains local " + "modifications; use '-f' to discard them"), + displaypath); + } + + if (!lstat(list_item->name, )) { + struct strbuf sb_rm = STRBUF_INIT; + const char *format; + + strbuf_addstr(_rm, list_item->name); + + if (!remove_dir_recursively(_rm, 0)) + format = _("Cleared directory '%s'\n"); + else + format = _("Could not remove submodule work tree '%s'\n"); + + if (!info->quiet) + printf(format, displaypath); + + strbuf_release(_rm); + } + } + + if (mkdir(list_item->name, st.st_mode)) + die(_("could not create empty submodule directory %s"), + displaypath); + + cp_config.git_cmd = 1; + argv_array_pushl(_config.args, "config", "--get-regexp", NULL); + argv_array_pushf(_config.args, "submodule.%s\\.", sub->name); + + /* remove the .git/config entries (unless the user already did it) */ + if (!capture_command(_config, _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 (!info->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(_config); +} + +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(, N_("Suppress submodule status output")), +