]> git.ipfire.org Git - thirdparty/git.git/commitdiff
submodule--helper: introduce add-clone subcommand
authorAtharva Raykar <raykar.ath@gmail.com>
Sat, 10 Jul 2021 07:48:01 +0000 (13:18 +0530)
committerJunio C Hamano <gitster@pobox.com>
Mon, 12 Jul 2021 19:06:21 +0000 (12:06 -0700)
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>
builtin/submodule--helper.c
git-submodule.sh

index ae246a35f948191c821ebed61b838055cb0f758b..6d52a73a57a551bed901bfdc20883d3b98ce944e 100644 (file)
@@ -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},
index 69bcb4fab203b2b2052090753c7ef1fb8524fea0..053daf37246994ec8c8dda450da5a190635120d8 100755 (executable)
@@ -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" ||