Let's add a new "add-clone" subcommand to `git submodule--helper` with
the goal of converting part of the shell code in git-submodule.sh
related to `git submodule add` into C code. This new subcommand clones
the repository that is to be added, and checks out to the appropriate
branch.
This is meant to be a faithful conversion that leaves the behaviour of
'cmd_add()' script unchanged.
Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
Based-on-patch-by: Shourya Shukla <periperidip@gmail.com>
Based-on-patch-by: Prathamesh Chavan <pc44800@gmail.com>
Helped-by: Đoàn Trần Công Danh <congdanhqx@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Atharva Raykar committedJul 10, 2021 at 13:18 UTC8c8195e9c3e21922673575828977845b613c3491
2 files changed+178-37
builtin/submodule--helper.c
+177
index ae246a35f9..6d52a73a57 100644--- a/builtin/submodule--helper.c+++ b/builtin/submodule--helper.c@@ -2760,6 +2760,182 @@ static int module_set_branch(int argc, const char **argv, const char *prefix) return !!ret; }+struct add_data {+ const char *prefix;+ const char *branch;+ const char *reference_path;+ const char *sm_path;+ const char *sm_name;+ const char *repo;+ const char *realrepo;+ int depth;+ unsigned int force: 1;+ unsigned int quiet: 1;+ unsigned int progress: 1;+ unsigned int dissociate: 1;+};+#define ADD_DATA_INIT { .depth = -1 }++static void show_fetch_remotes(FILE *output, const char *sm_name, const char *git_dir_path)+{+ struct child_process cp_remote = CHILD_PROCESS_INIT;+ struct strbuf sb_remote_out = STRBUF_INIT;++ cp_remote.git_cmd = 1;+ strvec_pushf(&cp_remote.env_array,+ "GIT_DIR=%s", git_dir_path);+ strvec_push(&cp_remote.env_array, "GIT_WORK_TREE=.");+ strvec_pushl(&cp_remote.args, "remote", "-v", NULL);+ if (!capture_command(&cp_remote, &sb_remote_out, 0)) {+ char *next_line;+ char *line = sb_remote_out.buf;+ while ((next_line = strchr(line, '\n')) != NULL) {+ size_t len = next_line - line;+ if (strip_suffix_mem(line, &len, " (fetch)"))+ fprintf(output, " %.*s\n", (int)len, line);+ line = next_line + 1;+ }+ }++ strbuf_release(&sb_remote_out);+}++static int add_submodule(const struct add_data *add_data)+{+ char *submod_gitdir_path;+ struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;++ /* perhaps the path already exists and is already a git repo, else clone it */+ if (is_directory(add_data->sm_path)) {+ struct strbuf sm_path = STRBUF_INIT;+ strbuf_addstr(&sm_path, add_data->sm_path);+ submod_gitdir_path = xstrfmt("%s/.git", add_data->sm_path);+ if (is_nonbare_repository_dir(&sm_path))+ printf(_("Adding existing repo at '%s' to the index\n"),+ add_data->sm_path);+ else+ die(_("'%s' already exists and is not a valid git repo"),+ add_data->sm_path);+ strbuf_release(&sm_path);+ free(submod_gitdir_path);+ } else {+ struct child_process cp = CHILD_PROCESS_INIT;+ submod_gitdir_path = xstrfmt(".git/modules/%s", add_data->sm_name);++ if (is_directory(submod_gitdir_path)) {+ if (!add_data->force) {+ fprintf(stderr, _("A git directory for '%s' is found "+ "locally with remote(s):"),+ add_data->sm_name);+ show_fetch_remotes(stderr, add_data->sm_name,+ submod_gitdir_path);+ free(submod_gitdir_path);+ die(_("If you want to reuse this local git "+ "directory instead of cloning again from\n"+ " %s\n"+ "use the '--force' option. If the local git "+ "directory is not the correct repo\n"+ "or if you are unsure what this means, choose "+ "another name with the '--name' option.\n"),+ add_data->realrepo);+ } else {+ printf(_("Reactivating local git directory for "+ "submodule '%s'\n"), add_data->sm_name);+ }+ }+ free(submod_gitdir_path);++ clone_data.prefix = add_data->prefix;+ clone_data.path = add_data->sm_path;+ clone_data.name = add_data->sm_name;+ clone_data.url = add_data->realrepo;+ clone_data.quiet = add_data->quiet;+ clone_data.progress = add_data->progress;+ if (add_data->reference_path)+ string_list_append(&clone_data.reference,+ xstrdup(add_data->reference_path));+ clone_data.dissociate = add_data->dissociate;+ if (add_data->depth >= 0)+ clone_data.depth = xstrfmt("%d", add_data->depth);++ if (clone_submodule(&clone_data))+ return -1;++ prepare_submodule_repo_env(&cp.env_array);+ cp.git_cmd = 1;+ cp.dir = add_data->sm_path;+ strvec_pushl(&cp.args, "checkout", "-f", "-q", NULL);++ if (add_data->branch) {+ strvec_pushl(&cp.args, "-B", add_data->branch, NULL);+ strvec_pushf(&cp.args, "origin/%s", add_data->branch);+ }++ if (run_command(&cp))+ die(_("unable to checkout submodule '%s'"), add_data->sm_path);+ }+ return 0;+}++static int add_clone(int argc, const char **argv, const char *prefix)+{+ int force = 0, quiet = 0, dissociate = 0, progress = 0;+ struct add_data add_data = ADD_DATA_INIT;++ struct option options[] = {+ OPT_STRING('b', "branch", &add_data.branch,+ N_("branch"),+ N_("branch of repository to checkout on cloning")),+ OPT_STRING(0, "prefix", &prefix,+ N_("path"),+ N_("alternative anchor for relative paths")),+ OPT_STRING(0, "path", &add_data.sm_path,+ N_("path"),+ N_("where the new submodule will be cloned to")),+ OPT_STRING(0, "name", &add_data.sm_name,+ N_("string"),+ N_("name of the new submodule")),+ OPT_STRING(0, "url", &add_data.realrepo,+ N_("string"),+ N_("url where to clone the submodule from")),+ OPT_STRING(0, "reference", &add_data.reference_path,+ N_("repo"),+ N_("reference repository")),+ OPT_BOOL(0, "dissociate", &dissociate,+ N_("use --reference only while cloning")),+ OPT_INTEGER(0, "depth", &add_data.depth,+ N_("depth for shallow clones")),+ OPT_BOOL(0, "progress", &progress,+ N_("force cloning progress")),+ OPT__FORCE(&force, N_("allow adding an otherwise ignored submodule path"),+ PARSE_OPT_NOCOMPLETE),+ OPT__QUIET(&quiet, "suppress output for cloning a submodule"),+ OPT_END()+ };++ const char *const usage[] = {+ N_("git submodule--helper add-clone [<options>...] "+ "--url <url> --path <path> --name <name>"),+ NULL+ };++ argc = parse_options(argc, argv, prefix, options, usage, 0);++ if (argc != 0)+ usage_with_options(usage, options);++ add_data.prefix = prefix;+ add_data.progress = !!progress;+ add_data.dissociate = !!dissociate;+ add_data.force = !!force;+ add_data.quiet = !!quiet;++ if (add_submodule(&add_data))+ return 1;++ return 0;+}+ #define SUPPORT_SUPER_PREFIX (1<<0) struct cmd_struct {@@ -2772,6 +2948,7 @@ static struct cmd_struct commands[] = { {"list", module_list, 0}, {"name", module_name, 0}, {"clone", module_clone, 0},+ {"add-clone", add_clone, 0}, {"update-module-mode", module_update_module_mode, 0}, {"update-clone", update_clone, 0}, {"ensure-core-worktree", ensure_core_worktree, 0},
git-submodule.sh
+1-37
index 69bcb4fab2..053daf3724 100755--- a/git-submodule.sh+++ b/git-submodule.sh@@ -241,43 +241,7 @@ cmd_add() die "fatal: $(eval_gettext "'$sm_name' is not a valid submodule name")" fi- # perhaps the path exists and is already a git repo, else clone it- if test -e "$sm_path"- then- if test -d "$sm_path"/.git || test -f "$sm_path"/.git- then- eval_gettextln "Adding existing repo at '\$sm_path' to the index"- else- die "$(eval_gettext "'\$sm_path' already exists and is not a valid git repo")"- fi-- else- if test -d ".git/modules/$sm_name"- then- if test -z "$force"- then- eval_gettextln >&2 "A git directory for '\$sm_name' is found locally with remote(s):"- GIT_DIR=".git/modules/$sm_name" GIT_WORK_TREE=. git remote -v | grep '(fetch)' | sed -e s,^," ", -e s,' (fetch)',, >&2- die "$(eval_gettextln "\-If you want to reuse this local git directory instead of cloning again from- \$realrepo-use the '--force' option. If the local git directory is not the correct repo-or you are unsure what this means choose another name with the '--name' option.")"- else- eval_gettextln "Reactivating local git directory for submodule '\$sm_name'."- fi- fi- git submodule--helper clone ${GIT_QUIET:+--quiet} ${progress:+"--progress"} --prefix "$wt_prefix" --path "$sm_path" --name "$sm_name" --url "$realrepo" ${reference:+"$reference"} ${dissociate:+"--dissociate"} ${depth:+"$depth"} || exit- (- sanitize_submodule_env- cd "$sm_path" &&- # ash fails to wordsplit ${branch:+-b "$branch"...}- case "$branch" in- '') git checkout -f -q ;;- ?*) git checkout -f -q -B "$branch" "origin/$branch" ;;- esac- ) || die "$(eval_gettext "Unable to checkout submodule '\$sm_path'")"- fi+ git submodule--helper add-clone ${GIT_QUIET:+--quiet} ${force:+"--force"} ${progress:+"--progress"} ${branch:+--branch "$branch"} --prefix "$wt_prefix" --path "$sm_path" --name "$sm_name" --url "$realrepo" ${reference:+"$reference"} ${dissociate:+"--dissociate"} ${depth:+"$depth"} || exit git config submodule."$sm_name".url "$realrepo" git add --no-warn-embedded-repo $force "$sm_path" ||