{"thread":{"id":"55844","subject":"[GSoC] [PATCH 0/2] submodule--helper: introduce subcommands for sh to C conversion","startedAt":"2021-06-05T11:40:53Z","lastAt":"2021-06-15T14:58:25Z","messageCount":40,"participants":["Atharva Raykar","Bagas Sanjaya","Christian Couder","Đoàn Trần Công Danh","Junio C Hamano","Rafael Silva","Eric Sunshine"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"426483","messageId":"20210605113913.29005-1-raykar.ath@gmail.com","threadId":"55844","inReplyTo":null,"subject":"[GSoC] [PATCH 0/2] submodule--helper: introduce subcommands for sh to C conversion","fromName":"Atharva Raykar","fromEmail":"raykar.ath@gmail.com","sentAt":"2021-06-05T11:39:11Z","receivedAt":"2021-06-05T11:40:53Z","isPatch":true,"sender":{"key":"raykar.ath@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24277692?v=4"},"body":"This patch series introduces the `add clone` and `add config`\nsubcommands in `git submodule--helper` with the goal of converting part\nof the shell code in `git-submodule.sh` related to `git submodule add`\ninto C code.\n\nThe first patch [1/2] has already been sent to the mailing list before:\nhttps://lore.kernel.org/git/20210604110524.84326-1-raykar.ath@gmail.com/\nNo changes have been made to it since then.\n\nBecause patch [2/2] depends on changes introduced in [1/2] I am sending\nthem together as a series.\n\nThe eventual goal is to replace all of the shell code with equivalent C\ncode. Subsequent patches will replace all of shell the code past the\nflag parsing of `cmd_add()` into a single call to subcommand\n`submodule--helper add` which will make use of the functions introduced\nin these two patches.\n\nLink to hosted Git repository for containing these patches:\nhttps://github.com/tfidfwastaken/git/commits/submodule-add-in-c\n\nAtharva Raykar (2):\n  submodule--helper: introduce add-clone subcommand\n  submodule--helper: introduce add-config subcommand\n\n builtin/submodule--helper.c | 312 ++++++++++++++++++++++++++++++++++++\n git-submodule.sh            |  66 +-------\n 2 files changed, 314 insertions(+), 64 deletions(-)\n\n-- \n2.31.1\n\n"},{"id":"426484","messageId":"20210605113913.29005-3-raykar.ath@gmail.com","threadId":"55844","inReplyTo":"20210605113913.29005-1-raykar.ath@gmail.com","subject":"[GSoC] [PATCH 2/2] submodule--helper: introduce add-config subcommand","fromName":"Atharva Raykar","fromEmail":"raykar.ath@gmail.com","sentAt":"2021-06-05T11:39:16Z","receivedAt":"2021-06-05T11:42:20Z","isPatch":true,"sender":{"key":"raykar.ath@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24277692?v=4"},"body":"Add a new \"add-config\" subcommand to `git submodule--helper` with the\ngoal of converting part of the shell code in git-submodule.sh related to\n`git submodule add` into C code. This new subcommand sets the\nconfiguration variables of a newly added submodule, by registering the\nurl in local git config, as well as the submodule name and path in the\n.gitmodules file. It also sets 'submodule.<name>.active' to \"true\" if\nthe submodule path has not already been covered by any pathspec\nspecified in 'submodule.active'.\n\nThis is meant to be a faithful conversion from shell to C, with only one\nminor change: A warning is emitted if no value is specified in\n'submodule.active', ie, the config looks like: \"[submodule] active\\n\".\n\nThe structure of the conditional to check if we need to set the 'active'\ntoggle looks different from the shell version -- but behaves the same.\nThe change was made to decrease code duplication. A comment has been\nadded to explain that only one value of 'submodule.active' is obtained\nto check if we need to call is_submodule_active() at all.\n\nThis is part of a series of changes that will result in all of\n'submodule add' being converted to C.\n\nSigned-off-by: Atharva Raykar <raykar.ath@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Shourya Shukla <shouryashukla.oo@gmail.com>\nBased-on-patch-by: Shourya Shukla <shouryashukla.oo@gmail.com>\nBased-on-patch-by: Prathamesh Chavan <pc44800@gmail.com>\n---\n builtin/submodule--helper.c | 113 ++++++++++++++++++++++++++++++++++++\n git-submodule.sh            |  28 +--------\n 2 files changed, 114 insertions(+), 27 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex c9cb535312..92d105be02 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2943,6 +2943,118 @@ static int add_clone(int argc, const char **argv, const char *prefix)\n \treturn 0;\n }\n \n+static void configure_added_submodule(struct add_data *add_data)\n+{\n+\tchar *key, *submod_pathspec = NULL;\n+\tstruct child_process add_submod = CHILD_PROCESS_INIT;\n+\tstruct child_process add_gitmodules = CHILD_PROCESS_INIT;\n+\tint pathspec_key_exists;\n+\n+\tkey = xstrfmt(\"submodule.%s.url\", add_data->sm_name);\n+\tgit_config_set_gently(key, add_data->realrepo);\n+\tfree(key);\n+\n+\tadd_submod.git_cmd = 1;\n+\tstrvec_pushl(&add_submod.args, \"add\",\n+\t\t     \"--no-warn-embedded-repo\", NULL);\n+\tif (add_data->force)\n+\t\tstrvec_push(&add_submod.args, \"--force\");\n+\tstrvec_pushl(&add_submod.args, \"--\", add_data->sm_path, NULL);\n+\n+\tif (run_command(&add_submod))\n+\t\tdie(_(\"Failed to add submodule '%s'\"), add_data->sm_path);\n+\n+\tkey = xstrfmt(\"submodule.%s.path\", add_data->sm_name);\n+\tconfig_set_in_gitmodules_file_gently(key, add_data->sm_path);\n+\tfree(key);\n+\tkey = xstrfmt(\"submodule.%s.url\", add_data->sm_name);\n+\tconfig_set_in_gitmodules_file_gently(key, add_data->repo);\n+\tfree(key);\n+\tif (add_data->branch) {\n+\t\tkey = xstrfmt(\"submodule.%s.branch\", add_data->sm_path);\n+\t\tconfig_set_in_gitmodules_file_gently(key, add_data->branch);\n+\t\tfree(key);\n+\t}\n+\n+\tadd_gitmodules.git_cmd = 1;\n+\tstrvec_pushl(&add_gitmodules.args,\n+\t\t     \"add\", \"--force\", \"--\", \".gitmodules\", NULL);\n+\n+\tif (run_command(&add_gitmodules))\n+\t\tdie(_(\"Failed to register submodule '%s'\"), add_data->sm_path);\n+\n+\t/*\n+\t * NEEDSWORK: In a multi-working-tree world this needs to be\n+\t * set in the per-worktree config.\n+\t */\n+\tpathspec_key_exists = !git_config_get_string(\"submodule.active\",\n+\t\t\t\t\t\t     &submod_pathspec);\n+\tif (pathspec_key_exists && !submod_pathspec)\n+\t\twarning(_(\"The submodule.active configuration exists, but \"\n+\t\t\t  \"no pathspec was specified. Setting the value of \"\n+\t\t\t  \"submodule.%s.active to 'true'.\"), add_data->sm_name);\n+\n+\t/*\n+\t * If submodule.active does not exist, we will activate this\n+\t * module unconditionally.\n+\t *\n+\t * Otherwise, we ask is_submodule_active(), which iterates\n+\t * through all the values of 'submodule.active' to determine\n+\t * if this module is already active.\n+\t */\n+\tif (!pathspec_key_exists ||\n+\t    !is_submodule_active(the_repository, add_data->sm_path)) {\n+\t\tkey = xstrfmt(\"submodule.%s.active\", add_data->sm_name);\n+\t\tgit_config_set_gently(key, \"true\");\n+\t\tfree(key);\n+\t}\n+}\n+\n+static int add_config(int argc, const char **argv, const char *prefix)\n+{\n+\tint force = 0;\n+\tstruct add_data add_data = ADD_DATA_INIT;\n+\n+\tstruct option options[] = {\n+\t\tOPT_STRING('b', \"branch\", &add_data.branch,\n+\t\t\t   N_(\"branch\"),\n+\t\t\t   N_(\"branch of repository to store in \"\n+\t\t\t      \"the submodule configuration\")),\n+\t\tOPT_STRING(0, \"url\", &add_data.repo,\n+\t\t\t   N_(\"string\"),\n+\t\t\t   N_(\"url to clone submodule from\")),\n+\t\tOPT_STRING(0, \"resolved-url\", &add_data.realrepo,\n+\t\t\t   N_(\"string\"),\n+\t\t\t   N_(\"url to clone the submodule from, after it has \"\n+\t\t\t      \"been dereferenced relative to parent's url, \"\n+\t\t\t      \"in the case where <url> is a relative url\")),\n+\t\tOPT_STRING(0, \"path\", &add_data.sm_path,\n+\t\t\t   N_(\"path\"),\n+\t\t\t   N_(\"where the new submodule will be cloned to\")),\n+\t\tOPT_STRING(0, \"name\", &add_data.sm_name,\n+\t\t\t   N_(\"string\"),\n+\t\t\t   N_(\"name of the new submodule\")),\n+\t\tOPT_BOOL('f', \"force\", &force,\n+\t\t\t N_(\"allow adding an otherwise ignored submodule path\")),\n+\t\tOPT_END()\n+\t};\n+\n+\tconst char *const usage[] = {\n+\t\tN_(\"git submodule--helper add-config \"\n+\t\t   \"[--force|-f] [--branch|-b <branch>] \"\n+\t\t   \"--url <url> --resolved-url <resolved-url> \"\n+\t\t   \"--path <path> --name <name>\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, usage, 0);\n+\n+\tadd_data.force = !!force;\n+\tconfigure_added_submodule(&add_data);\n+\n+\treturn 0;\n+}\n+\n #define SUPPORT_SUPER_PREFIX (1<<0)\n \n struct cmd_struct {\n@@ -2956,6 +3068,7 @@ static struct cmd_struct commands[] = {\n \t{\"name\", module_name, 0},\n \t{\"clone\", module_clone, 0},\n \t{\"add-clone\", add_clone, 0},\n+\t{\"add-config\", add_config, 0},\n \t{\"update-module-mode\", module_update_module_mode, 0},\n \t{\"update-clone\", update_clone, 0},\n \t{\"ensure-core-worktree\", ensure_core_worktree, 0},\ndiff --git a/git-submodule.sh b/git-submodule.sh\nindex f71e1e5495..9826378fa6 100755\n--- a/git-submodule.sh\n+++ b/git-submodule.sh\n@@ -242,33 +242,7 @@ cmd_add()\n \tfi\n \n \tgit 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\n-\tgit config submodule.\"$sm_name\".url \"$realrepo\"\n-\n-\tgit add --no-warn-embedded-repo $force \"$sm_path\" ||\n-\tdie \"$(eval_gettext \"Failed to add submodule '\\$sm_path'\")\"\n-\n-\tgit submodule--helper config submodule.\"$sm_name\".path \"$sm_path\" &&\n-\tgit submodule--helper config submodule.\"$sm_name\".url \"$repo\" &&\n-\tif test -n \"$branch\"\n-\tthen\n-\t\tgit submodule--helper config submodule.\"$sm_name\".branch \"$branch\"\n-\tfi &&\n-\tgit add --force .gitmodules ||\n-\tdie \"$(eval_gettext \"Failed to register submodule '\\$sm_path'\")\"\n-\n-\t# NEEDSWORK: In a multi-working-tree world, this needs to be\n-\t# set in the per-worktree config.\n-\tif git config --get submodule.active >/dev/null\n-\tthen\n-\t\t# If the submodule being adding isn't already covered by the\n-\t\t# current configured pathspec, set the submodule's active flag\n-\t\tif ! git submodule--helper is-active \"$sm_path\"\n-\t\tthen\n-\t\t\tgit config submodule.\"$sm_name\".active \"true\"\n-\t\tfi\n-\telse\n-\t\tgit config submodule.\"$sm_name\".active \"true\"\n-\tfi\n+\tgit submodule--helper add-config ${force:+--force} ${branch:+--branch \"$branch\"} --url \"$repo\" --resolved-url \"$realrepo\" --path \"$sm_path\" --name \"$sm_name\"\n }\n \n #\n-- \n2.31.1\n\n"},{"id":"426485","messageId":"20210605113913.29005-2-raykar.ath@gmail.com","threadId":"55844","inReplyTo":"20210605113913.29005-1-raykar.ath@gmail.com","subject":"[GSoC] [PATCH 1/2] submodule--helper: introduce add-clone subcommand","fromName":"Atharva Raykar","fromEmail":"raykar.ath@gmail.com","sentAt":"2021-06-05T11:39:14Z","receivedAt":"2021-06-05T11:42:59Z","isPatch":true,"sender":{"key":"raykar.ath@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24277692?v=4"},"body":"Let's add a new \"add-clone\" subcommand to `git submodule--helper` with\nthe goal of converting part of the shell code in git-submodule.sh\nrelated to `git submodule add` into C code. This new subcommand clones\nthe repository that is to be added, and checks out to the appropriate\nbranch.\n\nThis is meant to be a faithful conversion that leaves the behaviour of\n'submodule add' unchanged. The only minor change is that if a submodule\nname has been supplied with a name that clashes with a local submodule,\nthe message shown to the user (\"A git directory for 'foo' is found\nlocally...\") is prepended with \"error\" for clarity.\n\nThis is part of a series of changes that will result in all of\n'submodule add' being converted to C.\n\nSigned-off-by: Atharva Raykar <raykar.ath@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Shourya Shukla <shouryashukla.oo@gmail.com>\nBased-on-patch-by: Shourya Shukla <shouryashukla.oo@gmail.com>\nBased-on-patch-by: Prathamesh Chavan <pc44800@gmail.com>\n---\n builtin/submodule--helper.c | 199 ++++++++++++++++++++++++++++++++++++\n git-submodule.sh            |  38 +------\n 2 files changed, 200 insertions(+), 37 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex d55f6262e9..c9cb535312 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2745,6 +2745,204 @@ static int module_set_branch(int argc, const char **argv, const char *prefix)\n \treturn !!ret;\n }\n \n+struct add_data {\n+\tconst char *prefix;\n+\tconst char *branch;\n+\tconst char *reference_path;\n+\tconst char *sm_path;\n+\tconst char *sm_name;\n+\tconst char *repo;\n+\tconst char *realrepo;\n+\tint depth;\n+\tunsigned int force: 1;\n+\tunsigned int quiet: 1;\n+\tunsigned int progress: 1;\n+\tunsigned int dissociate: 1;\n+};\n+#define ADD_DATA_INIT { .depth = -1 }\n+\n+static char *parse_token(char **begin, const char *end, int *tok_len)\n+{\n+\tchar *tok_start, *pos = *begin;\n+\twhile (pos != end && (*pos != ' ' && *pos != '\\t' && *pos != '\\n'))\n+\t\tpos++;\n+\ttok_start = *begin;\n+\t*tok_len = pos - *begin;\n+\t*begin = pos + 1;\n+\treturn tok_start;\n+}\n+\n+static char *get_next_line(char *const begin, const char *const end)\n+{\n+\tchar *pos = begin;\n+\twhile (pos != end && *pos++ != '\\n');\n+\treturn pos;\n+}\n+\n+static void show_fetch_remotes(FILE *output, const char *sm_name, const char *git_dir_path)\n+{\n+\tstruct child_process cp_remote = CHILD_PROCESS_INIT;\n+\tstruct strbuf sb_remote_out = STRBUF_INIT;\n+\n+\tcp_remote.git_cmd = 1;\n+\tstrvec_pushf(&cp_remote.env_array,\n+\t\t     \"GIT_DIR=%s\", git_dir_path);\n+\tstrvec_push(&cp_remote.env_array, \"GIT_WORK_TREE=.\");\n+\tstrvec_pushl(&cp_remote.args, \"remote\", \"-v\", NULL);\n+\tif (!capture_command(&cp_remote, &sb_remote_out, 0)) {\n+\t\tchar *line;\n+\t\tchar *begin = sb_remote_out.buf;\n+\t\tchar *end = sb_remote_out.buf + sb_remote_out.len;\n+\t\twhile (begin != end && (line = get_next_line(begin, end))) {\n+\t\t\tint namelen = 0, urllen = 0, taillen = 0;\n+\t\t\tchar *name = parse_token(&begin, line, &namelen);\n+\t\t\tchar *url = parse_token(&begin, line, &urllen);\n+\t\t\tchar *tail = parse_token(&begin, line, &taillen);\n+\t\t\tif (!memcmp(tail, \"(fetch)\", 7))\n+\t\t\t\tfprintf(output, \"  %.*s\\t%.*s\\n\",\n+\t\t\t\t\tnamelen, name, urllen, url);\n+\t\t}\n+\t}\n+\n+\tstrbuf_release(&sb_remote_out);\n+}\n+\n+static int add_submodule(const struct add_data *add_data)\n+{\n+\tchar *submod_gitdir_path;\n+\t/* perhaps the path already exists and is already a git repo, else clone it */\n+\tif (is_directory(add_data->sm_path)) {\n+\t\tsubmod_gitdir_path = xstrfmt(\"%s/.git\", add_data->sm_path);\n+\t\tif (is_directory(submod_gitdir_path) || file_exists(submod_gitdir_path))\n+\t\t\tprintf(_(\"Adding existing path at '%s' to index\\n\"),\n+\t\t\t       add_data->sm_path);\n+\t\telse\n+\t\t\tdie(_(\"'%s' already exists and is not a valid git repo\"),\n+\t\t\t    add_data->sm_path);\n+\t\tfree(submod_gitdir_path);\n+\t} else {\n+\t\tstruct strvec clone_args = STRVEC_INIT;\n+\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\t\tsubmod_gitdir_path = xstrfmt(\".git/modules/%s\", add_data->sm_name);\n+\n+\t\tif (is_directory(submod_gitdir_path)) {\n+\t\t\tif (!add_data->force) {\n+\t\t\t\terror(_(\"A git directory for '%s' is found \"\n+\t\t\t\t\t\"locally with remote(s):\"), add_data->sm_name);\n+\t\t\t\tshow_fetch_remotes(stderr, add_data->sm_name,\n+\t\t\t\t\t\t   submod_gitdir_path);\n+\t\t\t\tfprintf(stderr,\n+\t\t\t\t\t_(\"If you want to reuse this local git \"\n+\t\t\t\t\t  \"directory instead of cloning again from\\n\"\n+\t\t\t\t\t  \"  %s\\n\"\n+\t\t\t\t\t  \"use the '--force' option. If the local git \"\n+\t\t\t\t\t  \"directory is not the correct repo\\n\"\n+\t\t\t\t\t  \"or if you are unsure what this means, choose \"\n+\t\t\t\t\t  \"another name with the '--name' option.\\n\"),\n+\t\t\t\t\tadd_data->realrepo);\n+\t\t\t\tfree(submod_gitdir_path);\n+\t\t\t\treturn 1;\n+\t\t\t} else {\n+\t\t\t\tprintf(_(\"Reactivating local git directory for \"\n+\t\t\t\t\t \"submodule '%s'\\n\"), add_data->sm_name);\n+\t\t\t}\n+\t\t}\n+\t\tfree(submod_gitdir_path);\n+\n+\t\tstrvec_pushl(&clone_args, \"clone\", \"--path\", add_data->sm_path, \"--name\",\n+\t\t\t     add_data->sm_name, \"--url\", add_data->realrepo, NULL);\n+\t\tif (add_data->quiet)\n+\t\t\tstrvec_push(&clone_args, \"--quiet\");\n+\t\tif (add_data->progress)\n+\t\t\tstrvec_push(&clone_args, \"--progress\");\n+\t\tif (add_data->prefix)\n+\t\t\tstrvec_pushl(&clone_args, \"--prefix\", add_data->prefix, NULL);\n+\t\tif (add_data->reference_path)\n+\t\t\tstrvec_pushl(&clone_args, \"--reference\",\n+\t\t\t\t     add_data->reference_path, NULL);\n+\t\tif (add_data->dissociate)\n+\t\t\tstrvec_push(&clone_args, \"--dissociate\");\n+\t\tif (add_data->depth >= 0)\n+\t\t\tstrvec_pushf(&clone_args, \"--depth=%d\", add_data->depth);\n+\n+\t\tif (module_clone(clone_args.nr, clone_args.v, add_data->prefix)) {\n+\t\t\tstrvec_clear(&clone_args);\n+\t\t\treturn -1;\n+\t\t}\n+\t\tstrvec_clear(&clone_args);\n+\n+\t\tprepare_submodule_repo_env(&cp.env_array);\n+\t\tcp.git_cmd = 1;\n+\t\tcp.dir = add_data->sm_path;\n+\t\tstrvec_pushl(&cp.args, \"checkout\", \"-f\", \"-q\", NULL);\n+\n+\t\tif (add_data->branch) {\n+\t\t\tstrvec_pushl(&cp.args, \"-B\", add_data->branch, NULL);\n+\t\t\tstrvec_pushf(&cp.args, \"origin/%s\", add_data->branch);\n+\t\t}\n+\n+\t\tif (run_command(&cp))\n+\t\t\tdie(_(\"unable to checkout submodule '%s'\"), add_data->sm_path);\n+\t}\n+\treturn 0;\n+}\n+\n+static int add_clone(int argc, const char **argv, const char *prefix)\n+{\n+\tint force = 0, quiet = 0, dissociate = 0, progress = 0;\n+\tstruct add_data add_data = ADD_DATA_INIT;\n+\n+\tstruct option options[] = {\n+\t\tOPT_STRING('b', \"branch\", &add_data.branch,\n+\t\t\t   N_(\"branch\"),\n+\t\t\t   N_(\"branch of repository to checkout on cloning\")),\n+\t\tOPT_STRING(0, \"prefix\", &add_data.prefix,\n+\t\t\t   N_(\"path\"),\n+\t\t\t   N_(\"alternative anchor for relative paths\")),\n+\t\tOPT_STRING(0, \"path\", &add_data.sm_path,\n+\t\t\t   N_(\"path\"),\n+\t\t\t   N_(\"where the new submodule will be cloned to\")),\n+\t\tOPT_STRING(0, \"name\", &add_data.sm_name,\n+\t\t\t   N_(\"string\"),\n+\t\t\t   N_(\"name of the new submodule\")),\n+\t\tOPT_STRING(0, \"url\", &add_data.realrepo,\n+\t\t\t   N_(\"string\"),\n+\t\t\t   N_(\"url where to clone the submodule from\")),\n+\t\tOPT_STRING(0, \"reference\", &add_data.reference_path,\n+\t\t\t   N_(\"repo\"),\n+\t\t\t   N_(\"reference repository\")),\n+\t\tOPT_BOOL(0, \"dissociate\", &dissociate,\n+\t\t\t N_(\"use --reference only while cloning\")),\n+\t\tOPT_INTEGER(0, \"depth\", &add_data.depth,\n+\t\t\t    N_(\"depth for shallow clones\")),\n+\t\tOPT_BOOL(0, \"progress\", &progress,\n+\t\t\t N_(\"force cloning progress\")),\n+\t\tOPT_BOOL('f', \"force\", &force,\n+\t\t\t N_(\"allow adding an otherwise ignored submodule path\")),\n+\t\tOPT__QUIET(&quiet, \"Suppress output for cloning a submodule\"),\n+\t\tOPT_END()\n+\t};\n+\n+\tconst char *const usage[] = {\n+\t\tN_(\"git submodule--helper add-clone [--prefix=<path>] [--quiet] [--force] \"\n+\t\t   \"[--reference <repository>] [--depth <depth>] [-b|--branch <branch>]\"\n+\t\t   \"[--progress] [--dissociate] --url <url> --path <path> --name <name>\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, usage, 0);\n+\n+\tadd_data.progress = !!progress;\n+\tadd_data.dissociate = !!dissociate;\n+\tadd_data.force = !!force;\n+\tadd_data.quiet = !!quiet;\n+\n+\tif (add_submodule(&add_data))\n+\t\treturn 1;\n+\n+\treturn 0;\n+}\n+\n #define SUPPORT_SUPER_PREFIX (1<<0)\n \n struct cmd_struct {\n@@ -2757,6 +2955,7 @@ static struct cmd_struct commands[] = {\n \t{\"list\", module_list, 0},\n \t{\"name\", module_name, 0},\n \t{\"clone\", module_clone, 0},\n+\t{\"add-clone\", add_clone, 0},\n \t{\"update-module-mode\", module_update_module_mode, 0},\n \t{\"update-clone\", update_clone, 0},\n \t{\"ensure-core-worktree\", ensure_core_worktree, 0},\ndiff --git a/git-submodule.sh b/git-submodule.sh\nindex 4678378424..f71e1e5495 100755\n--- a/git-submodule.sh\n+++ b/git-submodule.sh\n@@ -241,43 +241,7 @@ cmd_add()\n \t\tdie \"$(eval_gettext \"'$sm_name' is not a valid submodule name\")\"\n \tfi\n \n-\t# perhaps the path exists and is already a git repo, else clone it\n-\tif test -e \"$sm_path\"\n-\tthen\n-\t\tif test -d \"$sm_path\"/.git || test -f \"$sm_path\"/.git\n-\t\tthen\n-\t\t\teval_gettextln \"Adding existing repo at '\\$sm_path' to the index\"\n-\t\telse\n-\t\t\tdie \"$(eval_gettext \"'\\$sm_path' already exists and is not a valid git repo\")\"\n-\t\tfi\n-\n-\telse\n-\t\tif test -d \".git/modules/$sm_name\"\n-\t\tthen\n-\t\t\tif test -z \"$force\"\n-\t\t\tthen\n-\t\t\t\teval_gettextln >&2 \"A git directory for '\\$sm_name' is found locally with remote(s):\"\n-\t\t\t\tGIT_DIR=\".git/modules/$sm_name\" GIT_WORK_TREE=. git remote -v | grep '(fetch)' | sed -e s,^,\"  \", -e s,' (fetch)',, >&2\n-\t\t\t\tdie \"$(eval_gettextln \"\\\n-If you want to reuse this local git directory instead of cloning again from\n-  \\$realrepo\n-use the '--force' option. If the local git directory is not the correct repo\n-or you are unsure what this means choose another name with the '--name' option.\")\"\n-\t\t\telse\n-\t\t\t\teval_gettextln \"Reactivating local git directory for submodule '\\$sm_name'.\"\n-\t\t\tfi\n-\t\tfi\n-\t\tgit 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\n-\t\t(\n-\t\t\tsanitize_submodule_env\n-\t\t\tcd \"$sm_path\" &&\n-\t\t\t# ash fails to wordsplit ${branch:+-b \"$branch\"...}\n-\t\t\tcase \"$branch\" in\n-\t\t\t'') git checkout -f -q ;;\n-\t\t\t?*) git checkout -f -q -B \"$branch\" \"origin/$branch\" ;;\n-\t\t\tesac\n-\t\t) || die \"$(eval_gettext \"Unable to checkout submodule '\\$sm_path'\")\"\n-\tfi\n+\tgit 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\n \tgit config submodule.\"$sm_name\".url \"$realrepo\"\n \n \tgit add --no-warn-embedded-repo $force \"$sm_path\" ||\n-- \n2.31.1\n\n"},{"id":"426519","messageId":"2422ea94-473e-2fa9-a3cf-f22f941e9b44@gmail.com","threadId":"55844","inReplyTo":"20210605113913.29005-2-raykar.ath@gmail.com","subject":"Re: [GSoC] [PATCH 1/2] submodule--helper: introduce add-clone subcommand","fromName":"Bagas Sanjaya","fromEmail":"bagasdotme@gmail.com","sentAt":"2021-06-06T03:38:00Z","receivedAt":"2021-06-06T03:39:20Z","isPatch":true,"sender":{"key":"bagasdotme@gmail.com","avatar":"https://avatars.githubusercontent.com/u/40219486?v=4"},"body":"Hi,\n\nOn 05/06/21 18.39, Atharva Raykar wrote:\n> +\tgit 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\n\nWhat is the exit code you mean in case of error?\n\n>   \tgit config submodule.\"$sm_name\".url \"$realrepo\"\n>   \n>   \tgit add --no-warn-embedded-repo $force \"$sm_path\" ||\n> \n\nIs `git` executable that found in $PATH used? I have both \ndistro-packaged Git and custom-compiled Git installed, and I would like \nthe latter to be able to use git-submodule from its own install prefix \n(/path/to/git-prefix or whatsever).\n\n-- \nAn old man doll... just what I always wanted! - Clara\n"},{"id":"426525","messageId":"CAP8UFD0oXjxRCXi12nVWtKWBm+tAOcRhZ5g+1tM8qP+5nKbLpQ@mail.gmail.com","threadId":"55844","inReplyTo":"2422ea94-473e-2fa9-a3cf-f22f941e9b44@gmail.com","subject":"Re: [GSoC] [PATCH 1/2] submodule--helper: introduce add-clone subcommand","fromName":"Christian Couder","fromEmail":"christian.couder@gmail.com","sentAt":"2021-06-06T09:06:05Z","receivedAt":"2021-06-06T09:07:34Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"Hi,\n\nOn Sun, Jun 6, 2021 at 5:38 AM Bagas Sanjaya <bagasdotme@gmail.com> wrote:\n>\n> Hi,\n>\n> On 05/06/21 18.39, Atharva Raykar wrote:\n> > +     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\n>\n> What is the exit code you mean in case of error?\n\nThe exit code of the above is the same as the exit code of what's\nbefore `|| exit`.\n\nFor example:\n\n$ ( ( exit 10 ) || exit ); echo $?\n10\n\n> >       git config submodule.\"$sm_name\".url \"$realrepo\"\n> >\n> >       git add --no-warn-embedded-repo $force \"$sm_path\" ||\n>\n> Is `git` executable that found in $PATH used? I have both\n> distro-packaged Git and custom-compiled Git installed, and I would like\n> the latter to be able to use git-submodule from its own install prefix\n> (/path/to/git-prefix or whatsever).\n\nThis is a different issue than what this patch is doing.\n"},{"id":"426562","messageId":"CAP8UFD2kwSB60kF_cjs4JYSYeXzvjAtz0OuFOfTqEU7F+ijR-w@mail.gmail.com","threadId":"55844","inReplyTo":"20210605113913.29005-3-raykar.ath@gmail.com","subject":"Re: [GSoC] [PATCH 2/2] submodule--helper: introduce add-config subcommand","fromName":"Christian Couder","fromEmail":"christian.couder@gmail.com","sentAt":"2021-06-07T09:24:37Z","receivedAt":"2021-06-07T09:25:06Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"On Sat, Jun 5, 2021 at 1:42 PM Atharva Raykar <raykar.ath@gmail.com> wrote:\n>\n> Add a new \"add-config\" subcommand to `git submodule--helper` with the\n> goal of converting part of the shell code in git-submodule.sh related to\n> `git submodule add` into C code. This new subcommand sets the\n> configuration variables of a newly added submodule, by registering the\n> url in local git config, as well as the submodule name and path in the\n> .gitmodules file. It also sets 'submodule.<name>.active' to \"true\" if\n> the submodule path has not already been covered by any pathspec\n> specified in 'submodule.active'.\n>\n> This is meant to be a faithful conversion from shell to C, with only one\n> minor change: A warning is emitted if no value is specified in\n> 'submodule.active', ie, the config looks like: \"[submodule] active\\n\".\n\nMaybe explaining a bit how this warning is useful could help reviewers\nhere. Especially what could happen if no value is specified in\n'submodule.active'?\n\n> The structure of the conditional to check if we need to set the 'active'\n> toggle looks different from the shell version -- but behaves the same.\n> The change was made to decrease code duplication. A comment has been\n> added to explain that only one value of 'submodule.active' is obtained\n> to check if we need to call is_submodule_active() at all.\n>\n> This is part of a series of changes that will result in all of\n> 'submodule add' being converted to C.\n\n[...]\n\n\n> +       /*\n> +        * NEEDSWORK: In a multi-working-tree world this needs to be\n> +        * set in the per-worktree config.\n> +        */\n> +       pathspec_key_exists = !git_config_get_string(\"submodule.active\",\n> +                                                    &submod_pathspec);\n> +       if (pathspec_key_exists && !submod_pathspec)\n> +               warning(_(\"The submodule.active configuration exists, but \"\n> +                         \"no pathspec was specified. Setting the value of \"\n> +                         \"submodule.%s.active to 'true'.\"), add_data->sm_name);\n\nIt's not very clear that we will actually set\n'submodule.<name>.active' to true below as it depends on the result of\ncalling is_submodule_active(), and anyway is_submodule_active() will\ncheck again if \"submodule.active\" is set, which is wasteful.\n\nMaybe we could set a variable, called for example \"activate\" here,\nwith something like:\n\n       if (pathspec_key_exists && !submod_pathspec) {\n               warning(...);\n               activate = 1;\n      }\n\nand below use a check like:\n\n       if (!pathspec_key_exists || activate ||\n           !is_submodule_active(the_repository, add_data->sm_path)) {\n...\n\n> +       /*\n> +        * If submodule.active does not exist, we will activate this\n> +        * module unconditionally.\n> +        *\n> +        * Otherwise, we ask is_submodule_active(), which iterates\n> +        * through all the values of 'submodule.active' to determine\n> +        * if this module is already active.\n> +        */\n> +       if (!pathspec_key_exists ||\n> +           !is_submodule_active(the_repository, add_data->sm_path)) {\n> +               key = xstrfmt(\"submodule.%s.active\", add_data->sm_name);\n> +               git_config_set_gently(key, \"true\");\n> +               free(key);\n> +       }\n> +}\n\nThe rest looks good to me! Thanks!\n"},{"id":"426573","messageId":"441792CB-259C-4635-8B00-9511E8D1FC3C@gmail.com","threadId":"55844","inReplyTo":"CAP8UFD2kwSB60kF_cjs4JYSYeXzvjAtz0OuFOfTqEU7F+ijR-w@mail.gmail.com","subject":"Re: [GSoC] [PATCH 2/2] submodule--helper: introduce add-config subcommand","fromName":"Atharva Raykar","fromEmail":"raykar.ath@gmail.com","sentAt":"2021-06-07T11:24:27Z","receivedAt":"2021-06-07T11:25:33Z","isPatch":true,"sender":{"key":"raykar.ath@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24277692?v=4"},"body":"On 07-Jun-2021, at 14:54, Christian Couder <christian.couder@gmail.com> wrote:\n> \n> On Sat, Jun 5, 2021 at 1:42 PM Atharva Raykar <raykar.ath@gmail.com> wrote:\n>> \n>> Add a new \"add-config\" subcommand to `git submodule--helper` with the\n>> goal of converting part of the shell code in git-submodule.sh related to\n>> `git submodule add` into C code. This new subcommand sets the\n>> configuration variables of a newly added submodule, by registering the\n>> url in local git config, as well as the submodule name and path in the\n>> .gitmodules file. It also sets 'submodule.<name>.active' to \"true\" if\n>> the submodule path has not already been covered by any pathspec\n>> specified in 'submodule.active'.\n>> \n>> This is meant to be a faithful conversion from shell to C, with only one\n>> minor change: A warning is emitted if no value is specified in\n>> 'submodule.active', ie, the config looks like: \"[submodule] active\\n\".\n> \n> Maybe explaining a bit how this warning is useful could help reviewers\n> here. Especially what could happen if no value is specified in\n> 'submodule.active'?\n\nWill do.\n\nFor now I'll leave an explanation here as well, so that those\nwho might see this thread can know the motivation behind it.\n\n(I'll make it more concise in my cover letter of v2)\n\nJunio in his review of Shourya's patch[1] said:\n> When a user has \"[submodule] active\" in his or her\n> configuration file, it is a configuration error.  When Git reads\n> \"submodule.active\" configuration variable to make a decision (like\n> the above code) and finds that the user has such an error, the user\n> would appreciate if the error is pointed out, so that it can be\n> corrected, rather than silently ignored.\n\nGit might set 'submodule.<name>.active = true' in the above case, and\nsince it has a higher priority[2] than the 'submodule.active' switch,\nit would be useful to let the user know of this change, and the incorrect\nconfiguration that led to this behaviour, so that they may be able to\nremedy it, and avoid surprises later on.\n\n[1] https://lore.kernel.org/git/xmqqo8isxefz.fsf@gitster.c.googlers.com/\n[2] https://git-scm.com/docs/gitsubmodules#_active_submodules\n\n>> The structure of the conditional to check if we need to set the 'active'\n>> toggle looks different from the shell version -- but behaves the same.\n>> The change was made to decrease code duplication. A comment has been\n>> added to explain that only one value of 'submodule.active' is obtained\n>> to check if we need to call is_submodule_active() at all.\n>> \n>> This is part of a series of changes that will result in all of\n>> 'submodule add' being converted to C.\n> \n> [...]\n> \n> \n>> +       /*\n>> +        * NEEDSWORK: In a multi-working-tree world this needs to be\n>> +        * set in the per-worktree config.\n>> +        */\n>> +       pathspec_key_exists = !git_config_get_string(\"submodule.active\",\n>> +                                                    &submod_pathspec);\n>> +       if (pathspec_key_exists && !submod_pathspec)\n>> +               warning(_(\"The submodule.active configuration exists, but \"\n>> +                         \"no pathspec was specified. Setting the value of \"\n>> +                         \"submodule.%s.active to 'true'.\"), add_data->sm_name);\n> \n> It's not very clear that we will actually set\n> 'submodule.<name>.active' to true below as it depends on the result of\n> calling is_submodule_active()\n\nHmm, I see the issue.\n\nWould it be more accurate to say this:\n\n\"The submodule.active configuration exists, but no pathspec\nwas specified. If the module is not already active, the value\nof 'submodule.<name>.active' will be set to 'true'.\"\n\n> , and anyway is_submodule_active() will\n> check again if \"submodule.active\" is set, which is wasteful.\n> \n> Maybe we could set a variable, called for example \"activate\" here,\n> with something like:\n> \n>       if (pathspec_key_exists && !submod_pathspec) {\n>               warning(...);\n>               activate = 1;\n>      }\n> \n> and below use a check like:\n> \n>       if (!pathspec_key_exists || activate ||\n>           !is_submodule_active(the_repository, add_data->sm_path)) {\n\nGot it. Thanks for suggesting this improvement!\n\n> ...\n> \n>> +       /*\n>> +        * If submodule.active does not exist, we will activate this\n>> +        * module unconditionally.\n>> +        *\n>> +        * Otherwise, we ask is_submodule_active(), which iterates\n>> +        * through all the values of 'submodule.active' to determine\n>> +        * if this module is already active.\n>> +        */\n>> +       if (!pathspec_key_exists ||\n>> +           !is_submodule_active(the_repository, add_data->sm_path)) {\n>> +               key = xstrfmt(\"submodule.%s.active\", add_data->sm_name);\n>> +               git_config_set_gently(key, \"true\");\n>> +               free(key);\n>> +       }\n>> +}\n> \n> The rest looks good to me! Thanks!\n\n:^)"},{"id":"426751","messageId":"20210608095655.47324-1-raykar.ath@gmail.com","threadId":"55844","inReplyTo":"20210605113913.29005-1-raykar.ath@gmail.com","subject":"[GSoC] [PATCH v2 0/2] submodule--helper: introduce subcommands for sh to C conversion","fromName":"Atharva Raykar","fromEmail":"raykar.ath@gmail.com","sentAt":"2021-06-08T09:56:53Z","receivedAt":"2021-06-08T09:58:17Z","isPatch":true,"sender":{"key":"raykar.ath@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24277692?v=4"},"body":"I have elaborated the commit message more, to explain why a warning is emitted\nfor an empty value in 'submodule.active'. The warning has been worded more\naccurately than before.\n\nAn unnecessary extra check for 'submodule.active' has been avoided.\n\nI have included a range diff, in case that is useful.\n\nMy fork containing these changes can be found at:\nhttps://github.com/tfidfwastaken/git/commits/submodule-add-in-c\n\nEmily and Jonathan: To be on the safe side, I have CC'd you so you know where I\nkeep my changes, and we can avoid potential conflicts, as I believe you are\nworking on this area as well. Just so you know, every week, I update the link to\nall my ongoing work at:\nhttps://atharvaraykar.me/gitnotes/#my-public-gitgit-branches\n\nAtharva Raykar (2):\n  submodule--helper: introduce add-clone subcommand\n  submodule--helper: introduce add-config subcommand\n\n builtin/submodule--helper.c | 315 ++++++++++++++++++++++++++++++++++++\n git-submodule.sh            |  66 +-------\n 2 files changed, 317 insertions(+), 64 deletions(-)\n\nRange-diff against v1:\n1:  398bfa713d = 1:  4374ebb6b1 submodule--helper: introduce add-clone subcommand\n2:  f9954cfcf7 ! 2:  406022d0f7 submodule--helper: introduce add-config subcommand\n    @@ Commit message\n     \n         This is meant to be a faithful conversion from shell to C, with only one\n         minor change: A warning is emitted if no value is specified in\n    -    'submodule.active', ie, the config looks like: \"[submodule] active\\n\".\n    +    'submodule.active', ie, the config looks like: \"[submodule] active\\n\",\n    +    because it is an invalid configuration. It would be helpful to let the\n    +    user know that the pathspec is unset, and the value of\n    +    'submodule.<name>.active' might be set to 'true' so that they can\n    +    rectify their configuration and prevent future surprises (especially\n    +    given that the latter variable has a higher priority than the former).\n     \n         The structure of the conditional to check if we need to set the 'active'\n         toggle looks different from the shell version -- but behaves the same.\n    @@ builtin/submodule--helper.c: static int add_clone(int argc, const char **argv, c\n     +\tchar *key, *submod_pathspec = NULL;\n     +\tstruct child_process add_submod = CHILD_PROCESS_INIT;\n     +\tstruct child_process add_gitmodules = CHILD_PROCESS_INIT;\n    -+\tint pathspec_key_exists;\n    ++\tint pathspec_key_exists, activate = 0;\n     +\n     +\tkey = xstrfmt(\"submodule.%s.url\", add_data->sm_name);\n     +\tgit_config_set_gently(key, add_data->realrepo);\n    @@ builtin/submodule--helper.c: static int add_clone(int argc, const char **argv, c\n     +\t */\n     +\tpathspec_key_exists = !git_config_get_string(\"submodule.active\",\n     +\t\t\t\t\t\t     &submod_pathspec);\n    -+\tif (pathspec_key_exists && !submod_pathspec)\n    -+\t\twarning(_(\"The submodule.active configuration exists, but \"\n    -+\t\t\t  \"no pathspec was specified. Setting the value of \"\n    -+\t\t\t  \"submodule.%s.active to 'true'.\"), add_data->sm_name);\n    ++\tif (pathspec_key_exists && !submod_pathspec) {\n    ++\t\twarning(_(\"The submodule.active configuration exists, but the \"\n    ++\t\t\t  \"pathspec was unset. If the submodule is not already \"\n    ++\t\t\t  \"active, the value of submodule.%s.active will be \"\n    ++\t\t\t  \"be set to 'true'.\"), add_data->sm_name);\n    ++\t\tactivate = 1;\n    ++\t}\n     +\n     +\t/*\n    -+\t * If submodule.active does not exist, we will activate this\n    -+\t * module unconditionally.\n    ++\t * If submodule.active does not exist, or if the pathspec was unset,\n    ++\t * we will activate this module unconditionally.\n     +\t *\n     +\t * Otherwise, we ask is_submodule_active(), which iterates\n     +\t * through all the values of 'submodule.active' to determine\n     +\t * if this module is already active.\n     +\t */\n    -+\tif (!pathspec_key_exists ||\n    ++\tif (!pathspec_key_exists || activate ||\n     +\t    !is_submodule_active(the_repository, add_data->sm_path)) {\n     +\t\tkey = xstrfmt(\"submodule.%s.active\", add_data->sm_name);\n     +\t\tgit_config_set_gently(key, \"true\");\n-- \n2.31.1\n\n"},{"id":"426752","messageId":"20210608095655.47324-2-raykar.ath@gmail.com","threadId":"55844","inReplyTo":"20210608095655.47324-1-raykar.ath@gmail.com","subject":"[GSoC] [PATCH v2 1/2] submodule--helper: introduce add-clone subcommand","fromName":"Atharva Raykar","fromEmail":"raykar.ath@gmail.com","sentAt":"2021-06-08T09:56:54Z","receivedAt":"2021-06-08T09:59:13Z","isPatch":true,"sender":{"key":"raykar.ath@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24277692?v=4"},"body":"Let's add a new \"add-clone\" subcommand to `git submodule--helper` with\nthe goal of converting part of the shell code in git-submodule.sh\nrelated to `git submodule add` into C code. This new subcommand clones\nthe repository that is to be added, and checks out to the appropriate\nbranch.\n\nThis is meant to be a faithful conversion that leaves the behaviour of\n'submodule add' unchanged. The only minor change is that if a submodule name has\nbeen supplied with a name that clashes with a local submodule, the message shown\nto the user (\"A git directory for 'foo' is found locally...\") is prepended with\n\"error\" for clarity.\n\nThis is part of a series of changes that will result in all of 'submodule add'\nbeing converted to C.\n\nSigned-off-by: Atharva Raykar <raykar.ath@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Shourya Shukla <shouryashukla.oo@gmail.com>\nBased-on-patch-by: Shourya Shukla <shouryashukla.oo@gmail.com>\nBased-on-patch-by: Prathamesh Chavan <pc44800@gmail.com>\n---\n builtin/submodule--helper.c | 199 ++++++++++++++++++++++++++++++++++++\n git-submodule.sh            |  38 +------\n 2 files changed, 200 insertions(+), 37 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex d55f6262e9..c9cb535312 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2745,6 +2745,204 @@ static int module_set_branch(int argc, const char **argv, const char *prefix)\n \treturn !!ret;\n }\n \n+struct add_data {\n+\tconst char *prefix;\n+\tconst char *branch;\n+\tconst char *reference_path;\n+\tconst char *sm_path;\n+\tconst char *sm_name;\n+\tconst char *repo;\n+\tconst char *realrepo;\n+\tint depth;\n+\tunsigned int force: 1;\n+\tunsigned int quiet: 1;\n+\tunsigned int progress: 1;\n+\tunsigned int dissociate: 1;\n+};\n+#define ADD_DATA_INIT { .depth = -1 }\n+\n+static char *parse_token(char **begin, const char *end, int *tok_len)\n+{\n+\tchar *tok_start, *pos = *begin;\n+\twhile (pos != end && (*pos != ' ' && *pos != '\\t' && *pos != '\\n'))\n+\t\tpos++;\n+\ttok_start = *begin;\n+\t*tok_len = pos - *begin;\n+\t*begin = pos + 1;\n+\treturn tok_start;\n+}\n+\n+static char *get_next_line(char *const begin, const char *const end)\n+{\n+\tchar *pos = begin;\n+\twhile (pos != end && *pos++ != '\\n');\n+\treturn pos;\n+}\n+\n+static void show_fetch_remotes(FILE *output, const char *sm_name, const char *git_dir_path)\n+{\n+\tstruct child_process cp_remote = CHILD_PROCESS_INIT;\n+\tstruct strbuf sb_remote_out = STRBUF_INIT;\n+\n+\tcp_remote.git_cmd = 1;\n+\tstrvec_pushf(&cp_remote.env_array,\n+\t\t     \"GIT_DIR=%s\", git_dir_path);\n+\tstrvec_push(&cp_remote.env_array, \"GIT_WORK_TREE=.\");\n+\tstrvec_pushl(&cp_remote.args, \"remote\", \"-v\", NULL);\n+\tif (!capture_command(&cp_remote, &sb_remote_out, 0)) {\n+\t\tchar *line;\n+\t\tchar *begin = sb_remote_out.buf;\n+\t\tchar *end = sb_remote_out.buf + sb_remote_out.len;\n+\t\twhile (begin != end && (line = get_next_line(begin, end))) {\n+\t\t\tint namelen = 0, urllen = 0, taillen = 0;\n+\t\t\tchar *name = parse_token(&begin, line, &namelen);\n+\t\t\tchar *url = parse_token(&begin, line, &urllen);\n+\t\t\tchar *tail = parse_token(&begin, line, &taillen);\n+\t\t\tif (!memcmp(tail, \"(fetch)\", 7))\n+\t\t\t\tfprintf(output, \"  %.*s\\t%.*s\\n\",\n+\t\t\t\t\tnamelen, name, urllen, url);\n+\t\t}\n+\t}\n+\n+\tstrbuf_release(&sb_remote_out);\n+}\n+\n+static int add_submodule(const struct add_data *add_data)\n+{\n+\tchar *submod_gitdir_path;\n+\t/* perhaps the path already exists and is already a git repo, else clone it */\n+\tif (is_directory(add_data->sm_path)) {\n+\t\tsubmod_gitdir_path = xstrfmt(\"%s/.git\", add_data->sm_path);\n+\t\tif (is_directory(submod_gitdir_path) || file_exists(submod_gitdir_path))\n+\t\t\tprintf(_(\"Adding existing path at '%s' to index\\n\"),\n+\t\t\t       add_data->sm_path);\n+\t\telse\n+\t\t\tdie(_(\"'%s' already exists and is not a valid git repo\"),\n+\t\t\t    add_data->sm_path);\n+\t\tfree(submod_gitdir_path);\n+\t} else {\n+\t\tstruct strvec clone_args = STRVEC_INIT;\n+\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\t\tsubmod_gitdir_path = xstrfmt(\".git/modules/%s\", add_data->sm_name);\n+\n+\t\tif (is_directory(submod_gitdir_path)) {\n+\t\t\tif (!add_data->force) {\n+\t\t\t\terror(_(\"A git directory for '%s' is found \"\n+\t\t\t\t\t\"locally with remote(s):\"), add_data->sm_name);\n+\t\t\t\tshow_fetch_remotes(stderr, add_data->sm_name,\n+\t\t\t\t\t\t   submod_gitdir_path);\n+\t\t\t\tfprintf(stderr,\n+\t\t\t\t\t_(\"If you want to reuse this local git \"\n+\t\t\t\t\t  \"directory instead of cloning again from\\n\"\n+\t\t\t\t\t  \"  %s\\n\"\n+\t\t\t\t\t  \"use the '--force' option. If the local git \"\n+\t\t\t\t\t  \"directory is not the correct repo\\n\"\n+\t\t\t\t\t  \"or if you are unsure what this means, choose \"\n+\t\t\t\t\t  \"another name with the '--name' option.\\n\"),\n+\t\t\t\t\tadd_data->realrepo);\n+\t\t\t\tfree(submod_gitdir_path);\n+\t\t\t\treturn 1;\n+\t\t\t} else {\n+\t\t\t\tprintf(_(\"Reactivating local git directory for \"\n+\t\t\t\t\t \"submodule '%s'\\n\"), add_data->sm_name);\n+\t\t\t}\n+\t\t}\n+\t\tfree(submod_gitdir_path);\n+\n+\t\tstrvec_pushl(&clone_args, \"clone\", \"--path\", add_data->sm_path, \"--name\",\n+\t\t\t     add_data->sm_name, \"--url\", add_data->realrepo, NULL);\n+\t\tif (add_data->quiet)\n+\t\t\tstrvec_push(&clone_args, \"--quiet\");\n+\t\tif (add_data->progress)\n+\t\t\tstrvec_push(&clone_args, \"--progress\");\n+\t\tif (add_data->prefix)\n+\t\t\tstrvec_pushl(&clone_args, \"--prefix\", add_data->prefix, NULL);\n+\t\tif (add_data->reference_path)\n+\t\t\tstrvec_pushl(&clone_args, \"--reference\",\n+\t\t\t\t     add_data->reference_path, NULL);\n+\t\tif (add_data->dissociate)\n+\t\t\tstrvec_push(&clone_args, \"--dissociate\");\n+\t\tif (add_data->depth >= 0)\n+\t\t\tstrvec_pushf(&clone_args, \"--depth=%d\", add_data->depth);\n+\n+\t\tif (module_clone(clone_args.nr, clone_args.v, add_data->prefix)) {\n+\t\t\tstrvec_clear(&clone_args);\n+\t\t\treturn -1;\n+\t\t}\n+\t\tstrvec_clear(&clone_args);\n+\n+\t\tprepare_submodule_repo_env(&cp.env_array);\n+\t\tcp.git_cmd = 1;\n+\t\tcp.dir = add_data->sm_path;\n+\t\tstrvec_pushl(&cp.args, \"checkout\", \"-f\", \"-q\", NULL);\n+\n+\t\tif (add_data->branch) {\n+\t\t\tstrvec_pushl(&cp.args, \"-B\", add_data->branch, NULL);\n+\t\t\tstrvec_pushf(&cp.args, \"origin/%s\", add_data->branch);\n+\t\t}\n+\n+\t\tif (run_command(&cp))\n+\t\t\tdie(_(\"unable to checkout submodule '%s'\"), add_data->sm_path);\n+\t}\n+\treturn 0;\n+}\n+\n+static int add_clone(int argc, const char **argv, const char *prefix)\n+{\n+\tint force = 0, quiet = 0, dissociate = 0, progress = 0;\n+\tstruct add_data add_data = ADD_DATA_INIT;\n+\n+\tstruct option options[] = {\n+\t\tOPT_STRING('b', \"branch\", &add_data.branch,\n+\t\t\t   N_(\"branch\"),\n+\t\t\t   N_(\"branch of repository to checkout on cloning\")),\n+\t\tOPT_STRING(0, \"prefix\", &add_data.prefix,\n+\t\t\t   N_(\"path\"),\n+\t\t\t   N_(\"alternative anchor for relative paths\")),\n+\t\tOPT_STRING(0, \"path\", &add_data.sm_path,\n+\t\t\t   N_(\"path\"),\n+\t\t\t   N_(\"where the new submodule will be cloned to\")),\n+\t\tOPT_STRING(0, \"name\", &add_data.sm_name,\n+\t\t\t   N_(\"string\"),\n+\t\t\t   N_(\"name of the new submodule\")),\n+\t\tOPT_STRING(0, \"url\", &add_data.realrepo,\n+\t\t\t   N_(\"string\"),\n+\t\t\t   N_(\"url where to clone the submodule from\")),\n+\t\tOPT_STRING(0, \"reference\", &add_data.reference_path,\n+\t\t\t   N_(\"repo\"),\n+\t\t\t   N_(\"reference repository\")),\n+\t\tOPT_BOOL(0, \"dissociate\", &dissociate,\n+\t\t\t N_(\"use --reference only while cloning\")),\n+\t\tOPT_INTEGER(0, \"depth\", &add_data.depth,\n+\t\t\t    N_(\"depth for shallow clones\")),\n+\t\tOPT_BOOL(0, \"progress\", &progress,\n+\t\t\t N_(\"force cloning progress\")),\n+\t\tOPT_BOOL('f', \"force\", &force,\n+\t\t\t N_(\"allow adding an otherwise ignored submodule path\")),\n+\t\tOPT__QUIET(&quiet, \"Suppress output for cloning a submodule\"),\n+\t\tOPT_END()\n+\t};\n+\n+\tconst char *const usage[] = {\n+\t\tN_(\"git submodule--helper add-clone [--prefix=<path>] [--quiet] [--force] \"\n+\t\t   \"[--reference <repository>] [--depth <depth>] [-b|--branch <branch>]\"\n+\t\t   \"[--progress] [--dissociate] --url <url> --path <path> --name <name>\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, usage, 0);\n+\n+\tadd_data.progress = !!progress;\n+\tadd_data.dissociate = !!dissociate;\n+\tadd_data.force = !!force;\n+\tadd_data.quiet = !!quiet;\n+\n+\tif (add_submodule(&add_data))\n+\t\treturn 1;\n+\n+\treturn 0;\n+}\n+\n #define SUPPORT_SUPER_PREFIX (1<<0)\n \n struct cmd_struct {\n@@ -2757,6 +2955,7 @@ static struct cmd_struct commands[] = {\n \t{\"list\", module_list, 0},\n \t{\"name\", module_name, 0},\n \t{\"clone\", module_clone, 0},\n+\t{\"add-clone\", add_clone, 0},\n \t{\"update-module-mode\", module_update_module_mode, 0},\n \t{\"update-clone\", update_clone, 0},\n \t{\"ensure-core-worktree\", ensure_core_worktree, 0},\ndiff --git a/git-submodule.sh b/git-submodule.sh\nindex 4678378424..f71e1e5495 100755\n--- a/git-submodule.sh\n+++ b/git-submodule.sh\n@@ -241,43 +241,7 @@ cmd_add()\n \t\tdie \"$(eval_gettext \"'$sm_name' is not a valid submodule name\")\"\n \tfi\n \n-\t# perhaps the path exists and is already a git repo, else clone it\n-\tif test -e \"$sm_path\"\n-\tthen\n-\t\tif test -d \"$sm_path\"/.git || test -f \"$sm_path\"/.git\n-\t\tthen\n-\t\t\teval_gettextln \"Adding existing repo at '\\$sm_path' to the index\"\n-\t\telse\n-\t\t\tdie \"$(eval_gettext \"'\\$sm_path' already exists and is not a valid git repo\")\"\n-\t\tfi\n-\n-\telse\n-\t\tif test -d \".git/modules/$sm_name\"\n-\t\tthen\n-\t\t\tif test -z \"$force\"\n-\t\t\tthen\n-\t\t\t\teval_gettextln >&2 \"A git directory for '\\$sm_name' is found locally with remote(s):\"\n-\t\t\t\tGIT_DIR=\".git/modules/$sm_name\" GIT_WORK_TREE=. git remote -v | grep '(fetch)' | sed -e s,^,\"  \", -e s,' (fetch)',, >&2\n-\t\t\t\tdie \"$(eval_gettextln \"\\\n-If you want to reuse this local git directory instead of cloning again from\n-  \\$realrepo\n-use the '--force' option. If the local git directory is not the correct repo\n-or you are unsure what this means choose another name with the '--name' option.\")\"\n-\t\t\telse\n-\t\t\t\teval_gettextln \"Reactivating local git directory for submodule '\\$sm_name'.\"\n-\t\t\tfi\n-\t\tfi\n-\t\tgit 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\n-\t\t(\n-\t\t\tsanitize_submodule_env\n-\t\t\tcd \"$sm_path\" &&\n-\t\t\t# ash fails to wordsplit ${branch:+-b \"$branch\"...}\n-\t\t\tcase \"$branch\" in\n-\t\t\t'') git checkout -f -q ;;\n-\t\t\t?*) git checkout -f -q -B \"$branch\" \"origin/$branch\" ;;\n-\t\t\tesac\n-\t\t) || die \"$(eval_gettext \"Unable to checkout submodule '\\$sm_path'\")\"\n-\tfi\n+\tgit 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\n \tgit config submodule.\"$sm_name\".url \"$realrepo\"\n \n \tgit add --no-warn-embedded-repo $force \"$sm_path\" ||\n-- \n2.31.1\n\n"},{"id":"426753","messageId":"20210608095655.47324-3-raykar.ath@gmail.com","threadId":"55844","inReplyTo":"20210608095655.47324-1-raykar.ath@gmail.com","subject":"[GSoC] [PATCH v2 2/2] submodule--helper: introduce add-config subcommand","fromName":"Atharva Raykar","fromEmail":"raykar.ath@gmail.com","sentAt":"2021-06-08T09:56:55Z","receivedAt":"2021-06-08T09:59:18Z","isPatch":true,"sender":{"key":"raykar.ath@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24277692?v=4"},"body":"Add a new \"add-config\" subcommand to `git submodule--helper` with the\ngoal of converting part of the shell code in git-submodule.sh related to\n`git submodule add` into C code. This new subcommand sets the\nconfiguration variables of a newly added submodule, by registering the\nurl in local git config, as well as the submodule name and path in the\n.gitmodules file. It also sets 'submodule.<name>.active' to \"true\" if\nthe submodule path has not already been covered by any pathspec\nspecified in 'submodule.active'.\n\nThis is meant to be a faithful conversion from shell to C, with only one\nminor change: A warning is emitted if no value is specified in\n'submodule.active', ie, the config looks like: \"[submodule] active\\n\",\nbecause it is an invalid configuration. It would be helpful to let the\nuser know that the pathspec is unset, and the value of\n'submodule.<name>.active' might be set to 'true' so that they can\nrectify their configuration and prevent future surprises (especially\ngiven that the latter variable has a higher priority than the former).\n\nThe structure of the conditional to check if we need to set the 'active'\ntoggle looks different from the shell version -- but behaves the same.\nThe change was made to decrease code duplication. A comment has been\nadded to explain that only one value of 'submodule.active' is obtained\nto check if we need to call is_submodule_active() at all.\n\nThis is part of a series of changes that will result in all of\n'submodule add' being converted to C.\n\nSigned-off-by: Atharva Raykar <raykar.ath@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Shourya Shukla <shouryashukla.oo@gmail.com>\nBased-on-patch-by: Shourya Shukla <shouryashukla.oo@gmail.com>\nBased-on-patch-by: Prathamesh Chavan <pc44800@gmail.com>\n---\n builtin/submodule--helper.c | 116 ++++++++++++++++++++++++++++++++++++\n git-submodule.sh            |  28 +--------\n 2 files changed, 117 insertions(+), 27 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex c9cb535312..d7225f503d 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2943,6 +2943,121 @@ static int add_clone(int argc, const char **argv, const char *prefix)\n \treturn 0;\n }\n \n+static void configure_added_submodule(struct add_data *add_data)\n+{\n+\tchar *key, *submod_pathspec = NULL;\n+\tstruct child_process add_submod = CHILD_PROCESS_INIT;\n+\tstruct child_process add_gitmodules = CHILD_PROCESS_INIT;\n+\tint pathspec_key_exists, activate = 0;\n+\n+\tkey = xstrfmt(\"submodule.%s.url\", add_data->sm_name);\n+\tgit_config_set_gently(key, add_data->realrepo);\n+\tfree(key);\n+\n+\tadd_submod.git_cmd = 1;\n+\tstrvec_pushl(&add_submod.args, \"add\",\n+\t\t     \"--no-warn-embedded-repo\", NULL);\n+\tif (add_data->force)\n+\t\tstrvec_push(&add_submod.args, \"--force\");\n+\tstrvec_pushl(&add_submod.args, \"--\", add_data->sm_path, NULL);\n+\n+\tif (run_command(&add_submod))\n+\t\tdie(_(\"Failed to add submodule '%s'\"), add_data->sm_path);\n+\n+\tkey = xstrfmt(\"submodule.%s.path\", add_data->sm_name);\n+\tconfig_set_in_gitmodules_file_gently(key, add_data->sm_path);\n+\tfree(key);\n+\tkey = xstrfmt(\"submodule.%s.url\", add_data->sm_name);\n+\tconfig_set_in_gitmodules_file_gently(key, add_data->repo);\n+\tfree(key);\n+\tif (add_data->branch) {\n+\t\tkey = xstrfmt(\"submodule.%s.branch\", add_data->sm_path);\n+\t\tconfig_set_in_gitmodules_file_gently(key, add_data->branch);\n+\t\tfree(key);\n+\t}\n+\n+\tadd_gitmodules.git_cmd = 1;\n+\tstrvec_pushl(&add_gitmodules.args,\n+\t\t     \"add\", \"--force\", \"--\", \".gitmodules\", NULL);\n+\n+\tif (run_command(&add_gitmodules))\n+\t\tdie(_(\"Failed to register submodule '%s'\"), add_data->sm_path);\n+\n+\t/*\n+\t * NEEDSWORK: In a multi-working-tree world this needs to be\n+\t * set in the per-worktree config.\n+\t */\n+\tpathspec_key_exists = !git_config_get_string(\"submodule.active\",\n+\t\t\t\t\t\t     &submod_pathspec);\n+\tif (pathspec_key_exists && !submod_pathspec) {\n+\t\twarning(_(\"The submodule.active configuration exists, but the \"\n+\t\t\t  \"pathspec was unset. If the submodule is not already \"\n+\t\t\t  \"active, the value of submodule.%s.active will be \"\n+\t\t\t  \"be set to 'true'.\"), add_data->sm_name);\n+\t\tactivate = 1;\n+\t}\n+\n+\t/*\n+\t * If submodule.active does not exist, or if the pathspec was unset,\n+\t * we will activate this module unconditionally.\n+\t *\n+\t * Otherwise, we ask is_submodule_active(), which iterates\n+\t * through all the values of 'submodule.active' to determine\n+\t * if this module is already active.\n+\t */\n+\tif (!pathspec_key_exists || activate ||\n+\t    !is_submodule_active(the_repository, add_data->sm_path)) {\n+\t\tkey = xstrfmt(\"submodule.%s.active\", add_data->sm_name);\n+\t\tgit_config_set_gently(key, \"true\");\n+\t\tfree(key);\n+\t}\n+}\n+\n+static int add_config(int argc, const char **argv, const char *prefix)\n+{\n+\tint force = 0;\n+\tstruct add_data add_data = ADD_DATA_INIT;\n+\n+\tstruct option options[] = {\n+\t\tOPT_STRING('b', \"branch\", &add_data.branch,\n+\t\t\t   N_(\"branch\"),\n+\t\t\t   N_(\"branch of repository to store in \"\n+\t\t\t      \"the submodule configuration\")),\n+\t\tOPT_STRING(0, \"url\", &add_data.repo,\n+\t\t\t   N_(\"string\"),\n+\t\t\t   N_(\"url to clone submodule from\")),\n+\t\tOPT_STRING(0, \"resolved-url\", &add_data.realrepo,\n+\t\t\t   N_(\"string\"),\n+\t\t\t   N_(\"url to clone the submodule from, after it has \"\n+\t\t\t      \"been dereferenced relative to parent's url, \"\n+\t\t\t      \"in the case where <url> is a relative url\")),\n+\t\tOPT_STRING(0, \"path\", &add_data.sm_path,\n+\t\t\t   N_(\"path\"),\n+\t\t\t   N_(\"where the new submodule will be cloned to\")),\n+\t\tOPT_STRING(0, \"name\", &add_data.sm_name,\n+\t\t\t   N_(\"string\"),\n+\t\t\t   N_(\"name of the new submodule\")),\n+\t\tOPT_BOOL('f', \"force\", &force,\n+\t\t\t N_(\"allow adding an otherwise ignored submodule path\")),\n+\t\tOPT_END()\n+\t};\n+\n+\tconst char *const usage[] = {\n+\t\tN_(\"git submodule--helper add-config \"\n+\t\t   \"[--force|-f] [--branch|-b <branch>] \"\n+\t\t   \"--url <url> --resolved-url <resolved-url> \"\n+\t\t   \"--path <path> --name <name>\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, usage, 0);\n+\n+\tadd_data.force = !!force;\n+\tconfigure_added_submodule(&add_data);\n+\n+\treturn 0;\n+}\n+\n #define SUPPORT_SUPER_PREFIX (1<<0)\n \n struct cmd_struct {\n@@ -2956,6 +3071,7 @@ static struct cmd_struct commands[] = {\n \t{\"name\", module_name, 0},\n \t{\"clone\", module_clone, 0},\n \t{\"add-clone\", add_clone, 0},\n+\t{\"add-config\", add_config, 0},\n \t{\"update-module-mode\", module_update_module_mode, 0},\n \t{\"update-clone\", update_clone, 0},\n \t{\"ensure-core-worktree\", ensure_core_worktree, 0},\ndiff --git a/git-submodule.sh b/git-submodule.sh\nindex f71e1e5495..9826378fa6 100755\n--- a/git-submodule.sh\n+++ b/git-submodule.sh\n@@ -242,33 +242,7 @@ cmd_add()\n \tfi\n \n \tgit 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\n-\tgit config submodule.\"$sm_name\".url \"$realrepo\"\n-\n-\tgit add --no-warn-embedded-repo $force \"$sm_path\" ||\n-\tdie \"$(eval_gettext \"Failed to add submodule '\\$sm_path'\")\"\n-\n-\tgit submodule--helper config submodule.\"$sm_name\".path \"$sm_path\" &&\n-\tgit submodule--helper config submodule.\"$sm_name\".url \"$repo\" &&\n-\tif test -n \"$branch\"\n-\tthen\n-\t\tgit submodule--helper config submodule.\"$sm_name\".branch \"$branch\"\n-\tfi &&\n-\tgit add --force .gitmodules ||\n-\tdie \"$(eval_gettext \"Failed to register submodule '\\$sm_path'\")\"\n-\n-\t# NEEDSWORK: In a multi-working-tree world, this needs to be\n-\t# set in the per-worktree config.\n-\tif git config --get submodule.active >/dev/null\n-\tthen\n-\t\t# If the submodule being adding isn't already covered by the\n-\t\t# current configured pathspec, set the submodule's active flag\n-\t\tif ! git submodule--helper is-active \"$sm_path\"\n-\t\tthen\n-\t\t\tgit config submodule.\"$sm_name\".active \"true\"\n-\t\tfi\n-\telse\n-\t\tgit config submodule.\"$sm_name\".active \"true\"\n-\tfi\n+\tgit submodule--helper add-config ${force:+--force} ${branch:+--branch \"$branch\"} --url \"$repo\" --resolved-url \"$realrepo\" --path \"$sm_path\" --name \"$sm_name\"\n }\n \n #\n-- \n2.31.1\n\n"},{"id":"426766","messageId":"YL9jTFAoEBP+mDA2@danh.dev","threadId":"55844","inReplyTo":"20210608095655.47324-2-raykar.ath@gmail.com","subject":"Re: [GSoC] [PATCH v2 1/2] submodule--helper: introduce add-clone subcommand","fromName":"Đoàn Trần Công Danh","fromEmail":"congdanhqx@gmail.com","sentAt":"2021-06-08T12:32:12Z","receivedAt":"2021-06-08T12:33:16Z","isPatch":true,"sender":{"key":"congdanhqx@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42673067?v=4"},"body":"On 2021-06-08 15:26:54+0530, Atharva Raykar <raykar.ath@gmail.com> wrote:\n> Let's add a new \"add-clone\" subcommand to `git submodule--helper` with\n> the goal of converting part of the shell code in git-submodule.sh\n> related to `git submodule add` into C code. This new subcommand clones\n> the repository that is to be added, and checks out to the appropriate\n> branch.\n> \n> This is meant to be a faithful conversion that leaves the behaviour of\n> 'submodule add' unchanged. The only minor change is that if a submodule name has\n> been supplied with a name that clashes with a local submodule, the message shown\n> to the user (\"A git directory for 'foo' is found locally...\") is prepended with\n> \"error\" for clarity.\n> \n> This is part of a series of changes that will result in all of 'submodule add'\n> being converted to C.\n> \n> Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>\n> Mentored-by: Christian Couder <christian.couder@gmail.com>\n> Mentored-by: Shourya Shukla <shouryashukla.oo@gmail.com>\n> Based-on-patch-by: Shourya Shukla <shouryashukla.oo@gmail.com>\n> Based-on-patch-by: Prathamesh Chavan <pc44800@gmail.com>\n> ---\n>  builtin/submodule--helper.c | 199 ++++++++++++++++++++++++++++++++++++\n>  git-submodule.sh            |  38 +------\n>  2 files changed, 200 insertions(+), 37 deletions(-)\n> \n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> index d55f6262e9..c9cb535312 100644\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -2745,6 +2745,204 @@ static int module_set_branch(int argc, const char **argv, const char *prefix)\n>  \treturn !!ret;\n>  }\n>  \n> +struct add_data {\n> +\tconst char *prefix;\n> +\tconst char *branch;\n> +\tconst char *reference_path;\n> +\tconst char *sm_path;\n> +\tconst char *sm_name;\n> +\tconst char *repo;\n> +\tconst char *realrepo;\n> +\tint depth;\n> +\tunsigned int force: 1;\n> +\tunsigned int quiet: 1;\n> +\tunsigned int progress: 1;\n> +\tunsigned int dissociate: 1;\n> +};\n> +#define ADD_DATA_INIT { .depth = -1 }\n> +\n> +static char *parse_token(char **begin, const char *end, int *tok_len)\n> +{\n> +\tchar *tok_start, *pos = *begin;\n> +\twhile (pos != end && (*pos != ' ' && *pos != '\\t' && *pos != '\\n'))\n> +\t\tpos++;\n> +\ttok_start = *begin;\n> +\t*tok_len = pos - *begin;\n> +\t*begin = pos + 1;\n> +\treturn tok_start;\n> +}\n> +\n> +static char *get_next_line(char *const begin, const char *const end)\n> +{\n> +\tchar *pos = begin;\n> +\twhile (pos != end && *pos++ != '\\n');\n> +\treturn pos;\n> +}\n\nOn my first glance, this function looks like a reinvention of strchr(3).\nExcept that, this function also has a second parameter for \"end\".\nMaybe it has a specical use-case?\n\n> +\n> +static void show_fetch_remotes(FILE *output, const char *sm_name, const char *git_dir_path)\n> +{\n> +\tstruct child_process cp_remote = CHILD_PROCESS_INIT;\n> +\tstruct strbuf sb_remote_out = STRBUF_INIT;\n> +\n> +\tcp_remote.git_cmd = 1;\n> +\tstrvec_pushf(&cp_remote.env_array,\n> +\t\t     \"GIT_DIR=%s\", git_dir_path);\n> +\tstrvec_push(&cp_remote.env_array, \"GIT_WORK_TREE=.\");\n> +\tstrvec_pushl(&cp_remote.args, \"remote\", \"-v\", NULL);\n> +\tif (!capture_command(&cp_remote, &sb_remote_out, 0)) {\n> +\t\tchar *line;\n> +\t\tchar *begin = sb_remote_out.buf;\n> +\t\tchar *end = sb_remote_out.buf + sb_remote_out.len;\n> +\t\twhile (begin != end && (line = get_next_line(begin, end))) {\n\nAnd this is the only use-case.  Because you also want to check if you\nreached the last token or not.  I guess you really meant to write:\n\n\twhile ((line = strchr(begin, '\\n')) != NULL) {\n\nAnyway, I would name the \"line\" variable as \"nextline\"\n\n> +\t\t\tint namelen = 0, urllen = 0, taillen = 0;\n> +\t\t\tchar *name = parse_token(&begin, line, &namelen);\n> +\t\t\tchar *url = parse_token(&begin, line, &urllen);\n> +\t\t\tchar *tail = parse_token(&begin, line, &taillen);\n> +\t\t\tif (!memcmp(tail, \"(fetch)\", 7))\n> +\t\t\t\tfprintf(output, \"  %.*s\\t%.*s\\n\",\n> +\t\t\t\t\tnamelen, name, urllen, url);\n\nI think this whole block is better replaced with strip_suffix_mem and\nfprintf.\n\nOverral I would replace the block inside capture_command with:\n\n-----8<-----\n\tchar *nextline;\n\tchar *line = sb_remote_out.buf;\n\twhile ((nextline = strchr(line, '\\n')) != NULL) {\n\t\tsize_t len = nextline - line;\n\t\tif (strip_suffix_mem(line, &len, \"(fetch)\"))\n\t\t\tfprintf(output, \"  %.*s\\n\", (int)len, line);\n\t\tline = nextline + 1;\n\t}\n---->8-----\n\nAnd get rid of parse_token and get_next_line functions.\n\n\n\n> +\t\t}\n> +\t}\n> +\n> +\tstrbuf_release(&sb_remote_out);\n> +}\n> +\n> +static int add_submodule(const struct add_data *add_data)\n> +{\n> +\tchar *submod_gitdir_path;\n> +\t/* perhaps the path already exists and is already a git repo, else clone it */\n> +\tif (is_directory(add_data->sm_path)) {\n> +\t\tsubmod_gitdir_path = xstrfmt(\"%s/.git\", add_data->sm_path);\n> +\t\tif (is_directory(submod_gitdir_path) || file_exists(submod_gitdir_path))\n> +\t\t\tprintf(_(\"Adding existing path at '%s' to index\\n\"),\n> +\t\t\t       add_data->sm_path);\n> +\t\telse\n> +\t\t\tdie(_(\"'%s' already exists and is not a valid git repo\"),\n> +\t\t\t    add_data->sm_path);\n> +\t\tfree(submod_gitdir_path);\n> +\t} else {\n> +\t\tstruct strvec clone_args = STRVEC_INIT;\n> +\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n> +\t\tsubmod_gitdir_path = xstrfmt(\".git/modules/%s\", add_data->sm_name);\n> +\n> +\t\tif (is_directory(submod_gitdir_path)) {\n> +\t\t\tif (!add_data->force) {\n> +\t\t\t\terror(_(\"A git directory for '%s' is found \"\n> +\t\t\t\t\t\"locally with remote(s):\"), add_data->sm_name);\n\nWe don't capitalise first character of error message.\nIOW, downcase \"A git\".\n\nWell, it's bug-for-bug with shell implementation, so it doesn't matter much, anyway.\n\n> +\t\t\t\tshow_fetch_remotes(stderr, add_data->sm_name,\n> +\t\t\t\t\t\t   submod_gitdir_path);\n> +\t\t\t\tfprintf(stderr,\n> +\t\t\t\t\t_(\"If you want to reuse this local git \"\n> +\t\t\t\t\t  \"directory instead of cloning again from\\n\"\n> +\t\t\t\t\t  \"  %s\\n\"\n> +\t\t\t\t\t  \"use the '--force' option. If the local git \"\n> +\t\t\t\t\t  \"directory is not the correct repo\\n\"\n> +\t\t\t\t\t  \"or if you are unsure what this means, choose \"\n> +\t\t\t\t\t  \"another name with the '--name' option.\\n\"),\n> +\t\t\t\t\tadd_data->realrepo);\n\nIs there any reason we can't use \"error\" here?\n\n> +\t\t\t\tfree(submod_gitdir_path);\n> +\t\t\t\treturn 1;\n> +\t\t\t} else {\n> +\t\t\t\tprintf(_(\"Reactivating local git directory for \"\n> +\t\t\t\t\t \"submodule '%s'\\n\"), add_data->sm_name);\n> +\t\t\t}\n> +\t\t}\n> +\t\tfree(submod_gitdir_path);\n> +\n> +\t\tstrvec_pushl(&clone_args, \"clone\", \"--path\", add_data->sm_path, \"--name\",\n> +\t\t\t     add_data->sm_name, \"--url\", add_data->realrepo, NULL);\n> +\t\tif (add_data->quiet)\n> +\t\t\tstrvec_push(&clone_args, \"--quiet\");\n> +\t\tif (add_data->progress)\n> +\t\t\tstrvec_push(&clone_args, \"--progress\");\n> +\t\tif (add_data->prefix)\n> +\t\t\tstrvec_pushl(&clone_args, \"--prefix\", add_data->prefix, NULL);\n> +\t\tif (add_data->reference_path)\n> +\t\t\tstrvec_pushl(&clone_args, \"--reference\",\n> +\t\t\t\t     add_data->reference_path, NULL);\n> +\t\tif (add_data->dissociate)\n> +\t\t\tstrvec_push(&clone_args, \"--dissociate\");\n> +\t\tif (add_data->depth >= 0)\n> +\t\t\tstrvec_pushf(&clone_args, \"--depth=%d\", add_data->depth);\n> +\n> +\t\tif (module_clone(clone_args.nr, clone_args.v, add_data->prefix)) {\n> +\t\t\tstrvec_clear(&clone_args);\n> +\t\t\treturn -1;\n> +\t\t}\n> +\t\tstrvec_clear(&clone_args);\n> +\n> +\t\tprepare_submodule_repo_env(&cp.env_array);\n> +\t\tcp.git_cmd = 1;\n> +\t\tcp.dir = add_data->sm_path;\n> +\t\tstrvec_pushl(&cp.args, \"checkout\", \"-f\", \"-q\", NULL);\n> +\n> +\t\tif (add_data->branch) {\n> +\t\t\tstrvec_pushl(&cp.args, \"-B\", add_data->branch, NULL);\n> +\t\t\tstrvec_pushf(&cp.args, \"origin/%s\", add_data->branch);\n> +\t\t}\n> +\n> +\t\tif (run_command(&cp))\n> +\t\t\tdie(_(\"unable to checkout submodule '%s'\"), add_data->sm_path);\n> +\t}\n> +\treturn 0;\n> +}\n> +\n> +static int add_clone(int argc, const char **argv, const char *prefix)\n> +{\n> +\tint force = 0, quiet = 0, dissociate = 0, progress = 0;\n> +\tstruct add_data add_data = ADD_DATA_INIT;\n> +\n> +\tstruct option options[] = {\n> +\t\tOPT_STRING('b', \"branch\", &add_data.branch,\n> +\t\t\t   N_(\"branch\"),\n> +\t\t\t   N_(\"branch of repository to checkout on cloning\")),\n> +\t\tOPT_STRING(0, \"prefix\", &add_data.prefix,\n> +\t\t\t   N_(\"path\"),\n> +\t\t\t   N_(\"alternative anchor for relative paths\")),\n> +\t\tOPT_STRING(0, \"path\", &add_data.sm_path,\n> +\t\t\t   N_(\"path\"),\n> +\t\t\t   N_(\"where the new submodule will be cloned to\")),\n> +\t\tOPT_STRING(0, \"name\", &add_data.sm_name,\n> +\t\t\t   N_(\"string\"),\n> +\t\t\t   N_(\"name of the new submodule\")),\n> +\t\tOPT_STRING(0, \"url\", &add_data.realrepo,\n> +\t\t\t   N_(\"string\"),\n> +\t\t\t   N_(\"url where to clone the submodule from\")),\n> +\t\tOPT_STRING(0, \"reference\", &add_data.reference_path,\n> +\t\t\t   N_(\"repo\"),\n> +\t\t\t   N_(\"reference repository\")),\n> +\t\tOPT_BOOL(0, \"dissociate\", &dissociate,\n> +\t\t\t N_(\"use --reference only while cloning\")),\n> +\t\tOPT_INTEGER(0, \"depth\", &add_data.depth,\n> +\t\t\t    N_(\"depth for shallow clones\")),\n> +\t\tOPT_BOOL(0, \"progress\", &progress,\n> +\t\t\t N_(\"force cloning progress\")),\n> +\t\tOPT_BOOL('f', \"force\", &force,\n> +\t\t\t N_(\"allow adding an otherwise ignored submodule path\")),\n\nWe have OPT__FORCE, too.\n\n> +\t\tOPT__QUIET(&quiet, \"Suppress output for cloning a submodule\"),\n\nAnd, please downcase \"Suppress\".\n\n> +\t\tOPT_END()\n> +\t};\n> +\n> +\tconst char *const usage[] = {\n> +\t\tN_(\"git submodule--helper add-clone [--prefix=<path>] [--quiet] [--force] \"\n> +\t\t   \"[--reference <repository>] [--depth <depth>] [-b|--branch <branch>]\"\n> +\t\t   \"[--progress] [--dissociate] --url <url> --path <path> --name <name>\"),\n\nI think it's too crowded here, I guess it's better to write:\n\n\tN_(\"git submodule--helper add-clone [<options>...] --url <url> --path <path> --name <name>\"),\n\n> +\t\tNULL\n> +\t};\n> +\n> +\targc = parse_options(argc, argv, prefix, options, usage, 0);\n\nFrom above usage, I think url, path, name is required, should we have a check for them, here?\n> +\n> +\tadd_data.progress = !!progress;\n> +\tadd_data.dissociate = !!dissociate;\n> +\tadd_data.force = !!force;\n> +\tadd_data.quiet = !!quiet;\n> +\n> +\tif (add_submodule(&add_data))\n> +\t\treturn 1;\n> +\n> +\treturn 0;\n> +}\n> +\n>  #define SUPPORT_SUPER_PREFIX (1<<0)\n>  \n>  struct cmd_struct {\n> @@ -2757,6 +2955,7 @@ static struct cmd_struct commands[] = {\n>  \t{\"list\", module_list, 0},\n>  \t{\"name\", module_name, 0},\n>  \t{\"clone\", module_clone, 0},\n> +\t{\"add-clone\", add_clone, 0},\n>  \t{\"update-module-mode\", module_update_module_mode, 0},\n>  \t{\"update-clone\", update_clone, 0},\n>  \t{\"ensure-core-worktree\", ensure_core_worktree, 0},\n> diff --git a/git-submodule.sh b/git-submodule.sh\n> index 4678378424..f71e1e5495 100755\n> --- a/git-submodule.sh\n> +++ b/git-submodule.sh\n> @@ -241,43 +241,7 @@ cmd_add()\n>  \t\tdie \"$(eval_gettext \"'$sm_name' is not a valid submodule name\")\"\n>  \tfi\n>  \n> -\t# perhaps the path exists and is already a git repo, else clone it\n> -\tif test -e \"$sm_path\"\n> -\tthen\n> -\t\tif test -d \"$sm_path\"/.git || test -f \"$sm_path\"/.git\n> -\t\tthen\n> -\t\t\teval_gettextln \"Adding existing repo at '\\$sm_path' to the index\"\n> -\t\telse\n> -\t\t\tdie \"$(eval_gettext \"'\\$sm_path' already exists and is not a valid git repo\")\"\n> -\t\tfi\n> -\n> -\telse\n> -\t\tif test -d \".git/modules/$sm_name\"\n> -\t\tthen\n> -\t\t\tif test -z \"$force\"\n> -\t\t\tthen\n> -\t\t\t\teval_gettextln >&2 \"A git directory for '\\$sm_name' is found locally with remote(s):\"\n> -\t\t\t\tGIT_DIR=\".git/modules/$sm_name\" GIT_WORK_TREE=. git remote -v | grep '(fetch)' | sed -e s,^,\"  \", -e s,' (fetch)',, >&2\n> -\t\t\t\tdie \"$(eval_gettextln \"\\\n> -If you want to reuse this local git directory instead of cloning again from\n> -  \\$realrepo\n> -use the '--force' option. If the local git directory is not the correct repo\n> -or you are unsure what this means choose another name with the '--name' option.\")\"\n> -\t\t\telse\n> -\t\t\t\teval_gettextln \"Reactivating local git directory for submodule '\\$sm_name'.\"\n> -\t\t\tfi\n> -\t\tfi\n> -\t\tgit 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\n> -\t\t(\n> -\t\t\tsanitize_submodule_env\n> -\t\t\tcd \"$sm_path\" &&\n> -\t\t\t# ash fails to wordsplit ${branch:+-b \"$branch\"...}\n> -\t\t\tcase \"$branch\" in\n> -\t\t\t'') git checkout -f -q ;;\n> -\t\t\t?*) git checkout -f -q -B \"$branch\" \"origin/$branch\" ;;\n> -\t\t\tesac\n> -\t\t) || die \"$(eval_gettext \"Unable to checkout submodule '\\$sm_path'\")\"\n> -\tfi\n> +\tgit 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\n>  \tgit config submodule.\"$sm_name\".url \"$realrepo\"\n>  \n>  \tgit add --no-warn-embedded-repo $force \"$sm_path\" ||\n> -- \n> 2.31.1\n> \n\n-- \nDanh\n"},{"id":"426841","messageId":"xmqqh7i7ll6h.fsf@gitster.g","threadId":"55844","inReplyTo":"20210608095655.47324-2-raykar.ath@gmail.com","subject":"Re: [GSoC] [PATCH v2 1/2] submodule--helper: introduce add-clone subcommand","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-06-09T04:24:38Z","receivedAt":"2021-06-09T04:24:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Just a bit of random comments, leaving the full review to mentors.\n\n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> index d55f6262e9..c9cb535312 100644\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -2745,6 +2745,204 @@ static int module_set_branch(int argc, const char **argv, const char *prefix)\n>  \treturn !!ret;\n>  }\n>  \n> +struct add_data {\n> +\tconst char *prefix;\n> +\tconst char *branch;\n> +\tconst char *reference_path;\n> +\tconst char *sm_path;\n> +\tconst char *sm_name;\n> +\tconst char *repo;\n> +\tconst char *realrepo;\n> +\tint depth;\n> +\tunsigned int force: 1;\n> +\tunsigned int quiet: 1;\n> +\tunsigned int progress: 1;\n> +\tunsigned int dissociate: 1;\n> +};\n> +#define ADD_DATA_INIT { .depth = -1 }\n> +\n> +static char *parse_token(char **begin, const char *end, int *tok_len)\n> +{\n> +\tchar *tok_start, *pos = *begin;\n\nMake it a habit to have a blank line between the initial block\nof declarations and the first statement.\n\n> +\twhile (pos != end && (*pos != ' ' && *pos != '\\t' && *pos != '\\n'))\n> +\t\tpos++;\n> +\ttok_start = *begin;\n> +\t*tok_len = pos - *begin;\n> +\t*begin = pos + 1;\n> +\treturn tok_start;\n> +}\n> +static char *get_next_line(char *const begin, const char *const end)\n> +{\n> +\tchar *pos = begin;\n> +\twhile (pos != end && *pos++ != '\\n');\n\nWrite an empty loop on two lines, like this:\n\n\twhile (... condition ...)\n\t\t; /* keep scanning */\n\nIf there is a NUL byte between begin and end, this keeps going and\nthe resulting string will contain one.  Is that a problem?\n\n> +\treturn pos;\n> +}\n\nIn general, this project is mature enough that we should question\nourselves if there is already a suitable line parser we can reuse\nwhen tempted to write another one.\n\n> +static void show_fetch_remotes(FILE *output, const char *sm_name, const char *git_dir_path)\n> +{\n> +\tstruct child_process cp_remote = CHILD_PROCESS_INIT;\n> +\tstruct strbuf sb_remote_out = STRBUF_INIT;\n> +\n> +\tcp_remote.git_cmd = 1;\n> +\tstrvec_pushf(&cp_remote.env_array,\n> +\t\t     \"GIT_DIR=%s\", git_dir_path);\n> +\tstrvec_push(&cp_remote.env_array, \"GIT_WORK_TREE=.\");\n> +\tstrvec_pushl(&cp_remote.args, \"remote\", \"-v\", NULL);\n> +\tif (!capture_command(&cp_remote, &sb_remote_out, 0)) {\n> +\t\tchar *line;\n> +\t\tchar *begin = sb_remote_out.buf;\n> +\t\tchar *end = sb_remote_out.buf + sb_remote_out.len;\n> +\t\twhile (begin != end && (line = get_next_line(begin, end))) {\n\nOK, so this tries to parse output from \"git remote -v\", so NUL will\nnot be an issue at all.  We will get a string that is NUL terminated\nand has zero or more lines, terminated with LFs.\n\nIf that is the case, I think it is far easier to read without\na custom get-next-line wrapper, e.g.\n\n\tfor (this_line = begin;\n\t     *this_line;\n\t     this_line = next_line) {\n\t\tnext_line = strchrnul(this_line, '\\n');\n\t\t... process bytes between this_line..next_line ...\n\t}                \n\n> +\t\t\tint namelen = 0, urllen = 0, taillen = 0;\n> +\t\t\tchar *name = parse_token(&begin, line, &namelen);\n\nSimilarly, consider if strcspn() is useful in implementing\nparse_token().  See how existing code uses the standard system\nfunction with\n\n\t$ git grep strcspn \\*.c\n\n> +\t\t\tchar *url = parse_token(&begin, line, &urllen);\n> +\t\t\tchar *tail = parse_token(&begin, line, &taillen);\n> +\t\t\tif (!memcmp(tail, \"(fetch)\", 7))\n\nAt this point do we know there are enough number of bytes after\ntail[0] to allow us to do this comparison safely?  Otherwise,\n\n\t\t\tif (starts_with(tail, \"(fetch)\")\n\nmay be preferrable.\n"},{"id":"426873","messageId":"25A3EBAB-AD9A-4FF4-8E6E-63E8ACFF2739@gmail.com","threadId":"55844","inReplyTo":"xmqqh7i7ll6h.fsf@gitster.g","subject":"Re: [GSoC] [PATCH v2 1/2] submodule--helper: introduce add-clone subcommand","fromName":"Atharva Raykar","fromEmail":"raykar.ath@gmail.com","sentAt":"2021-06-09T10:31:42Z","receivedAt":"2021-06-09T10:32:08Z","isPatch":true,"sender":{"key":"raykar.ath@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24277692?v=4"},"body":"On 09-Jun-2021, at 09:54, Junio C Hamano <gitster@pobox.com> wrote:\n> \n> Just a bit of random comments, leaving the full review to mentors.\n> \n>> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n>> index d55f6262e9..c9cb535312 100644\n>> --- a/builtin/submodule--helper.c\n>> +++ b/builtin/submodule--helper.c\n>> @@ -2745,6 +2745,204 @@ static int module_set_branch(int argc, const char **argv, const char *prefix)\n>> \treturn !!ret;\n>> }\n>> \n>> +struct add_data {\n>> +\tconst char *prefix;\n>> +\tconst char *branch;\n>> +\tconst char *reference_path;\n>> +\tconst char *sm_path;\n>> +\tconst char *sm_name;\n>> +\tconst char *repo;\n>> +\tconst char *realrepo;\n>> +\tint depth;\n>> +\tunsigned int force: 1;\n>> +\tunsigned int quiet: 1;\n>> +\tunsigned int progress: 1;\n>> +\tunsigned int dissociate: 1;\n>> +};\n>> +#define ADD_DATA_INIT { .depth = -1 }\n>> +\n>> +static char *parse_token(char **begin, const char *end, int *tok_len)\n>> +{\n>> +\tchar *tok_start, *pos = *begin;\n> \n> Make it a habit to have a blank line between the initial block\n> of declarations and the first statement.\n> \n>> +\twhile (pos != end && (*pos != ' ' && *pos != '\\t' && *pos != '\\n'))\n>> +\t\tpos++;\n>> +\ttok_start = *begin;\n>> +\t*tok_len = pos - *begin;\n>> +\t*begin = pos + 1;\n>> +\treturn tok_start;\n>> +}\n>> +static char *get_next_line(char *const begin, const char *const end)\n>> +{\n>> +\tchar *pos = begin;\n>> +\twhile (pos != end && *pos++ != '\\n');\n> \n> Write an empty loop on two lines, like this:\n> \n> \twhile (... condition ...)\n> \t\t; /* keep scanning */\n\nOK.\n\n> If there is a NUL byte between begin and end, this keeps going and\n> the resulting string will contain one.  Is that a problem?\n> \n>> +\treturn pos;\n>> +}\n> \n> In general, this project is mature enough that we should question\n> ourselves if there is already a suitable line parser we can reuse\n> when tempted to write another one.\n\nI will keep this in mind.\n\n>> +static void show_fetch_remotes(FILE *output, const char *sm_name, const char *git_dir_path)\n>> +{\n>> +\tstruct child_process cp_remote = CHILD_PROCESS_INIT;\n>> +\tstruct strbuf sb_remote_out = STRBUF_INIT;\n>> +\n>> +\tcp_remote.git_cmd = 1;\n>> +\tstrvec_pushf(&cp_remote.env_array,\n>> +\t\t     \"GIT_DIR=%s\", git_dir_path);\n>> +\tstrvec_push(&cp_remote.env_array, \"GIT_WORK_TREE=.\");\n>> +\tstrvec_pushl(&cp_remote.args, \"remote\", \"-v\", NULL);\n>> +\tif (!capture_command(&cp_remote, &sb_remote_out, 0)) {\n>> +\t\tchar *line;\n>> +\t\tchar *begin = sb_remote_out.buf;\n>> +\t\tchar *end = sb_remote_out.buf + sb_remote_out.len;\n>> +\t\twhile (begin != end && (line = get_next_line(begin, end))) {\n> \n> OK, so this tries to parse output from \"git remote -v\", so NUL will\n> not be an issue at all.  We will get a string that is NUL terminated\n> and has zero or more lines, terminated with LFs.\n> \n> If that is the case, I think it is far easier to read without\n> a custom get-next-line wrapper, e.g.\n> \n> \tfor (this_line = begin;\n> \t     *this_line;\n> \t     this_line = next_line) {\n> \t\tnext_line = strchrnul(this_line, '\\n');\n> \t\t... process bytes between this_line..next_line ...\n> \t}                \n> \n>> +\t\t\tint namelen = 0, urllen = 0, taillen = 0;\n>> +\t\t\tchar *name = parse_token(&begin, line, &namelen);\n> \n> Similarly, consider if strcspn() is useful in implementing\n> parse_token().  See how existing code uses the standard system\n> function with\n> \n> \t$ git grep strcspn \\*.c\n> \n>> +\t\t\tchar *url = parse_token(&begin, line, &urllen);\n>> +\t\t\tchar *tail = parse_token(&begin, line, &taillen);\n>> +\t\t\tif (!memcmp(tail, \"(fetch)\", 7))\n> \n> At this point do we know there are enough number of bytes after\n> tail[0] to allow us to do this comparison safely?  Otherwise,\n> \n> \t\t\tif (starts_with(tail, \"(fetch)\")\n> \n> may be preferrable.\n\nThis solution is definitely an improvement over what I was doing.\n\nThat said, I like Danh's suggestion[1] more, because it eliminates the\nneed for parsing tokens entirely.\n\nThe fundamental thing that piece of code was meant to do is:\n\n\"If this line ends with '(fetch)', print the line, but without the '(fetch)'\"\n\nParsing tokens only to put them back together through fprintf() may\nnot be necessary for this usage, so using strchr() with\nstrip_suffix_mem() should do the trick.\n\n[1] https://lore.kernel.org/git/YL9jTFAoEBP+mDA2@danh.dev/\n\nThanks for the comments :^)"},{"id":"426874","messageId":"3B9B2CD5-2B99-4BF3-B348-5766F1CEB6D7@gmail.com","threadId":"55844","inReplyTo":"YL9jTFAoEBP+mDA2@danh.dev","subject":"Re: [GSoC] [PATCH v2 1/2] submodule--helper: introduce add-clone subcommand","fromName":"Atharva Raykar","fromEmail":"raykar.ath@gmail.com","sentAt":"2021-06-09T10:31:40Z","receivedAt":"2021-06-09T10:32:48Z","isPatch":true,"sender":{"key":"raykar.ath@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24277692?v=4"},"body":"On 08-Jun-2021, at 18:02, Đoàn Trần Công Danh <congdanhqx@gmail.com> wrote:\n> \n>> [...]\n>> +static char *get_next_line(char *const begin, const char *const end)\n>> +{\n>> +\tchar *pos = begin;\n>> +\twhile (pos != end && *pos++ != '\\n');\n>> +\treturn pos;\n>> +}\n> \n> On my first glance, this function looks like a reinvention of strchr(3).\n> Except that, this function also has a second parameter for \"end\".\n> Maybe it has a specical use-case?\n> \n>> +\n>> +static void show_fetch_remotes(FILE *output, const char *sm_name, const char *git_dir_path)\n>> +{\n>> +\tstruct child_process cp_remote = CHILD_PROCESS_INIT;\n>> +\tstruct strbuf sb_remote_out = STRBUF_INIT;\n>> +\n>> +\tcp_remote.git_cmd = 1;\n>> +\tstrvec_pushf(&cp_remote.env_array,\n>> +\t\t     \"GIT_DIR=%s\", git_dir_path);\n>> +\tstrvec_push(&cp_remote.env_array, \"GIT_WORK_TREE=.\");\n>> +\tstrvec_pushl(&cp_remote.args, \"remote\", \"-v\", NULL);\n>> +\tif (!capture_command(&cp_remote, &sb_remote_out, 0)) {\n>> +\t\tchar *line;\n>> +\t\tchar *begin = sb_remote_out.buf;\n>> +\t\tchar *end = sb_remote_out.buf + sb_remote_out.len;\n>> +\t\twhile (begin != end && (line = get_next_line(begin, end))) {\n> \n> And this is the only use-case.  Because you also want to check if you\n> reached the last token or not.  I guess you really meant to write:\n> \n> \twhile ((line = strchr(begin, '\\n')) != NULL) {\n> \n> Anyway, I would name the \"line\" variable as \"nextline\"\n> \n>> +\t\t\tint namelen = 0, urllen = 0, taillen = 0;\n>> +\t\t\tchar *name = parse_token(&begin, line, &namelen);\n>> +\t\t\tchar *url = parse_token(&begin, line, &urllen);\n>> +\t\t\tchar *tail = parse_token(&begin, line, &taillen);\n>> +\t\t\tif (!memcmp(tail, \"(fetch)\", 7))\n>> +\t\t\t\tfprintf(output, \"  %.*s\\t%.*s\\n\",\n>> +\t\t\t\t\tnamelen, name, urllen, url);\n> \n> I think this whole block is better replaced with strip_suffix_mem and\n> fprintf.\n> \n> Overral I would replace the block inside capture_command with:\n> \n> -----8<-----\n> \tchar *nextline;\n> \tchar *line = sb_remote_out.buf;\n> \twhile ((nextline = strchr(line, '\\n')) != NULL) {\n> \t\tsize_t len = nextline - line;\n> \t\tif (strip_suffix_mem(line, &len, \"(fetch)\"))\n> \t\t\tfprintf(output, \"  %.*s\\n\", (int)len, line);\n> \t\tline = nextline + 1;\n> \t}\n> ---->8-----\n> \n> And get rid of parse_token and get_next_line functions.\n\nThat looks much simpler. Thanks!\n\nI realised that all the token parsing I do is not really necessary.\nWhat I really want to do is \"If this line ends with '(fetch)',\nprint it, but without the '(fetch)'\", and I think your version\ncaptures that succinctly.\n\n>> +\t\t}\n>> +\t}\n>> +\n>> +\tstrbuf_release(&sb_remote_out);\n>> +}\n>> +\n>> +static int add_submodule(const struct add_data *add_data)\n>> +{\n>> +\tchar *submod_gitdir_path;\n>> +\t/* perhaps the path already exists and is already a git repo, else clone it */\n>> +\tif (is_directory(add_data->sm_path)) {\n>> +\t\tsubmod_gitdir_path = xstrfmt(\"%s/.git\", add_data->sm_path);\n>> +\t\tif (is_directory(submod_gitdir_path) || file_exists(submod_gitdir_path))\n>> +\t\t\tprintf(_(\"Adding existing path at '%s' to index\\n\"),\n>> +\t\t\t       add_data->sm_path);\n>> +\t\telse\n>> +\t\t\tdie(_(\"'%s' already exists and is not a valid git repo\"),\n>> +\t\t\t    add_data->sm_path);\n>> +\t\tfree(submod_gitdir_path);\n>> +\t} else {\n>> +\t\tstruct strvec clone_args = STRVEC_INIT;\n>> +\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n>> +\t\tsubmod_gitdir_path = xstrfmt(\".git/modules/%s\", add_data->sm_name);\n>> +\n>> +\t\tif (is_directory(submod_gitdir_path)) {\n>> +\t\t\tif (!add_data->force) {\n>> +\t\t\t\terror(_(\"A git directory for '%s' is found \"\n>> +\t\t\t\t\t\"locally with remote(s):\"), add_data->sm_name);\n> \n> We don't capitalise first character of error message.\n> IOW, downcase \"A git\".\n\nGot it.\n\n> Well, it's bug-for-bug with shell implementation, so it doesn't matter much, anyway.\n\nWhile it is meant to be a faithful implementation, I think this\nis a good opportunity to make minor style fixes.\n\n>> +\t\t\t\tshow_fetch_remotes(stderr, add_data->sm_name,\n>> +\t\t\t\t\t\t   submod_gitdir_path);\n>> +\t\t\t\tfprintf(stderr,\n>> +\t\t\t\t\t_(\"If you want to reuse this local git \"\n>> +\t\t\t\t\t  \"directory instead of cloning again from\\n\"\n>> +\t\t\t\t\t  \"  %s\\n\"\n>> +\t\t\t\t\t  \"use the '--force' option. If the local git \"\n>> +\t\t\t\t\t  \"directory is not the correct repo\\n\"\n>> +\t\t\t\t\t  \"or if you are unsure what this means, choose \"\n>> +\t\t\t\t\t  \"another name with the '--name' option.\\n\"),\n>> +\t\t\t\t\tadd_data->realrepo);\n> \n> Is there any reason we can't use \"error\" here?\n\nThe message in its entirety looks like this:\n\nerror: A git directory for 'test' is found locally with remote(s):\n  origin\tgit@github.com:tfidfwastaken/abc.git\nIf you want to reuse this local git directory instead of cloning again from\n  git@github.com:tfidfwastaken/test.git\nuse the '--force' option. If the local git directory is not the correct repo\nor if you are unsure what this means, choose another name with the '--name' option.\n\nSince the 'error:' is already there in the first line, having it\nprepended before 'If you want to reuse...' felt redundant to me.\n\nBesides, it's more of an informational message about what a user\ncan do next, rather than a message that signifies an error.\n\nIf there is a preferred convention or label for such messages,\nI can use that. The shell version did not have any such thing though.\n\n>> [...]\n>> +\tstruct option options[] = {\n>> +\t\tOPT_STRING('b', \"branch\", &add_data.branch,\n>> +\t\t\t   N_(\"branch\"),\n>> +\t\t\t   N_(\"branch of repository to checkout on cloning\")),\n>> +\t\tOPT_STRING(0, \"prefix\", &add_data.prefix,\n>> +\t\t\t   N_(\"path\"),\n>> +\t\t\t   N_(\"alternative anchor for relative paths\")),\n>> +\t\tOPT_STRING(0, \"path\", &add_data.sm_path,\n>> +\t\t\t   N_(\"path\"),\n>> +\t\t\t   N_(\"where the new submodule will be cloned to\")),\n>> +\t\tOPT_STRING(0, \"name\", &add_data.sm_name,\n>> +\t\t\t   N_(\"string\"),\n>> +\t\t\t   N_(\"name of the new submodule\")),\n>> +\t\tOPT_STRING(0, \"url\", &add_data.realrepo,\n>> +\t\t\t   N_(\"string\"),\n>> +\t\t\t   N_(\"url where to clone the submodule from\")),\n>> +\t\tOPT_STRING(0, \"reference\", &add_data.reference_path,\n>> +\t\t\t   N_(\"repo\"),\n>> +\t\t\t   N_(\"reference repository\")),\n>> +\t\tOPT_BOOL(0, \"dissociate\", &dissociate,\n>> +\t\t\t N_(\"use --reference only while cloning\")),\n>> +\t\tOPT_INTEGER(0, \"depth\", &add_data.depth,\n>> +\t\t\t    N_(\"depth for shallow clones\")),\n>> +\t\tOPT_BOOL(0, \"progress\", &progress,\n>> +\t\t\t N_(\"force cloning progress\")),\n>> +\t\tOPT_BOOL('f', \"force\", &force,\n>> +\t\t\t N_(\"allow adding an otherwise ignored submodule path\")),\n> \n> We have OPT__FORCE, too.\n\nWill switch over to that.\n\n>> +\t\tOPT__QUIET(&quiet, \"Suppress output for cloning a submodule\"),\n> \n> And, please downcase \"Suppress\".\n\nOK.\n\n>> +\t\tOPT_END()\n>> +\t};\n>> +\n>> +\tconst char *const usage[] = {\n>> +\t\tN_(\"git submodule--helper add-clone [--prefix=<path>] [--quiet] [--force] \"\n>> +\t\t   \"[--reference <repository>] [--depth <depth>] [-b|--branch <branch>]\"\n>> +\t\t   \"[--progress] [--dissociate] --url <url> --path <path> --name <name>\"),\n> \n> I think it's too crowded here, I guess it's better to write:\n> \n> \tN_(\"git submodule--helper add-clone [<options>...] --url <url> --path <path> --name <name>\"),\n\nOK. It shouldn't be an issue to shorten it, because this is not\nuser-facing, and is only ever used within 'cmd_add()'.\n\n>> +\t\tNULL\n>> +\t};\n>> +\n>> +\targc = parse_options(argc, argv, prefix, options, usage, 0);\n> \n> From above usage, I think url, path, name is required, should we have a check for them, here?\n\nWe could. The reason why I was not too rigorous about this is\nbecause I plan to eliminate the shell interface for this helper\neventually and call add-clone from within C, in the next few\npatches.\n\nBut this is a small ask, and I can just add a quick check just\nto be extra safe, so I'll do it.\n\n>> +\n>> +\tadd_data.progress = !!progress;\n>> +\tadd_data.dissociate = !!dissociate;\n>> +\tadd_data.force = !!force;\n>> +\tadd_data.quiet = !!quiet;\n>> +\n>> +\tif (add_submodule(&add_data))\n>> +\t\treturn 1;\n>> +\n>> +\treturn 0;\n>> +}\n>> +\n>> #define SUPPORT_SUPER_PREFIX (1<<0)\n>> \n>> struct cmd_struct {\n>> @@ -2757,6 +2955,7 @@ static struct cmd_struct commands[] = {\n>> \t{\"list\", module_list, 0},\n>> \t{\"name\", module_name, 0},\n>> \t{\"clone\", module_clone, 0},\n>> +\t{\"add-clone\", add_clone, 0},\n>> \t{\"update-module-mode\", module_update_module_mode, 0},\n>> \t{\"update-clone\", update_clone, 0},\n>> \t{\"ensure-core-worktree\", ensure_core_worktree, 0},\n>> diff --git a/git-submodule.sh b/git-submodule.sh\n>> index 4678378424..f71e1e5495 100755\n>> --- a/git-submodule.sh\n>> +++ b/git-submodule.sh\n>> @@ -241,43 +241,7 @@ cmd_add()\n>> \t\tdie \"$(eval_gettext \"'$sm_name' is not a valid submodule name\")\"\n>> \tfi\n>> \n>> -\t# perhaps the path exists and is already a git repo, else clone it\n>> -\tif test -e \"$sm_path\"\n>> -\tthen\n>> -\t\tif test -d \"$sm_path\"/.git || test -f \"$sm_path\"/.git\n>> -\t\tthen\n>> -\t\t\teval_gettextln \"Adding existing repo at '\\$sm_path' to the index\"\n>> -\t\telse\n>> -\t\t\tdie \"$(eval_gettext \"'\\$sm_path' already exists and is not a valid git repo\")\"\n>> -\t\tfi\n>> -\n>> -\telse\n>> -\t\tif test -d \".git/modules/$sm_name\"\n>> -\t\tthen\n>> -\t\t\tif test -z \"$force\"\n>> -\t\t\tthen\n>> -\t\t\t\teval_gettextln >&2 \"A git directory for '\\$sm_name' is found locally with remote(s):\"\n>> -\t\t\t\tGIT_DIR=\".git/modules/$sm_name\" GIT_WORK_TREE=. git remote -v | grep '(fetch)' | sed -e s,^,\"  \", -e s,' (fetch)',, >&2\n>> -\t\t\t\tdie \"$(eval_gettextln \"\\\n>> -If you want to reuse this local git directory instead of cloning again from\n>> -  \\$realrepo\n>> -use the '--force' option. If the local git directory is not the correct repo\n>> -or you are unsure what this means choose another name with the '--name' option.\")\"\n>> -\t\t\telse\n>> -\t\t\t\teval_gettextln \"Reactivating local git directory for submodule '\\$sm_name'.\"\n>> -\t\t\tfi\n>> -\t\tfi\n>> -\t\tgit 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\n>> -\t\t(\n>> -\t\t\tsanitize_submodule_env\n>> -\t\t\tcd \"$sm_path\" &&\n>> -\t\t\t# ash fails to wordsplit ${branch:+-b \"$branch\"...}\n>> -\t\t\tcase \"$branch\" in\n>> -\t\t\t'') git checkout -f -q ;;\n>> -\t\t\t?*) git checkout -f -q -B \"$branch\" \"origin/$branch\" ;;\n>> -\t\t\tesac\n>> -\t\t) || die \"$(eval_gettext \"Unable to checkout submodule '\\$sm_path'\")\"\n>> -\tfi\n>> +\tgit 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\n>> \tgit config submodule.\"$sm_name\".url \"$realrepo\"\n>> \n>> \tgit add --no-warn-embedded-repo $force \"$sm_path\" ||\n>> -- \n>> 2.31.1\n>> \n> \n> -- \n> Danh\n\nThanks for the comments!"},{"id":"426878","messageId":"YMC8upoajZm0QQ5G@danh.dev","threadId":"55844","inReplyTo":"3B9B2CD5-2B99-4BF3-B348-5766F1CEB6D7@gmail.com","subject":"Re: [GSoC] [PATCH v2 1/2] submodule--helper: introduce add-clone subcommand","fromName":"Đoàn Trần Công Danh","fromEmail":"congdanhqx@gmail.com","sentAt":"2021-06-09T13:06:02Z","receivedAt":"2021-06-09T13:06:21Z","isPatch":true,"sender":{"key":"congdanhqx@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42673067?v=4"},"body":"On 2021-06-09 16:01:40+0530, Atharva Raykar <raykar.ath@gmail.com> wrote:\n> On 08-Jun-2021, at 18:02, Đoàn Trần Công Danh <congdanhqx@gmail.com> wrote:\n> > \n> >> [...]\n> >> +static char *get_next_line(char *const begin, const char *const end)\n> >> +{\n> >> +\tchar *pos = begin;\n> >> +\twhile (pos != end && *pos++ != '\\n');\n> >> +\treturn pos;\n> >> +}\n> > \n> > On my first glance, this function looks like a reinvention of strchr(3).\n> > Except that, this function also has a second parameter for \"end\".\n> > Maybe it has a specical use-case?\n> > \n> >> +\n> >> +static void show_fetch_remotes(FILE *output, const char *sm_name, const char *git_dir_path)\n> >> +{\n> >> +\tstruct child_process cp_remote = CHILD_PROCESS_INIT;\n> >> +\tstruct strbuf sb_remote_out = STRBUF_INIT;\n> >> +\n> >> +\tcp_remote.git_cmd = 1;\n> >> +\tstrvec_pushf(&cp_remote.env_array,\n> >> +\t\t     \"GIT_DIR=%s\", git_dir_path);\n> >> +\tstrvec_push(&cp_remote.env_array, \"GIT_WORK_TREE=.\");\n> >> +\tstrvec_pushl(&cp_remote.args, \"remote\", \"-v\", NULL);\n> >> +\tif (!capture_command(&cp_remote, &sb_remote_out, 0)) {\n> >> +\t\tchar *line;\n> >> +\t\tchar *begin = sb_remote_out.buf;\n> >> +\t\tchar *end = sb_remote_out.buf + sb_remote_out.len;\n> >> +\t\twhile (begin != end && (line = get_next_line(begin, end))) {\n> > \n> > And this is the only use-case.  Because you also want to check if you\n> > reached the last token or not.  I guess you really meant to write:\n> > \n> > \twhile ((line = strchr(begin, '\\n')) != NULL) {\n> > \n> > Anyway, I would name the \"line\" variable as \"nextline\"\n> > \n> >> +\t\t\tint namelen = 0, urllen = 0, taillen = 0;\n> >> +\t\t\tchar *name = parse_token(&begin, line, &namelen);\n> >> +\t\t\tchar *url = parse_token(&begin, line, &urllen);\n> >> +\t\t\tchar *tail = parse_token(&begin, line, &taillen);\n> >> +\t\t\tif (!memcmp(tail, \"(fetch)\", 7))\n> >> +\t\t\t\tfprintf(output, \"  %.*s\\t%.*s\\n\",\n> >> +\t\t\t\t\tnamelen, name, urllen, url);\n> > \n> > I think this whole block is better replaced with strip_suffix_mem and\n> > fprintf.\n> > \n> > Overral I would replace the block inside capture_command with:\n> > \n> > -----8<-----\n> > \tchar *nextline;\n> > \tchar *line = sb_remote_out.buf;\n> > \twhile ((nextline = strchr(line, '\\n')) != NULL) {\n> > \t\tsize_t len = nextline - line;\n> > \t\tif (strip_suffix_mem(line, &len, \"(fetch)\"))\n> > \t\t\tfprintf(output, \"  %.*s\\n\", (int)len, line);\n\nFix-up for my suggestion:\n\nTo be bug-for-bug with shell implementation, it should be:\n\n\t\tif (strip_suffix_mem(line, &len, \" (fetch)\"))\n\n> > \t\tline = nextline + 1;\n> > \t}\n> > ---->8-----\n> > \n> > And get rid of parse_token and get_next_line functions.\n> \n> That looks much simpler. Thanks!\n> \n> I realised that all the token parsing I do is not really necessary.\n> What I really want to do is \"If this line ends with '(fetch)',\n> print it, but without the '(fetch)'\", and I think your version\n> captures that succinctly.\n> \n> >> +\t\t}\n> >> +\t}\n> >> +\n> >> +\tstrbuf_release(&sb_remote_out);\n> >> +}\n> >> +\n> >> +static int add_submodule(const struct add_data *add_data)\n> >> +{\n> >> +\tchar *submod_gitdir_path;\n> >> +\t/* perhaps the path already exists and is already a git repo, else clone it */\n> >> +\tif (is_directory(add_data->sm_path)) {\n> >> +\t\tsubmod_gitdir_path = xstrfmt(\"%s/.git\", add_data->sm_path);\n> >> +\t\tif (is_directory(submod_gitdir_path) || file_exists(submod_gitdir_path))\n> >> +\t\t\tprintf(_(\"Adding existing path at '%s' to index\\n\"),\n> >> +\t\t\t       add_data->sm_path);\n> >> +\t\telse\n> >> +\t\t\tdie(_(\"'%s' already exists and is not a valid git repo\"),\n> >> +\t\t\t    add_data->sm_path);\n> >> +\t\tfree(submod_gitdir_path);\n> >> +\t} else {\n> >> +\t\tstruct strvec clone_args = STRVEC_INIT;\n> >> +\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n> >> +\t\tsubmod_gitdir_path = xstrfmt(\".git/modules/%s\", add_data->sm_name);\n> >> +\n> >> +\t\tif (is_directory(submod_gitdir_path)) {\n> >> +\t\t\tif (!add_data->force) {\n> >> +\t\t\t\terror(_(\"A git directory for '%s' is found \"\n> >> +\t\t\t\t\t\"locally with remote(s):\"), add_data->sm_name);\n> > \n> > We don't capitalise first character of error message.\n> > IOW, downcase \"A git\".\n> \n> Got it.\n> \n> > Well, it's bug-for-bug with shell implementation, so it doesn't matter much, anyway.\n> \n> While it is meant to be a faithful implementation, I think this\n> is a good opportunity to make minor style fixes.\n> \n> >> +\t\t\t\tshow_fetch_remotes(stderr, add_data->sm_name,\n> >> +\t\t\t\t\t\t   submod_gitdir_path);\n> >> +\t\t\t\tfprintf(stderr,\n> >> +\t\t\t\t\t_(\"If you want to reuse this local git \"\n> >> +\t\t\t\t\t  \"directory instead of cloning again from\\n\"\n> >> +\t\t\t\t\t  \"  %s\\n\"\n> >> +\t\t\t\t\t  \"use the '--force' option. If the local git \"\n> >> +\t\t\t\t\t  \"directory is not the correct repo\\n\"\n> >> +\t\t\t\t\t  \"or if you are unsure what this means, choose \"\n> >> +\t\t\t\t\t  \"another name with the '--name' option.\\n\"),\n> >> +\t\t\t\t\tadd_data->realrepo);\n> > \n> > Is there any reason we can't use \"error\" here?\n> \n> The message in its entirety looks like this:\n> \n> error: A git directory for 'test' is found locally with remote(s):\n>   origin\tgit@github.com:tfidfwastaken/abc.git\n> If you want to reuse this local git directory instead of cloning again from\n>   git@github.com:tfidfwastaken/test.git\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> \n> Since the 'error:' is already there in the first line, having it\n> prepended before 'If you want to reuse...' felt redundant to me.\n> \n> Besides, it's more of an informational message about what a user\n> can do next, rather than a message that signifies an error.\n> \n> If there is a preferred convention or label for such messages,\n> I can use that. The shell version did not have any such thing though.\n> \n> >> [...]\n> >> +\tstruct option options[] = {\n> >> +\t\tOPT_STRING('b', \"branch\", &add_data.branch,\n> >> +\t\t\t   N_(\"branch\"),\n> >> +\t\t\t   N_(\"branch of repository to checkout on cloning\")),\n> >> +\t\tOPT_STRING(0, \"prefix\", &add_data.prefix,\n> >> +\t\t\t   N_(\"path\"),\n> >> +\t\t\t   N_(\"alternative anchor for relative paths\")),\n> >> +\t\tOPT_STRING(0, \"path\", &add_data.sm_path,\n> >> +\t\t\t   N_(\"path\"),\n> >> +\t\t\t   N_(\"where the new submodule will be cloned to\")),\n> >> +\t\tOPT_STRING(0, \"name\", &add_data.sm_name,\n> >> +\t\t\t   N_(\"string\"),\n> >> +\t\t\t   N_(\"name of the new submodule\")),\n> >> +\t\tOPT_STRING(0, \"url\", &add_data.realrepo,\n> >> +\t\t\t   N_(\"string\"),\n> >> +\t\t\t   N_(\"url where to clone the submodule from\")),\n> >> +\t\tOPT_STRING(0, \"reference\", &add_data.reference_path,\n> >> +\t\t\t   N_(\"repo\"),\n> >> +\t\t\t   N_(\"reference repository\")),\n> >> +\t\tOPT_BOOL(0, \"dissociate\", &dissociate,\n> >> +\t\t\t N_(\"use --reference only while cloning\")),\n> >> +\t\tOPT_INTEGER(0, \"depth\", &add_data.depth,\n> >> +\t\t\t    N_(\"depth for shallow clones\")),\n> >> +\t\tOPT_BOOL(0, \"progress\", &progress,\n> >> +\t\t\t N_(\"force cloning progress\")),\n> >> +\t\tOPT_BOOL('f', \"force\", &force,\n> >> +\t\t\t N_(\"allow adding an otherwise ignored submodule path\")),\n> > \n> > We have OPT__FORCE, too.\n> \n> Will switch over to that.\n> \n> >> +\t\tOPT__QUIET(&quiet, \"Suppress output for cloning a submodule\"),\n> > \n> > And, please downcase \"Suppress\".\n> \n> OK.\n> \n> >> +\t\tOPT_END()\n> >> +\t};\n> >> +\n> >> +\tconst char *const usage[] = {\n> >> +\t\tN_(\"git submodule--helper add-clone [--prefix=<path>] [--quiet] [--force] \"\n> >> +\t\t   \"[--reference <repository>] [--depth <depth>] [-b|--branch <branch>]\"\n> >> +\t\t   \"[--progress] [--dissociate] --url <url> --path <path> --name <name>\"),\n> > \n> > I think it's too crowded here, I guess it's better to write:\n> > \n> > \tN_(\"git submodule--helper add-clone [<options>...] --url <url> --path <path> --name <name>\"),\n> \n> OK. It shouldn't be an issue to shorten it, because this is not\n> user-facing, and is only ever used within 'cmd_add()'.\n> \n> >> +\t\tNULL\n> >> +\t};\n> >> +\n> >> +\targc = parse_options(argc, argv, prefix, options, usage, 0);\n> > \n> > From above usage, I think url, path, name is required, should we have a check for them, here?\n> \n> We could. The reason why I was not too rigorous about this is\n> because I plan to eliminate the shell interface for this helper\n> eventually and call add-clone from within C, in the next few\n> patches.\n> \n> But this is a small ask, and I can just add a quick check just\n> to be extra safe, so I'll do it.\n> \n> >> +\n> >> +\tadd_data.progress = !!progress;\n> >> +\tadd_data.dissociate = !!dissociate;\n> >> +\tadd_data.force = !!force;\n> >> +\tadd_data.quiet = !!quiet;\n> >> +\n> >> +\tif (add_submodule(&add_data))\n> >> +\t\treturn 1;\n> >> +\n> >> +\treturn 0;\n> >> +}\n> >> +\n> >> #define SUPPORT_SUPER_PREFIX (1<<0)\n> >> \n> >> struct cmd_struct {\n> >> @@ -2757,6 +2955,7 @@ static struct cmd_struct commands[] = {\n> >> \t{\"list\", module_list, 0},\n> >> \t{\"name\", module_name, 0},\n> >> \t{\"clone\", module_clone, 0},\n> >> +\t{\"add-clone\", add_clone, 0},\n> >> \t{\"update-module-mode\", module_update_module_mode, 0},\n> >> \t{\"update-clone\", update_clone, 0},\n> >> \t{\"ensure-core-worktree\", ensure_core_worktree, 0},\n> >> diff --git a/git-submodule.sh b/git-submodule.sh\n> >> index 4678378424..f71e1e5495 100755\n> >> --- a/git-submodule.sh\n> >> +++ b/git-submodule.sh\n> >> @@ -241,43 +241,7 @@ cmd_add()\n> >> \t\tdie \"$(eval_gettext \"'$sm_name' is not a valid submodule name\")\"\n> >> \tfi\n> >> \n> >> -\t# perhaps the path exists and is already a git repo, else clone it\n> >> -\tif test -e \"$sm_path\"\n> >> -\tthen\n> >> -\t\tif test -d \"$sm_path\"/.git || test -f \"$sm_path\"/.git\n> >> -\t\tthen\n> >> -\t\t\teval_gettextln \"Adding existing repo at '\\$sm_path' to the index\"\n> >> -\t\telse\n> >> -\t\t\tdie \"$(eval_gettext \"'\\$sm_path' already exists and is not a valid git repo\")\"\n> >> -\t\tfi\n> >> -\n> >> -\telse\n> >> -\t\tif test -d \".git/modules/$sm_name\"\n> >> -\t\tthen\n> >> -\t\t\tif test -z \"$force\"\n> >> -\t\t\tthen\n> >> -\t\t\t\teval_gettextln >&2 \"A git directory for '\\$sm_name' is found locally with remote(s):\"\n> >> -\t\t\t\tGIT_DIR=\".git/modules/$sm_name\" GIT_WORK_TREE=. git remote -v | grep '(fetch)' | sed -e s,^,\"  \", -e s,' (fetch)',, >&2\n> >> -\t\t\t\tdie \"$(eval_gettextln \"\\\n> >> -If you want to reuse this local git directory instead of cloning again from\n> >> -  \\$realrepo\n> >> -use the '--force' option. If the local git directory is not the correct repo\n> >> -or you are unsure what this means choose another name with the '--name' option.\")\"\n> >> -\t\t\telse\n> >> -\t\t\t\teval_gettextln \"Reactivating local git directory for submodule '\\$sm_name'.\"\n> >> -\t\t\tfi\n> >> -\t\tfi\n> >> -\t\tgit 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\n> >> -\t\t(\n> >> -\t\t\tsanitize_submodule_env\n> >> -\t\t\tcd \"$sm_path\" &&\n> >> -\t\t\t# ash fails to wordsplit ${branch:+-b \"$branch\"...}\n> >> -\t\t\tcase \"$branch\" in\n> >> -\t\t\t'') git checkout -f -q ;;\n> >> -\t\t\t?*) git checkout -f -q -B \"$branch\" \"origin/$branch\" ;;\n> >> -\t\t\tesac\n> >> -\t\t) || die \"$(eval_gettext \"Unable to checkout submodule '\\$sm_path'\")\"\n> >> -\tfi\n> >> +\tgit 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\n> >> \tgit config submodule.\"$sm_name\".url \"$realrepo\"\n> >> \n> >> \tgit add --no-warn-embedded-repo $force \"$sm_path\" ||\n> >> -- \n> >> 2.31.1\n> >> \n> > \n> > -- \n> > Danh\n> \n> Thanks for the comments!\n\n-- \nDanh\n"},{"id":"426879","messageId":"05339721-1CD6-4866-8A5E-368C9B379679@gmail.com","threadId":"55844","inReplyTo":"YMC8upoajZm0QQ5G@danh.dev","subject":"Re: [GSoC] [PATCH v2 1/2] submodule--helper: introduce add-clone subcommand","fromName":"Atharva Raykar","fromEmail":"raykar.ath@gmail.com","sentAt":"2021-06-09T13:10:48Z","receivedAt":"2021-06-09T13:11:54Z","isPatch":true,"sender":{"key":"raykar.ath@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24277692?v=4"},"body":"On 09-Jun-2021, at 18:36, Đoàn Trần Công Danh <congdanhqx@gmail.com> wrote:\n>>> \n>>> Overral I would replace the block inside capture_command with:\n>>> \n>>> -----8<-----\n>>> \tchar *nextline;\n>>> \tchar *line = sb_remote_out.buf;\n>>> \twhile ((nextline = strchr(line, '\\n')) != NULL) {\n>>> \t\tsize_t len = nextline - line;\n>>> \t\tif (strip_suffix_mem(line, &len, \"(fetch)\"))\n>>> \t\t\tfprintf(output, \"  %.*s\\n\", (int)len, line);\n> \n> Fix-up for my suggestion:\n> \n> To be bug-for-bug with shell implementation, it should be:\n> \n> \t\tif (strip_suffix_mem(line, &len, \" (fetch)\"))\n\nThat is very subtle, and I would have definitely missed it.\nThanks."},{"id":"426957","messageId":"20210610083916.96243-1-raykar.ath@gmail.com","threadId":"55844","inReplyTo":"20210608095655.47324-1-raykar.ath@gmail.com","subject":"[GSoC] [PATCH v3 0/2] submodule--helper: introduce subcommands for sh to C conversion","fromName":"Atharva Raykar","fromEmail":"raykar.ath@gmail.com","sentAt":"2021-06-10T08:39:14Z","receivedAt":"2021-06-10T08:41:46Z","isPatch":true,"sender":{"key":"raykar.ath@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24277692?v=4"},"body":"Notable changes since v2:\n - In show_fetch_remotes(), remove the get_next_line() and parse_token()\n   in favour of a simpler solution that uses strchr() and\n   strip_suffix_mem()\n - Use OPT__FORCE() instead of OPT_BOOL for '--force' flags\n - Add checks for number of arguments in the helper subcommands\n - Simplify usage string for add-clone and make error messages start in\n   lowercase.\n\nAtharva Raykar (2):\n  submodule--helper: introduce add-clone subcommand\n  submodule--helper: introduce add-config subcommand\n\n builtin/submodule--helper.c | 299 ++++++++++++++++++++++++++++++++++++\n git-submodule.sh            |  66 +-------\n 2 files changed, 301 insertions(+), 64 deletions(-)\n\n-- \n2.31.1\n\n"},{"id":"426958","messageId":"20210610083916.96243-2-raykar.ath@gmail.com","threadId":"55844","inReplyTo":"20210610083916.96243-1-raykar.ath@gmail.com","subject":"[PATCH v3 1/2] submodule--helper: introduce add-clone subcommand","fromName":"Atharva Raykar","fromEmail":"raykar.ath@gmail.com","sentAt":"2021-06-10T08:39:15Z","receivedAt":"2021-06-10T08:42:08Z","isPatch":true,"sender":{"key":"raykar.ath@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24277692?v=4"},"body":"Let's add a new \"add-clone\" subcommand to `git submodule--helper` with\nthe goal of converting part of the shell code in git-submodule.sh\nrelated to `git submodule add` into C code. This new subcommand clones\nthe repository that is to be added, and checks out to the appropriate\nbranch.\n\nThis is meant to be a faithful conversion that leaves the behaviour of\n'submodule add' unchanged. The only minor change is that if a submodule name has\nbeen supplied with a name that clashes with a local submodule, the message shown\nto the user (\"A git directory for 'foo' is found locally...\") is prepended with\n\"error\" for clarity.\n\nThis is part of a series of changes that will result in all of 'submodule add'\nbeing converted to C.\n\nSigned-off-by: Atharva Raykar <raykar.ath@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Shourya Shukla <shouryashukla.oo@gmail.com>\nBased-on-patch-by: Shourya Shukla <shouryashukla.oo@gmail.com>\nBased-on-patch-by: Prathamesh Chavan <pc44800@gmail.com>\nHelped-by: Đoàn Trần Công Danh <congdanhqx@gmail.com>\n---\n builtin/submodule--helper.c | 180 ++++++++++++++++++++++++++++++++++++\n git-submodule.sh            |  38 +-------\n 2 files changed, 181 insertions(+), 37 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex d55f6262e9..21b2e9fb14 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2745,6 +2745,185 @@ static int module_set_branch(int argc, const char **argv, const char *prefix)\n \treturn !!ret;\n }\n \n+struct add_data {\n+\tconst char *prefix;\n+\tconst char *branch;\n+\tconst char *reference_path;\n+\tconst char *sm_path;\n+\tconst char *sm_name;\n+\tconst char *repo;\n+\tconst char *realrepo;\n+\tint depth;\n+\tunsigned int force: 1;\n+\tunsigned int quiet: 1;\n+\tunsigned int progress: 1;\n+\tunsigned int dissociate: 1;\n+};\n+#define ADD_DATA_INIT { .depth = -1 }\n+\n+static void show_fetch_remotes(FILE *output, const char *sm_name, const char *git_dir_path)\n+{\n+\tstruct child_process cp_remote = CHILD_PROCESS_INIT;\n+\tstruct strbuf sb_remote_out = STRBUF_INIT;\n+\n+\tcp_remote.git_cmd = 1;\n+\tstrvec_pushf(&cp_remote.env_array,\n+\t\t     \"GIT_DIR=%s\", git_dir_path);\n+\tstrvec_push(&cp_remote.env_array, \"GIT_WORK_TREE=.\");\n+\tstrvec_pushl(&cp_remote.args, \"remote\", \"-v\", NULL);\n+\tif (!capture_command(&cp_remote, &sb_remote_out, 0)) {\n+\t\tchar *next_line;\n+\t\tchar *line = sb_remote_out.buf;\n+\t\twhile ((next_line = strchr(line, '\\n')) != NULL) {\n+\t\t\tsize_t len = next_line - line;\n+\t\t\tif (strip_suffix_mem(line, &len, \" (fetch)\"))\n+\t\t\t\tfprintf(output, \"  %.*s\\n\", (int)len, line);\n+\t\t\tline = next_line + 1;\n+\t\t}\n+\t}\n+\n+\tstrbuf_release(&sb_remote_out);\n+}\n+\n+static int add_submodule(const struct add_data *add_data)\n+{\n+\tchar *submod_gitdir_path;\n+\n+\t/* perhaps the path already exists and is already a git repo, else clone it */\n+\tif (is_directory(add_data->sm_path)) {\n+\t\tsubmod_gitdir_path = xstrfmt(\"%s/.git\", add_data->sm_path);\n+\t\tif (is_directory(submod_gitdir_path) || file_exists(submod_gitdir_path))\n+\t\t\tprintf(_(\"Adding existing path at '%s' to index\\n\"),\n+\t\t\t       add_data->sm_path);\n+\t\telse\n+\t\t\tdie(_(\"'%s' already exists and is not a valid git repo\"),\n+\t\t\t    add_data->sm_path);\n+\t\tfree(submod_gitdir_path);\n+\t} else {\n+\t\tstruct strvec clone_args = STRVEC_INIT;\n+\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\t\tsubmod_gitdir_path = xstrfmt(\".git/modules/%s\", add_data->sm_name);\n+\n+\t\tif (is_directory(submod_gitdir_path)) {\n+\t\t\tif (!add_data->force) {\n+\t\t\t\terror(_(\"a git directory for '%s' is found \"\n+\t\t\t\t\t\"locally with remote(s):\"), add_data->sm_name);\n+\t\t\t\tshow_fetch_remotes(stderr, add_data->sm_name,\n+\t\t\t\t\t\t   submod_gitdir_path);\n+\t\t\t\tfprintf(stderr,\n+\t\t\t\t\t_(\"If you want to reuse this local git \"\n+\t\t\t\t\t  \"directory instead of cloning again from\\n\"\n+\t\t\t\t\t  \"  %s\\n\"\n+\t\t\t\t\t  \"use the '--force' option. If the local git \"\n+\t\t\t\t\t  \"directory is not the correct repo\\n\"\n+\t\t\t\t\t  \"or if you are unsure what this means, choose \"\n+\t\t\t\t\t  \"another name with the '--name' option.\\n\"),\n+\t\t\t\t\tadd_data->realrepo);\n+\t\t\t\tfree(submod_gitdir_path);\n+\t\t\t\treturn 1;\n+\t\t\t} else {\n+\t\t\t\tprintf(_(\"Reactivating local git directory for \"\n+\t\t\t\t\t \"submodule '%s'\\n\"), add_data->sm_name);\n+\t\t\t}\n+\t\t}\n+\t\tfree(submod_gitdir_path);\n+\n+\t\tstrvec_pushl(&clone_args, \"clone\", \"--path\", add_data->sm_path, \"--name\",\n+\t\t\t     add_data->sm_name, \"--url\", add_data->realrepo, NULL);\n+\t\tif (add_data->quiet)\n+\t\t\tstrvec_push(&clone_args, \"--quiet\");\n+\t\tif (add_data->progress)\n+\t\t\tstrvec_push(&clone_args, \"--progress\");\n+\t\tif (add_data->prefix)\n+\t\t\tstrvec_pushl(&clone_args, \"--prefix\", add_data->prefix, NULL);\n+\t\tif (add_data->reference_path)\n+\t\t\tstrvec_pushl(&clone_args, \"--reference\",\n+\t\t\t\t     add_data->reference_path, NULL);\n+\t\tif (add_data->dissociate)\n+\t\t\tstrvec_push(&clone_args, \"--dissociate\");\n+\t\tif (add_data->depth >= 0)\n+\t\t\tstrvec_pushf(&clone_args, \"--depth=%d\", add_data->depth);\n+\n+\t\tif (module_clone(clone_args.nr, clone_args.v, add_data->prefix)) {\n+\t\t\tstrvec_clear(&clone_args);\n+\t\t\treturn -1;\n+\t\t}\n+\t\tstrvec_clear(&clone_args);\n+\n+\t\tprepare_submodule_repo_env(&cp.env_array);\n+\t\tcp.git_cmd = 1;\n+\t\tcp.dir = add_data->sm_path;\n+\t\tstrvec_pushl(&cp.args, \"checkout\", \"-f\", \"-q\", NULL);\n+\n+\t\tif (add_data->branch) {\n+\t\t\tstrvec_pushl(&cp.args, \"-B\", add_data->branch, NULL);\n+\t\t\tstrvec_pushf(&cp.args, \"origin/%s\", add_data->branch);\n+\t\t}\n+\n+\t\tif (run_command(&cp))\n+\t\t\tdie(_(\"unable to checkout submodule '%s'\"), add_data->sm_path);\n+\t}\n+\treturn 0;\n+}\n+\n+static int add_clone(int argc, const char **argv, const char *prefix)\n+{\n+\tint force = 0, quiet = 0, dissociate = 0, progress = 0;\n+\tstruct add_data add_data = ADD_DATA_INIT;\n+\n+\tstruct option options[] = {\n+\t\tOPT_STRING('b', \"branch\", &add_data.branch,\n+\t\t\t   N_(\"branch\"),\n+\t\t\t   N_(\"branch of repository to checkout on cloning\")),\n+\t\tOPT_STRING(0, \"prefix\", &add_data.prefix,\n+\t\t\t   N_(\"path\"),\n+\t\t\t   N_(\"alternative anchor for relative paths\")),\n+\t\tOPT_STRING(0, \"path\", &add_data.sm_path,\n+\t\t\t   N_(\"path\"),\n+\t\t\t   N_(\"where the new submodule will be cloned to\")),\n+\t\tOPT_STRING(0, \"name\", &add_data.sm_name,\n+\t\t\t   N_(\"string\"),\n+\t\t\t   N_(\"name of the new submodule\")),\n+\t\tOPT_STRING(0, \"url\", &add_data.realrepo,\n+\t\t\t   N_(\"string\"),\n+\t\t\t   N_(\"url where to clone the submodule from\")),\n+\t\tOPT_STRING(0, \"reference\", &add_data.reference_path,\n+\t\t\t   N_(\"repo\"),\n+\t\t\t   N_(\"reference repository\")),\n+\t\tOPT_BOOL(0, \"dissociate\", &dissociate,\n+\t\t\t N_(\"use --reference only while cloning\")),\n+\t\tOPT_INTEGER(0, \"depth\", &add_data.depth,\n+\t\t\t    N_(\"depth for shallow clones\")),\n+\t\tOPT_BOOL(0, \"progress\", &progress,\n+\t\t\t N_(\"force cloning progress\")),\n+\t\tOPT__FORCE(&force, N_(\"allow adding an otherwise ignored submodule path\"),\n+\t\t\t   PARSE_OPT_NOCOMPLETE),\n+\t\tOPT__QUIET(&quiet, \"suppress output for cloning a submodule\"),\n+\t\tOPT_END()\n+\t};\n+\n+\tconst char *const usage[] = {\n+\t\tN_(\"git submodule--helper add-clone [<options>...] \"\n+\t\t   \"--url <url> --path <path> --name <name>\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, usage, 0);\n+\n+\tif (argc != 0)\n+\t\tusage_with_options(usage, options);\n+\n+\tadd_data.progress = !!progress;\n+\tadd_data.dissociate = !!dissociate;\n+\tadd_data.force = !!force;\n+\tadd_data.quiet = !!quiet;\n+\n+\tif (add_submodule(&add_data))\n+\t\treturn 1;\n+\n+\treturn 0;\n+}\n+\n #define SUPPORT_SUPER_PREFIX (1<<0)\n \n struct cmd_struct {\n@@ -2757,6 +2936,7 @@ static struct cmd_struct commands[] = {\n \t{\"list\", module_list, 0},\n \t{\"name\", module_name, 0},\n \t{\"clone\", module_clone, 0},\n+\t{\"add-clone\", add_clone, 0},\n \t{\"update-module-mode\", module_update_module_mode, 0},\n \t{\"update-clone\", update_clone, 0},\n \t{\"ensure-core-worktree\", ensure_core_worktree, 0},\ndiff --git a/git-submodule.sh b/git-submodule.sh\nindex 4678378424..f71e1e5495 100755\n--- a/git-submodule.sh\n+++ b/git-submodule.sh\n@@ -241,43 +241,7 @@ cmd_add()\n \t\tdie \"$(eval_gettext \"'$sm_name' is not a valid submodule name\")\"\n \tfi\n \n-\t# perhaps the path exists and is already a git repo, else clone it\n-\tif test -e \"$sm_path\"\n-\tthen\n-\t\tif test -d \"$sm_path\"/.git || test -f \"$sm_path\"/.git\n-\t\tthen\n-\t\t\teval_gettextln \"Adding existing repo at '\\$sm_path' to the index\"\n-\t\telse\n-\t\t\tdie \"$(eval_gettext \"'\\$sm_path' already exists and is not a valid git repo\")\"\n-\t\tfi\n-\n-\telse\n-\t\tif test -d \".git/modules/$sm_name\"\n-\t\tthen\n-\t\t\tif test -z \"$force\"\n-\t\t\tthen\n-\t\t\t\teval_gettextln >&2 \"A git directory for '\\$sm_name' is found locally with remote(s):\"\n-\t\t\t\tGIT_DIR=\".git/modules/$sm_name\" GIT_WORK_TREE=. git remote -v | grep '(fetch)' | sed -e s,^,\"  \", -e s,' (fetch)',, >&2\n-\t\t\t\tdie \"$(eval_gettextln \"\\\n-If you want to reuse this local git directory instead of cloning again from\n-  \\$realrepo\n-use the '--force' option. If the local git directory is not the correct repo\n-or you are unsure what this means choose another name with the '--name' option.\")\"\n-\t\t\telse\n-\t\t\t\teval_gettextln \"Reactivating local git directory for submodule '\\$sm_name'.\"\n-\t\t\tfi\n-\t\tfi\n-\t\tgit 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\n-\t\t(\n-\t\t\tsanitize_submodule_env\n-\t\t\tcd \"$sm_path\" &&\n-\t\t\t# ash fails to wordsplit ${branch:+-b \"$branch\"...}\n-\t\t\tcase \"$branch\" in\n-\t\t\t'') git checkout -f -q ;;\n-\t\t\t?*) git checkout -f -q -B \"$branch\" \"origin/$branch\" ;;\n-\t\t\tesac\n-\t\t) || die \"$(eval_gettext \"Unable to checkout submodule '\\$sm_path'\")\"\n-\tfi\n+\tgit 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\n \tgit config submodule.\"$sm_name\".url \"$realrepo\"\n \n \tgit add --no-warn-embedded-repo $force \"$sm_path\" ||\n-- \n2.31.1\n\n"},{"id":"426959","messageId":"20210610083916.96243-3-raykar.ath@gmail.com","threadId":"55844","inReplyTo":"20210610083916.96243-1-raykar.ath@gmail.com","subject":"[PATCH v3 2/2] submodule--helper: introduce add-config subcommand","fromName":"Atharva Raykar","fromEmail":"raykar.ath@gmail.com","sentAt":"2021-06-10T08:39:16Z","receivedAt":"2021-06-10T08:42:21Z","isPatch":true,"sender":{"key":"raykar.ath@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24277692?v=4"},"body":"Add a new \"add-config\" subcommand to `git submodule--helper` with the\ngoal of converting part of the shell code in git-submodule.sh related to\n`git submodule add` into C code. This new subcommand sets the\nconfiguration variables of a newly added submodule, by registering the\nurl in local git config, as well as the submodule name and path in the\n.gitmodules file. It also sets 'submodule.<name>.active' to \"true\" if\nthe submodule path has not already been covered by any pathspec\nspecified in 'submodule.active'.\n\nThis is meant to be a faithful conversion from shell to C, with only one\nminor change: A warning is emitted if no value is specified in\n'submodule.active', ie, the config looks like: \"[submodule] active\\n\",\nbecause it is an invalid configuration. It would be helpful to let the\nuser know that the pathspec is unset, and the value of\n'submodule.<name>.active' might be set to 'true' so that they can\nrectify their configuration and prevent future surprises (especially\ngiven that the latter variable has a higher priority than the former).\n\nThe structure of the conditional to check if we need to set the 'active'\ntoggle looks different from the shell version -- but behaves the same.\nThe change was made to decrease code duplication. A comment has been\nadded to explain that only one value of 'submodule.active' is obtained\nto check if we need to call is_submodule_active() at all.\n\nThis is part of a series of changes that will result in all of\n'submodule add' being converted to C.\n\nSigned-off-by: Atharva Raykar <raykar.ath@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Shourya Shukla <shouryashukla.oo@gmail.com>\nBased-on-patch-by: Shourya Shukla <shouryashukla.oo@gmail.com>\nBased-on-patch-by: Prathamesh Chavan <pc44800@gmail.com>\n---\n builtin/submodule--helper.c | 119 ++++++++++++++++++++++++++++++++++++\n git-submodule.sh            |  28 +--------\n 2 files changed, 120 insertions(+), 27 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 21b2e9fb14..a50276138b 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2924,6 +2924,124 @@ static int add_clone(int argc, const char **argv, const char *prefix)\n \treturn 0;\n }\n \n+static void configure_added_submodule(struct add_data *add_data)\n+{\n+\tchar *key, *submod_pathspec = NULL;\n+\tstruct child_process add_submod = CHILD_PROCESS_INIT;\n+\tstruct child_process add_gitmodules = CHILD_PROCESS_INIT;\n+\tint pathspec_key_exists, activate = 0;\n+\n+\tkey = xstrfmt(\"submodule.%s.url\", add_data->sm_name);\n+\tgit_config_set_gently(key, add_data->realrepo);\n+\tfree(key);\n+\n+\tadd_submod.git_cmd = 1;\n+\tstrvec_pushl(&add_submod.args, \"add\",\n+\t\t     \"--no-warn-embedded-repo\", NULL);\n+\tif (add_data->force)\n+\t\tstrvec_push(&add_submod.args, \"--force\");\n+\tstrvec_pushl(&add_submod.args, \"--\", add_data->sm_path, NULL);\n+\n+\tif (run_command(&add_submod))\n+\t\tdie(_(\"Failed to add submodule '%s'\"), add_data->sm_path);\n+\n+\tkey = xstrfmt(\"submodule.%s.path\", add_data->sm_name);\n+\tconfig_set_in_gitmodules_file_gently(key, add_data->sm_path);\n+\tfree(key);\n+\tkey = xstrfmt(\"submodule.%s.url\", add_data->sm_name);\n+\tconfig_set_in_gitmodules_file_gently(key, add_data->repo);\n+\tfree(key);\n+\tif (add_data->branch) {\n+\t\tkey = xstrfmt(\"submodule.%s.branch\", add_data->sm_path);\n+\t\tconfig_set_in_gitmodules_file_gently(key, add_data->branch);\n+\t\tfree(key);\n+\t}\n+\n+\tadd_gitmodules.git_cmd = 1;\n+\tstrvec_pushl(&add_gitmodules.args,\n+\t\t     \"add\", \"--force\", \"--\", \".gitmodules\", NULL);\n+\n+\tif (run_command(&add_gitmodules))\n+\t\tdie(_(\"Failed to register submodule '%s'\"), add_data->sm_path);\n+\n+\t/*\n+\t * NEEDSWORK: In a multi-working-tree world this needs to be\n+\t * set in the per-worktree config.\n+\t */\n+\tpathspec_key_exists = !git_config_get_string(\"submodule.active\",\n+\t\t\t\t\t\t     &submod_pathspec);\n+\tif (pathspec_key_exists && !submod_pathspec) {\n+\t\twarning(_(\"The submodule.active configuration exists, but the \"\n+\t\t\t  \"pathspec was unset. If the submodule is not already \"\n+\t\t\t  \"active, the value of submodule.%s.active will be \"\n+\t\t\t  \"be set to 'true'.\"), add_data->sm_name);\n+\t\tactivate = 1;\n+\t}\n+\n+\t/*\n+\t * If submodule.active does not exist, or if the pathspec was unset,\n+\t * we will activate this module unconditionally.\n+\t *\n+\t * Otherwise, we ask is_submodule_active(), which iterates\n+\t * through all the values of 'submodule.active' to determine\n+\t * if this module is already active.\n+\t */\n+\tif (!pathspec_key_exists || activate ||\n+\t    !is_submodule_active(the_repository, add_data->sm_path)) {\n+\t\tkey = xstrfmt(\"submodule.%s.active\", add_data->sm_name);\n+\t\tgit_config_set_gently(key, \"true\");\n+\t\tfree(key);\n+\t}\n+}\n+\n+static int add_config(int argc, const char **argv, const char *prefix)\n+{\n+\tint force = 0;\n+\tstruct add_data add_data = ADD_DATA_INIT;\n+\n+\tstruct option options[] = {\n+\t\tOPT_STRING('b', \"branch\", &add_data.branch,\n+\t\t\t   N_(\"branch\"),\n+\t\t\t   N_(\"branch of repository to store in \"\n+\t\t\t      \"the submodule configuration\")),\n+\t\tOPT_STRING(0, \"url\", &add_data.repo,\n+\t\t\t   N_(\"string\"),\n+\t\t\t   N_(\"url to clone submodule from\")),\n+\t\tOPT_STRING(0, \"resolved-url\", &add_data.realrepo,\n+\t\t\t   N_(\"string\"),\n+\t\t\t   N_(\"url to clone the submodule from, after it has \"\n+\t\t\t      \"been dereferenced relative to parent's url, \"\n+\t\t\t      \"in the case where <url> is a relative url\")),\n+\t\tOPT_STRING(0, \"path\", &add_data.sm_path,\n+\t\t\t   N_(\"path\"),\n+\t\t\t   N_(\"where the new submodule will be cloned to\")),\n+\t\tOPT_STRING(0, \"name\", &add_data.sm_name,\n+\t\t\t   N_(\"string\"),\n+\t\t\t   N_(\"name of the new submodule\")),\n+\t\tOPT__FORCE(&force, N_(\"allow adding an otherwise ignored submodule path\"),\n+\t\t\t   PARSE_OPT_NOCOMPLETE),\n+\t\tOPT_END()\n+\t};\n+\n+\tconst char *const usage[] = {\n+\t\tN_(\"git submodule--helper add-config \"\n+\t\t   \"[--force|-f] [--branch|-b <branch>] \"\n+\t\t   \"--url <url> --resolved-url <resolved-url> \"\n+\t\t   \"--path <path> --name <name>\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, usage, 0);\n+\n+\tif (argc != 0)\n+\t\tusage_with_options(usage, options);\n+\n+\tadd_data.force = !!force;\n+\tconfigure_added_submodule(&add_data);\n+\n+\treturn 0;\n+}\n+\n #define SUPPORT_SUPER_PREFIX (1<<0)\n \n struct cmd_struct {\n@@ -2937,6 +3055,7 @@ static struct cmd_struct commands[] = {\n \t{\"name\", module_name, 0},\n \t{\"clone\", module_clone, 0},\n \t{\"add-clone\", add_clone, 0},\n+\t{\"add-config\", add_config, 0},\n \t{\"update-module-mode\", module_update_module_mode, 0},\n \t{\"update-clone\", update_clone, 0},\n \t{\"ensure-core-worktree\", ensure_core_worktree, 0},\ndiff --git a/git-submodule.sh b/git-submodule.sh\nindex f71e1e5495..9826378fa6 100755\n--- a/git-submodule.sh\n+++ b/git-submodule.sh\n@@ -242,33 +242,7 @@ cmd_add()\n \tfi\n \n \tgit 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\n-\tgit config submodule.\"$sm_name\".url \"$realrepo\"\n-\n-\tgit add --no-warn-embedded-repo $force \"$sm_path\" ||\n-\tdie \"$(eval_gettext \"Failed to add submodule '\\$sm_path'\")\"\n-\n-\tgit submodule--helper config submodule.\"$sm_name\".path \"$sm_path\" &&\n-\tgit submodule--helper config submodule.\"$sm_name\".url \"$repo\" &&\n-\tif test -n \"$branch\"\n-\tthen\n-\t\tgit submodule--helper config submodule.\"$sm_name\".branch \"$branch\"\n-\tfi &&\n-\tgit add --force .gitmodules ||\n-\tdie \"$(eval_gettext \"Failed to register submodule '\\$sm_path'\")\"\n-\n-\t# NEEDSWORK: In a multi-working-tree world, this needs to be\n-\t# set in the per-worktree config.\n-\tif git config --get submodule.active >/dev/null\n-\tthen\n-\t\t# If the submodule being adding isn't already covered by the\n-\t\t# current configured pathspec, set the submodule's active flag\n-\t\tif ! git submodule--helper is-active \"$sm_path\"\n-\t\tthen\n-\t\t\tgit config submodule.\"$sm_name\".active \"true\"\n-\t\tfi\n-\telse\n-\t\tgit config submodule.\"$sm_name\".active \"true\"\n-\tfi\n+\tgit submodule--helper add-config ${force:+--force} ${branch:+--branch \"$branch\"} --url \"$repo\" --resolved-url \"$realrepo\" --path \"$sm_path\" --name \"$sm_name\"\n }\n \n #\n-- \n2.31.1\n\n"},{"id":"427082","messageId":"xmqqim2l3p92.fsf@gitster.g","threadId":"55844","inReplyTo":"20210610083916.96243-2-raykar.ath@gmail.com","subject":"Re: [PATCH v3 1/2] submodule--helper: introduce add-clone subcommand","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-06-11T06:10:49Z","receivedAt":"2021-06-11T06:10:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Atharva Raykar <raykar.ath@gmail.com> writes:\n\n> Let's add a new \"add-clone\" subcommand to `git submodule--helper` with\n> the goal of converting part of the shell code in git-submodule.sh\n> related to `git submodule add` into C code. This new subcommand clones\n> the repository that is to be added, and checks out to the appropriate\n> branch.\n>\n> This is meant to be a faithful conversion that leaves the behaviour of\n> 'submodule add' unchanged. The only minor change is that if a submodule name has\n> been supplied with a name that clashes with a local submodule, the message shown\n> to the user (\"A git directory for 'foo' is found locally...\") is prepended with\n> \"error\" for clarity.\n>\n> This is part of a series of changes that will result in all of 'submodule add'\n> being converted to C.\n>\n> Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>\n> Mentored-by: Christian Couder <christian.couder@gmail.com>\n> Mentored-by: Shourya Shukla <shouryashukla.oo@gmail.com>\n> Based-on-patch-by: Shourya Shukla <shouryashukla.oo@gmail.com>\n> Based-on-patch-by: Prathamesh Chavan <pc44800@gmail.com>\n> Helped-by: Đoàn Trần Công Danh <congdanhqx@gmail.com>\n> ---\n>  builtin/submodule--helper.c | 180 ++++++++++++++++++++++++++++++++++++\n>  git-submodule.sh            |  38 +-------\n>  2 files changed, 181 insertions(+), 37 deletions(-)\n>\n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> index d55f6262e9..21b2e9fb14 100644\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -2745,6 +2745,185 @@ static int module_set_branch(int argc, const char **argv, const char *prefix)\n>  \treturn !!ret;\n>  }\n>  \n> +struct add_data {\n> +\tconst char *prefix;\n> +\tconst char *branch;\n> +\tconst char *reference_path;\n> +\tconst char *sm_path;\n> +\tconst char *sm_name;\n> +\tconst char *repo;\n> +\tconst char *realrepo;\n> +\tint depth;\n> +\tunsigned int force: 1;\n> +\tunsigned int quiet: 1;\n> +\tunsigned int progress: 1;\n> +\tunsigned int dissociate: 1;\n> +};\n> +#define ADD_DATA_INIT { .depth = -1 }\n> +\n> +static void show_fetch_remotes(FILE *output, const char *sm_name, const char *git_dir_path)\n> +{\n> +\tstruct child_process cp_remote = CHILD_PROCESS_INIT;\n> +\tstruct strbuf sb_remote_out = STRBUF_INIT;\n> +\n> +\tcp_remote.git_cmd = 1;\n> +\tstrvec_pushf(&cp_remote.env_array,\n> +\t\t     \"GIT_DIR=%s\", git_dir_path);\n> +\tstrvec_push(&cp_remote.env_array, \"GIT_WORK_TREE=.\");\n> +\tstrvec_pushl(&cp_remote.args, \"remote\", \"-v\", NULL);\n> +\tif (!capture_command(&cp_remote, &sb_remote_out, 0)) {\n> +\t\tchar *next_line;\n> +\t\tchar *line = sb_remote_out.buf;\n> +\t\twhile ((next_line = strchr(line, '\\n')) != NULL) {\n> +\t\t\tsize_t len = next_line - line;\n> +\t\t\tif (strip_suffix_mem(line, &len, \" (fetch)\"))\n> +\t\t\t\tfprintf(output, \"  %.*s\\n\", (int)len, line);\n> +\t\t\tline = next_line + 1;\n\nGood.  That's a handy helper to use here.\n\n> +\t\t}\n> +\t}\n> +\n> +\tstrbuf_release(&sb_remote_out);\n> +}\n> +\n> +static int add_submodule(const struct add_data *add_data)\n> +{\n> +\tchar *submod_gitdir_path;\n> +\n> +\t/* perhaps the path already exists and is already a git repo, else clone it */\n> +\tif (is_directory(add_data->sm_path)) {\n> +\t\tsubmod_gitdir_path = xstrfmt(\"%s/.git\", add_data->sm_path);\n> +\t\tif (is_directory(submod_gitdir_path) || file_exists(submod_gitdir_path))\n\nThis is a faithful translation from the scripted Porcelain that did\nnot have access to our C internals that can do a lot better job at\ndeciding if a given \"$path\", which has \".git\" directly or file below\nit, is the top of a working tree that is governed by that \".git\"\nthing.\n\nI wonder if setup.c::is_nonbare_repository_dir() can be used here.\nYou really want to see add_data->sm_path is the top of the working\ntree of a submodule, so requiring only that \".git\" thing is either\na directory or a file, which was the cheap thing the scripted version\ncould do without expending too much engineering effort, is a shame\nwhen you are now writing in C and all the infrastructure to do much\nbetter job is available to you.\n\n> +\t\t\tprintf(_(\"Adding existing path at '%s' to index\\n\"),\n> +\t\t\t       add_data->sm_path);\n\nUnintended change to the message here; you lost \"the\".\n\n> +\t\telse\n> +\t\t\tdie(_(\"'%s' already exists and is not a valid git repo\"),\n> +\t\t\t    add_data->sm_path);\n> +\t\tfree(submod_gitdir_path);\n> +\t} else {\n> +\t\tstruct strvec clone_args = STRVEC_INIT;\n> +\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n> +\t\tsubmod_gitdir_path = xstrfmt(\".git/modules/%s\", add_data->sm_name);\n> +\n> +\t\tif (is_directory(submod_gitdir_path)) {\n> +\t\t\tif (!add_data->force) {\n> +\t\t\t\terror(_(\"a git directory for '%s' is found \"\n> +\t\t\t\t\t\"locally with remote(s):\"), add_data->sm_name);\n\nThe original is a mere eval_gettextln, so a faithful conversion\nwould be to emit this to the standard error stream, without letting\nerror() to add \"error: \" prefix and downcasing \"A git directory\".\n\n> +\t\t\t\tshow_fetch_remotes(stderr, add_data->sm_name,\n> +\t\t\t\t\t\t   submod_gitdir_path);\n> +\t\t\t\tfprintf(stderr,\n> +\t\t\t\t\t_(\"If you want to reuse this local git \"\n> +\t\t\t\t\t  \"directory instead of cloning again from\\n\"\n> +\t\t\t\t\t  \"  %s\\n\"\n> +\t\t\t\t\t  \"use the '--force' option. If the local git \"\n> +\t\t\t\t\t  \"directory is not the correct repo\\n\"\n> +\t\t\t\t\t  \"or if you are unsure what this means, choose \"\n> +\t\t\t\t\t  \"another name with the '--name' option.\\n\"),\n> +\t\t\t\t\tadd_data->realrepo);\n> +\t\t\t\tfree(submod_gitdir_path);\n> +\t\t\t\treturn 1;\n\nThe updated caller in the scripted Porcelain just silently exits\nupon an error, so the above message that used to be given to die()\nneeds to be emitted by us like this.  You may want to do die(),\ninstead of fprintf(stderr) followed by return 1, to be a more\nfaithful conversion.\n\n> +\t\t\t} else {\n> +\t\t\t\tprintf(_(\"Reactivating local git directory for \"\n> +\t\t\t\t\t \"submodule '%s'\\n\"), add_data->sm_name);\n> +\t\t\t}\n> +\t\t}\n> +\t\tfree(submod_gitdir_path);\n\nAnd at this point, the original scripted Porcelain used to call \"git\nsubmodule--helper clone\".\n\n> +\t\tstrvec_pushl(&clone_args, \"clone\", \"--path\", add_data->sm_path, \"--name\",\n> +\t\t\t     add_data->sm_name, \"--url\", add_data->realrepo, NULL);\n> +\t\tif (add_data->quiet)\n> +\t\t\tstrvec_push(&clone_args, \"--quiet\");\n> +\t\tif (add_data->progress)\n> +\t\t\tstrvec_push(&clone_args, \"--progress\");\n> +\t\tif (add_data->prefix)\n> +\t\t\tstrvec_pushl(&clone_args, \"--prefix\", add_data->prefix, NULL);\n> +\t\tif (add_data->reference_path)\n> +\t\t\tstrvec_pushl(&clone_args, \"--reference\",\n> +\t\t\t\t     add_data->reference_path, NULL);\n> +\t\tif (add_data->dissociate)\n> +\t\t\tstrvec_push(&clone_args, \"--dissociate\");\n> +\t\tif (add_data->depth >= 0)\n> +\t\t\tstrvec_pushf(&clone_args, \"--depth=%d\", add_data->depth);\n> +\n> +\t\tif (module_clone(clone_args.nr, clone_args.v, add_data->prefix)) {\n> +\t\t\tstrvec_clear(&clone_args);\n> +\t\t\treturn -1;\n> +\t\t}\n> +\t\tstrvec_clear(&clone_args);\n\nAll of the above does the equivalent of it, but having to \"unparse\"\nmembers of the add_data structure and stuff clone_args, only to be\nparsed by parse_options() call in module_clone(), is somewhat\nunsatisfactory.  I think this series would be helped greatly if\nthere were an additional step that splits module_clone() into two,\nfactoring out everything that happens after module_clone() calls\nparse_options() into a helper function.  module_clone() will call\nthat helper function, and then at this step, instead of the above\n\"unparse and then call module_clone()\", this place will call the\nsame helper function.\n\nIn any case, after doing \"git submodule--helper clone\", the original\nscripted version does \"git checkout\" in the newly cloned working\ntree.  I wonder if the \"module_clone\" is done with \"--no-checkout\"\noption---cloning and letting a commit checked out, and checking out\nanother commit immediately after it, feels somewhat wasteful.\n\n> +\t\tprepare_submodule_repo_env(&cp.env_array);\n> +\t\tcp.git_cmd = 1;\n> +\t\tcp.dir = add_data->sm_path;\n> +\t\tstrvec_pushl(&cp.args, \"checkout\", \"-f\", \"-q\", NULL);\n> +\n> +\t\tif (add_data->branch) {\n> +\t\t\tstrvec_pushl(&cp.args, \"-B\", add_data->branch, NULL);\n> +\t\t\tstrvec_pushf(&cp.args, \"origin/%s\", add_data->branch);\n> +\t\t}\n> +\n> +\t\tif (run_command(&cp))\n> +\t\t\tdie(_(\"unable to checkout submodule '%s'\"), add_data->sm_path);\n> +\t}\n> +\treturn 0;\n> +}\n\nOther than all the commented parts, this looks fairly faithful\nconversion from the part of the scripted Porcelain that has been\nlost by the same patch.\n\n> +static int add_clone(int argc, const char **argv, const char *prefix)\n> +{\n> +\tint force = 0, quiet = 0, dissociate = 0, progress = 0;\n> +\tstruct add_data add_data = ADD_DATA_INIT;\n> + ...\n> +\targc = parse_options(argc, argv, prefix, options, usage, 0);\n> +\n> +\tif (argc != 0)\n> +\t\tusage_with_options(usage, options);\n> +\n> +\tadd_data.progress = !!progress;\n> +\tadd_data.dissociate = !!dissociate;\n> +\tadd_data.force = !!force;\n> +\tadd_data.quiet = !!quiet;\n> +\n> +\tif (add_submodule(&add_data))\n> +\t\treturn 1;\n> +\n> +\treturn 0;\n> +}\n\nAs you wrote the add_clone() vs add_submodule() split of labour, the\nreason this one is much more nicely organized than module_clone()\nshould be obvious to you.  This part of the design of this patch is\nvery well done.\n\nThe \"additional step\" I hinted earlier is exactly that---by making\nthe guts of the feature add_submodule() to work on already parsed\ndata and a separate wrapper add_clone() that interfaces to the\nscripts to parse the command line arguments into a shape that is\nmore appropriate for the inner function, you can later call the\ninner function add_submodule() directly without going through the\nunparsing-then-parsing dance from other places.  If module_clone()\nwere split into two in a similar way, then the place to call it from\nadd_submodule() would become much cleaner.\n\nThanks.\n"},{"id":"427091","messageId":"EAB5ABE4-B032-41AE-B39D-44D0D0A40D52@gmail.com","threadId":"55844","inReplyTo":"xmqqim2l3p92.fsf@gitster.g","subject":"Re: [PATCH v3 1/2] submodule--helper: introduce add-clone subcommand","fromName":"Atharva Raykar","fromEmail":"raykar.ath@gmail.com","sentAt":"2021-06-11T07:32:57Z","receivedAt":"2021-06-11T07:33:17Z","isPatch":true,"sender":{"key":"raykar.ath@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24277692?v=4"},"body":"On 11-Jun-2021, at 11:40, Junio C Hamano <gitster@pobox.com> wrote:\n> \n>> [...]\n>> +static void show_fetch_remotes(FILE *output, const char *sm_name, const char *git_dir_path)\n>> +{\n>> +\tstruct child_process cp_remote = CHILD_PROCESS_INIT;\n>> +\tstruct strbuf sb_remote_out = STRBUF_INIT;\n>> +\n>> +\tcp_remote.git_cmd = 1;\n>> +\tstrvec_pushf(&cp_remote.env_array,\n>> +\t\t     \"GIT_DIR=%s\", git_dir_path);\n>> +\tstrvec_push(&cp_remote.env_array, \"GIT_WORK_TREE=.\");\n>> +\tstrvec_pushl(&cp_remote.args, \"remote\", \"-v\", NULL);\n>> +\tif (!capture_command(&cp_remote, &sb_remote_out, 0)) {\n>> +\t\tchar *next_line;\n>> +\t\tchar *line = sb_remote_out.buf;\n>> +\t\twhile ((next_line = strchr(line, '\\n')) != NULL) {\n>> +\t\t\tsize_t len = next_line - line;\n>> +\t\t\tif (strip_suffix_mem(line, &len, \" (fetch)\"))\n>> +\t\t\t\tfprintf(output, \"  %.*s\\n\", (int)len, line);\n>> +\t\t\tline = next_line + 1;\n> \n> Good.  That's a handy helper to use here.\n> \n>> +\t\t}\n>> +\t}\n>> +\n>> +\tstrbuf_release(&sb_remote_out);\n>> +}\n>> +\n>> +static int add_submodule(const struct add_data *add_data)\n>> +{\n>> +\tchar *submod_gitdir_path;\n>> +\n>> +\t/* perhaps the path already exists and is already a git repo, else clone it */\n>> +\tif (is_directory(add_data->sm_path)) {\n>> +\t\tsubmod_gitdir_path = xstrfmt(\"%s/.git\", add_data->sm_path);\n>> +\t\tif (is_directory(submod_gitdir_path) || file_exists(submod_gitdir_path))\n> \n> This is a faithful translation from the scripted Porcelain that did\n> not have access to our C internals that can do a lot better job at\n> deciding if a given \"$path\", which has \".git\" directly or file below\n> it, is the top of a working tree that is governed by that \".git\"\n> thing.\n> \n> I wonder if setup.c::is_nonbare_repository_dir() can be used here.\n> You really want to see add_data->sm_path is the top of the working\n> tree of a submodule, so requiring only that \".git\" thing is either\n> a directory or a file, which was the cheap thing the scripted version\n> could do without expending too much engineering effort, is a shame\n> when you are now writing in C and all the infrastructure to do much\n> better job is available to you.\n\nYes, that looks like the right function to use. My version just\npoorly reinvented that wheel. Will fix.\n\n>> +\t\t\tprintf(_(\"Adding existing path at '%s' to index\\n\"),\n>> +\t\t\t       add_data->sm_path);\n> \n> Unintended change to the message here; you lost \"the\".\n\nWill change.\n\n>> +\t\telse\n>> +\t\t\tdie(_(\"'%s' already exists and is not a valid git repo\"),\n>> +\t\t\t    add_data->sm_path);\n>> +\t\tfree(submod_gitdir_path);\n>> +\t} else {\n>> +\t\tstruct strvec clone_args = STRVEC_INIT;\n>> +\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n>> +\t\tsubmod_gitdir_path = xstrfmt(\".git/modules/%s\", add_data->sm_name);\n>> +\n>> +\t\tif (is_directory(submod_gitdir_path)) {\n>> +\t\t\tif (!add_data->force) {\n>> +\t\t\t\terror(_(\"a git directory for '%s' is found \"\n>> +\t\t\t\t\t\"locally with remote(s):\"), add_data->sm_name);\n> \n> The original is a mere eval_gettextln, so a faithful conversion\n> would be to emit this to the standard error stream, without letting\n> error() to add \"error: \" prefix and downcasing \"A git directory\".\n\nThis was an intentional change on my part, which was motivated by\nSivaraam's message on a similar patch[1]. I agreed with his point\nthat it is better to be explicit to the user that an error has\noccurred.\n\nI think the larger concern I have to balance here is what level of\n\"faithfulness\" should I be having with this conversion. My current\nthoughts on this are:\n\n - Functionality and behaviour should be as similar as possible.\n\n - If there is an *obvious* bug that can be fixed in translation,\n   then I should fix it.\n\n - If there are error messages that can be made more clear and\n   consistent with the rest of the Porcelain, then I'll make the\n   required change.\n\nIf you want me to turn up the faithfulness all the way up, so that\nthe error messages are identical word-for-word, I don't mind doing\nthat. I just personally felt that this conversion would be a good\nopportunity to make the error messages consistent with the rest of\nGit.\n\n(I'll concede that I'm not sure what \"consistent with the rest\nof Git\" would exactly mean, so I'll be happy if any reviewers will\nguide me on those kind of changes).\n\nIf I do go down the \"reproduce messages exactly\" path, it should\nalso follow that I not include this warning in [2/2], as it is\nnot in the original shell script:\n\n+\tif (pathspec_key_exists && !submod_pathspec) {\n+\t\twarning(_(\"The submodule.active configuration exists, but the \"\n+\t\t\t  \"pathspec was unset. If the submodule is not already \"\n+\t\t\t  \"active, the value of submodule.%s.active will be \"\n+\t\t\t  \"be set to 'true'.\"), add_data->sm_name);\n+\t\tactivate = 1;\n+\t}\n\nThis was something that you suggested including, back when Shourya was\nworking on this[2]. So I used that as the signal that lightly altering\nthe messages emitted would be acceptable. I'm willing to change my\nboundaries though.\n\n[1] https://lore.kernel.org/git/5899bf6e-f61f-8a19-196d-d38d611dc037@gmail.com/\n[2] https://lore.kernel.org/git/xmqqo8isxefz.fsf@gitster.c.googlers.com/\n\n>> +\t\t\t\tshow_fetch_remotes(stderr, add_data->sm_name,\n>> +\t\t\t\t\t\t   submod_gitdir_path);\n>> +\t\t\t\tfprintf(stderr,\n>> +\t\t\t\t\t_(\"If you want to reuse this local git \"\n>> +\t\t\t\t\t  \"directory instead of cloning again from\\n\"\n>> +\t\t\t\t\t  \"  %s\\n\"\n>> +\t\t\t\t\t  \"use the '--force' option. If the local git \"\n>> +\t\t\t\t\t  \"directory is not the correct repo\\n\"\n>> +\t\t\t\t\t  \"or if you are unsure what this means, choose \"\n>> +\t\t\t\t\t  \"another name with the '--name' option.\\n\"),\n>> +\t\t\t\t\tadd_data->realrepo);\n>> +\t\t\t\tfree(submod_gitdir_path);\n>> +\t\t\t\treturn 1;\n> \n> The updated caller in the scripted Porcelain just silently exits\n> upon an error, so the above message that used to be given to die()\n> needs to be emitted by us like this.  You may want to do die(),\n> instead of fprintf(stderr) followed by return 1, to be a more\n> faithful conversion.\n\nOkay. Will change.\n\n>> +\t\t\t} else {\n>> +\t\t\t\tprintf(_(\"Reactivating local git directory for \"\n>> +\t\t\t\t\t \"submodule '%s'\\n\"), add_data->sm_name);\n>> +\t\t\t}\n>> +\t\t}\n>> +\t\tfree(submod_gitdir_path);\n> \n> And at this point, the original scripted Porcelain used to call \"git\n> submodule--helper clone\".\n> \n>> +\t\tstrvec_pushl(&clone_args, \"clone\", \"--path\", add_data->sm_path, \"--name\",\n>> +\t\t\t     add_data->sm_name, \"--url\", add_data->realrepo, NULL);\n>> +\t\tif (add_data->quiet)\n>> +\t\t\tstrvec_push(&clone_args, \"--quiet\");\n>> +\t\tif (add_data->progress)\n>> +\t\t\tstrvec_push(&clone_args, \"--progress\");\n>> +\t\tif (add_data->prefix)\n>> +\t\t\tstrvec_pushl(&clone_args, \"--prefix\", add_data->prefix, NULL);\n>> +\t\tif (add_data->reference_path)\n>> +\t\t\tstrvec_pushl(&clone_args, \"--reference\",\n>> +\t\t\t\t     add_data->reference_path, NULL);\n>> +\t\tif (add_data->dissociate)\n>> +\t\t\tstrvec_push(&clone_args, \"--dissociate\");\n>> +\t\tif (add_data->depth >= 0)\n>> +\t\t\tstrvec_pushf(&clone_args, \"--depth=%d\", add_data->depth);\n>> +\n>> +\t\tif (module_clone(clone_args.nr, clone_args.v, add_data->prefix)) {\n>> +\t\t\tstrvec_clear(&clone_args);\n>> +\t\t\treturn -1;\n>> +\t\t}\n>> +\t\tstrvec_clear(&clone_args);\n> \n> All of the above does the equivalent of it, but having to \"unparse\"\n> members of the add_data structure and stuff clone_args, only to be\n> parsed by parse_options() call in module_clone(), is somewhat\n> unsatisfactory.  I think this series would be helped greatly if\n> there were an additional step that splits module_clone() into two,\n> factoring out everything that happens after module_clone() calls\n> parse_options() into a helper function.  module_clone() will call\n> that helper function, and then at this step, instead of the above\n> \"unparse and then call module_clone()\", this place will call the\n> same helper function.\n\nThis is the right idea. I will do this.\n\n> In any case, after doing \"git submodule--helper clone\", the original\n> scripted version does \"git checkout\" in the newly cloned working\n> tree.  I wonder if the \"module_clone\" is done with \"--no-checkout\"\n> option---cloning and letting a commit checked out, and checking out\n> another commit immediately after it, feels somewhat wasteful.\n\nI just had a look: module_clone() already uses \"--no-checkout\".\nSo this should not be a problem.\n\n>> +\t\tprepare_submodule_repo_env(&cp.env_array);\n>> +\t\tcp.git_cmd = 1;\n>> +\t\tcp.dir = add_data->sm_path;\n>> +\t\tstrvec_pushl(&cp.args, \"checkout\", \"-f\", \"-q\", NULL);\n>> +\n>> +\t\tif (add_data->branch) {\n>> +\t\t\tstrvec_pushl(&cp.args, \"-B\", add_data->branch, NULL);\n>> +\t\t\tstrvec_pushf(&cp.args, \"origin/%s\", add_data->branch);\n>> +\t\t}\n>> +\n>> +\t\tif (run_command(&cp))\n>> +\t\t\tdie(_(\"unable to checkout submodule '%s'\"), add_data->sm_path);\n>> +\t}\n>> +\treturn 0;\n>> +}\n> \n> Other than all the commented parts, this looks fairly faithful\n> conversion from the part of the scripted Porcelain that has been\n> lost by the same patch.\n> \n>> +static int add_clone(int argc, const char **argv, const char *prefix)\n>> +{\n>> +\tint force = 0, quiet = 0, dissociate = 0, progress = 0;\n>> +\tstruct add_data add_data = ADD_DATA_INIT;\n>> + ...\n>> +\targc = parse_options(argc, argv, prefix, options, usage, 0);\n>> +\n>> +\tif (argc != 0)\n>> +\t\tusage_with_options(usage, options);\n>> +\n>> +\tadd_data.progress = !!progress;\n>> +\tadd_data.dissociate = !!dissociate;\n>> +\tadd_data.force = !!force;\n>> +\tadd_data.quiet = !!quiet;\n>> +\n>> +\tif (add_submodule(&add_data))\n>> +\t\treturn 1;\n>> +\n>> +\treturn 0;\n>> +}\n> \n> As you wrote the add_clone() vs add_submodule() split of labour, the\n> reason this one is much more nicely organized than module_clone()\n> should be obvious to you.  This part of the design of this patch is\n> very well done.\n> \n> The \"additional step\" I hinted earlier is exactly that---by making\n> the guts of the feature add_submodule() to work on already parsed\n> data and a separate wrapper add_clone() that interfaces to the\n> scripts to parse the command line arguments into a shape that is\n> more appropriate for the inner function, you can later call the\n> inner function add_submodule() directly without going through the\n> unparsing-then-parsing dance from other places.  If module_clone()\n> were split into two in a similar way, then the place to call it from\n> add_submodule() would become much cleaner.\n\nThis definitely sounds like the way to go. I'll be on it.\n\n> Thanks.\n\nThanks for your suggestions. They were very helpful."},{"id":"427092","messageId":"xmqqk0n03k84.fsf@gitster.g","threadId":"55844","inReplyTo":"EAB5ABE4-B032-41AE-B39D-44D0D0A40D52@gmail.com","subject":"Re: [PATCH v3 1/2] submodule--helper: introduce add-clone subcommand","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-06-11T07:59:23Z","receivedAt":"2021-06-11T07:59:34Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Atharva Raykar <raykar.ath@gmail.com> writes:\n\n> I think the larger concern I have to balance here is what level of\n> \"faithfulness\" should I be having with this conversion. My current\n> thoughts on this are:\n>\n>  - Functionality and behaviour should be as similar as possible.\n>\n>  - If there is an *obvious* bug that can be fixed in translation,\n>    then I should fix it.\n>\n>  - If there are error messages that can be made more clear and\n>    consistent with the rest of the Porcelain, then I'll make the\n>    required change.\n\nI have been assuming that we would draw the line between the second\none and the third one, that is, as little end-user visible behaviour\nchanges as possible, unless the behaviour being changed is a clear\nbugfix, during the conversion.  Polishing the results into an even\nbetter shape can and should be done after the initial conversion is\ncompleted.  Switching from die() to fprintf() with error return would\nbe very desirable in the end result, but that would be a bit distracting\nto read during the review of the initial conversion.\n\nThanks.\n\n"},{"id":"427334","messageId":"20210614125157.99426-1-raykar.ath@gmail.com","threadId":"55844","inReplyTo":"20210610083916.96243-1-raykar.ath@gmail.com","subject":"[GSoC] [PATCH v4 0/3] submodule--helper: introduce subcommands for sh to C conversion","fromName":"Atharva Raykar","fromEmail":"raykar.ath@gmail.com","sentAt":"2021-06-14T12:51:54Z","receivedAt":"2021-06-14T12:54:25Z","isPatch":true,"sender":{"key":"raykar.ath@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24277692?v=4"},"body":"This version modifies 'module_clone()' and separates the flag parsing from the\nactual cloning logic. This allows us to use the functionality of\n'submodule--helper clone' without needing to push arguments to a strvec from\n'add_submodule'. We use a new struct called `module_clone_data` instead.\n\nBecause this change involves moving the contents of 'module_clone()' to\n'clone_submodule()', the whole function had to be relocated further down so that\nall the helpers it calls are available to it.\n\nOther changes include making error output match more closely to the shell\nversion, and better usage of the C API\n('is_directory()' -> 'is_nonbare_repo_dir()')\n\nAtharva Raykar (3):\n  submodule--helper: refactor module_clone()\n  submodule--helper: introduce add-clone subcommand\n  submodule--helper: introduce add-config subcommand\n\n builtin/submodule--helper.c | 536 ++++++++++++++++++++++++++++--------\n git-submodule.sh            |  66 +----\n 2 files changed, 425 insertions(+), 177 deletions(-)\n\nRange-diff against v3:\n-:  ---------- > 1:  11d035ce75 submodule--helper: refactor module_clone()\n1:  3b5f7bec7c ! 2:  c85701b79a submodule--helper: introduce add-clone subcommand\n    @@ builtin/submodule--helper.c: static int module_set_branch(int argc, const char *\n     +static int add_submodule(const struct add_data *add_data)\n     +{\n     +\tchar *submod_gitdir_path;\n    ++\tstruct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;\n     +\n     +\t/* perhaps the path already exists and is already a git repo, else clone it */\n     +\tif (is_directory(add_data->sm_path)) {\n    ++\t\tstruct strbuf sm_path = STRBUF_INIT;\n    ++\t\tstrbuf_addstr(&sm_path, add_data->sm_path);\n     +\t\tsubmod_gitdir_path = xstrfmt(\"%s/.git\", add_data->sm_path);\n    -+\t\tif (is_directory(submod_gitdir_path) || file_exists(submod_gitdir_path))\n    -+\t\t\tprintf(_(\"Adding existing path at '%s' to index\\n\"),\n    ++\t\tif (is_nonbare_repository_dir(&sm_path))\n    ++\t\t\tprintf(_(\"Adding existing repo at '%s' to the index\\n\"),\n     +\t\t\t       add_data->sm_path);\n     +\t\telse\n     +\t\t\tdie(_(\"'%s' already exists and is not a valid git repo\"),\n     +\t\t\t    add_data->sm_path);\n    ++\t\tstrbuf_release(&sm_path);\n     +\t\tfree(submod_gitdir_path);\n     +\t} else {\n    -+\t\tstruct strvec clone_args = STRVEC_INIT;\n     +\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n     +\t\tsubmod_gitdir_path = xstrfmt(\".git/modules/%s\", add_data->sm_name);\n     +\n     +\t\tif (is_directory(submod_gitdir_path)) {\n     +\t\t\tif (!add_data->force) {\n    -+\t\t\t\terror(_(\"a git directory for '%s' is found \"\n    -+\t\t\t\t\t\"locally with remote(s):\"), add_data->sm_name);\n    ++\t\t\t\tfprintf(stderr, _(\"A git directory for '%s' is found \"\n    ++\t\t\t\t\t\t  \"locally with remote(s):\"),\n    ++\t\t\t\t\tadd_data->sm_name);\n     +\t\t\t\tshow_fetch_remotes(stderr, add_data->sm_name,\n     +\t\t\t\t\t\t   submod_gitdir_path);\n    -+\t\t\t\tfprintf(stderr,\n    -+\t\t\t\t\t_(\"If you want to reuse this local git \"\n    -+\t\t\t\t\t  \"directory instead of cloning again from\\n\"\n    -+\t\t\t\t\t  \"  %s\\n\"\n    -+\t\t\t\t\t  \"use the '--force' option. If the local git \"\n    -+\t\t\t\t\t  \"directory is not the correct repo\\n\"\n    -+\t\t\t\t\t  \"or if you are unsure what this means, choose \"\n    -+\t\t\t\t\t  \"another name with the '--name' option.\\n\"),\n    -+\t\t\t\t\tadd_data->realrepo);\n     +\t\t\t\tfree(submod_gitdir_path);\n    -+\t\t\t\treturn 1;\n    ++\t\t\t\tdie(_(\"If you want to reuse this local git \"\n    ++\t\t\t\t      \"directory instead of cloning again from\\n\"\n    ++\t\t\t\t      \"  %s\\n\"\n    ++\t\t\t\t      \"use the '--force' option. If the local git \"\n    ++\t\t\t\t      \"directory is not the correct repo\\n\"\n    ++\t\t\t\t      \"or if you are unsure what this means, choose \"\n    ++\t\t\t\t      \"another name with the '--name' option.\\n\"),\n    ++\t\t\t\t    add_data->realrepo);\n     +\t\t\t} else {\n     +\t\t\t\tprintf(_(\"Reactivating local git directory for \"\n     +\t\t\t\t\t \"submodule '%s'\\n\"), add_data->sm_name);\n    @@ builtin/submodule--helper.c: static int module_set_branch(int argc, const char *\n     +\t\t}\n     +\t\tfree(submod_gitdir_path);\n     +\n    -+\t\tstrvec_pushl(&clone_args, \"clone\", \"--path\", add_data->sm_path, \"--name\",\n    -+\t\t\t     add_data->sm_name, \"--url\", add_data->realrepo, NULL);\n    -+\t\tif (add_data->quiet)\n    -+\t\t\tstrvec_push(&clone_args, \"--quiet\");\n    -+\t\tif (add_data->progress)\n    -+\t\t\tstrvec_push(&clone_args, \"--progress\");\n    -+\t\tif (add_data->prefix)\n    -+\t\t\tstrvec_pushl(&clone_args, \"--prefix\", add_data->prefix, NULL);\n    ++\t\tclone_data.prefix = add_data->prefix;\n    ++\t\tclone_data.path = add_data->sm_path;\n    ++\t\tclone_data.name = add_data->sm_name;\n    ++\t\tclone_data.url = add_data->realrepo;\n    ++\t\tclone_data.quiet = add_data->quiet;\n    ++\t\tclone_data.progress = add_data->progress;\n     +\t\tif (add_data->reference_path)\n    -+\t\t\tstrvec_pushl(&clone_args, \"--reference\",\n    -+\t\t\t\t     add_data->reference_path, NULL);\n    -+\t\tif (add_data->dissociate)\n    -+\t\t\tstrvec_push(&clone_args, \"--dissociate\");\n    ++\t\t\tstring_list_append(&clone_data.reference,\n    ++\t\t\t\t\t   xstrdup(add_data->reference_path));\n    ++\t\tclone_data.dissociate = add_data->dissociate;\n     +\t\tif (add_data->depth >= 0)\n    -+\t\t\tstrvec_pushf(&clone_args, \"--depth=%d\", add_data->depth);\n    ++\t\t\tclone_data.depth = xstrfmt(\"%d\", add_data->depth);\n     +\n    -+\t\tif (module_clone(clone_args.nr, clone_args.v, add_data->prefix)) {\n    -+\t\t\tstrvec_clear(&clone_args);\n    ++\t\tif (clone_submodule(&clone_data))\n     +\t\t\treturn -1;\n    -+\t\t}\n    -+\t\tstrvec_clear(&clone_args);\n     +\n     +\t\tprepare_submodule_repo_env(&cp.env_array);\n     +\t\tcp.git_cmd = 1;\n2:  a2a6b4d74c = 3:  6532b4ae11 submodule--helper: introduce add-config subcommand\n-- \n2.31.1\n\n"},{"id":"427335","messageId":"20210614125157.99426-2-raykar.ath@gmail.com","threadId":"55844","inReplyTo":"20210614125157.99426-1-raykar.ath@gmail.com","subject":"[PATCH v4 1/3] submodule--helper: refactor module_clone()","fromName":"Atharva Raykar","fromEmail":"raykar.ath@gmail.com","sentAt":"2021-06-14T12:51:55Z","receivedAt":"2021-06-14T12:54:57Z","isPatch":true,"sender":{"key":"raykar.ath@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24277692?v=4"},"body":"Separate out the core logic of module_clone() from the flag\nparsing---this way we can call the equivalent of the `submodule--helper\nclone` subcommand directly within C, without needing to push arguments\nin a strvec.\n---\n builtin/submodule--helper.c | 241 +++++++++++++++++++-----------------\n 1 file changed, 128 insertions(+), 113 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex d55f6262e9..321b623d81 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1658,45 +1658,20 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n \treturn 0;\n }\n \n-static int clone_submodule(const char *path, const char *gitdir, const char *url,\n-\t\t\t   const char *depth, struct string_list *reference, int dissociate,\n-\t\t\t   int quiet, int progress, int single_branch)\n-{\n-\tstruct child_process cp = CHILD_PROCESS_INIT;\n-\n-\tstrvec_push(&cp.args, \"clone\");\n-\tstrvec_push(&cp.args, \"--no-checkout\");\n-\tif (quiet)\n-\t\tstrvec_push(&cp.args, \"--quiet\");\n-\tif (progress)\n-\t\tstrvec_push(&cp.args, \"--progress\");\n-\tif (depth && *depth)\n-\t\tstrvec_pushl(&cp.args, \"--depth\", depth, NULL);\n-\tif (reference->nr) {\n-\t\tstruct string_list_item *item;\n-\t\tfor_each_string_list_item(item, reference)\n-\t\t\tstrvec_pushl(&cp.args, \"--reference\",\n-\t\t\t\t     item->string, NULL);\n-\t}\n-\tif (dissociate)\n-\t\tstrvec_push(&cp.args, \"--dissociate\");\n-\tif (gitdir && *gitdir)\n-\t\tstrvec_pushl(&cp.args, \"--separate-git-dir\", gitdir, NULL);\n-\tif (single_branch >= 0)\n-\t\tstrvec_push(&cp.args, single_branch ?\n-\t\t\t\t\t  \"--single-branch\" :\n-\t\t\t\t\t  \"--no-single-branch\");\n-\n-\tstrvec_push(&cp.args, \"--\");\n-\tstrvec_push(&cp.args, url);\n-\tstrvec_push(&cp.args, path);\n-\n-\tcp.git_cmd = 1;\n-\tprepare_submodule_repo_env(&cp.env_array);\n-\tcp.no_stdin = 1;\n-\n-\treturn run_command(&cp);\n-}\n+struct module_clone_data {\n+\tconst char* prefix;\n+\tconst char* path;\n+\tconst char* name;\n+\tconst char* url;\n+\tconst char* depth;\n+\tstruct string_list reference;\n+\tunsigned int quiet: 1;\n+\tunsigned int progress: 1;\n+\tunsigned int dissociate: 1;\n+\tunsigned int require_init: 1;\n+\tint single_branch;\n+};\n+#define MODULE_CLONE_DATA_INIT { .reference = STRING_LIST_INIT_NODUP, .single_branch = -1 }\n \n struct submodule_alternate_setup {\n \tconst char *submodule_name;\n@@ -1802,37 +1777,128 @@ static void prepare_possible_alternates(const char *sm_name,\n \tfree(error_strategy);\n }\n \n+static int clone_submodule(struct module_clone_data *clone_data)\n+{\n+\tchar *p, *sm_gitdir;\n+\tchar *sm_alternate = NULL, *error_strategy = NULL;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\n+\tstrbuf_addf(&sb, \"%s/modules/%s\", get_git_dir(), clone_data->name);\n+\tsm_gitdir = absolute_pathdup(sb.buf);\n+\tstrbuf_reset(&sb);\n+\n+\tif (!is_absolute_path(clone_data->path)) {\n+\t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), clone_data->path);\n+\t\tclone_data->path = strbuf_detach(&sb, NULL);\n+\t} else {\n+\t\tclone_data->path = xstrdup(clone_data->path);\n+\t}\n+\n+\tif (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)\n+\t\tdie(_(\"refusing to create/use '%s' in another submodule's \"\n+\t\t      \"git dir\"), sm_gitdir);\n+\n+\tif (!file_exists(sm_gitdir)) {\n+\t\tif (safe_create_leading_directories_const(sm_gitdir) < 0)\n+\t\t\tdie(_(\"could not create directory '%s'\"), sm_gitdir);\n+\n+\t\tprepare_possible_alternates(clone_data->name, &clone_data->reference);\n+\n+\t\tstrvec_push(&cp.args, \"clone\");\n+\t\tstrvec_push(&cp.args, \"--no-checkout\");\n+\t\tif (clone_data->quiet)\n+\t\t\tstrvec_push(&cp.args, \"--quiet\");\n+\t\tif (clone_data->progress)\n+\t\t\tstrvec_push(&cp.args, \"--progress\");\n+\t\tif (clone_data->depth && *(clone_data->depth))\n+\t\t\tstrvec_pushl(&cp.args, \"--depth\", clone_data->depth, NULL);\n+\t\tif (clone_data->reference.nr) {\n+\t\t\tstruct string_list_item *item;\n+\t\t\tfor_each_string_list_item(item, &clone_data->reference)\n+\t\t\t\tstrvec_pushl(&cp.args, \"--reference\",\n+\t\t\t\t\t     item->string, NULL);\n+\t\t}\n+\t\tif (clone_data->dissociate)\n+\t\t\tstrvec_push(&cp.args, \"--dissociate\");\n+\t\tif (sm_gitdir && *sm_gitdir)\n+\t\t\tstrvec_pushl(&cp.args, \"--separate-git-dir\", sm_gitdir, NULL);\n+\t\tif (clone_data->single_branch >= 0)\n+\t\t\tstrvec_push(&cp.args, clone_data->single_branch ?\n+\t\t\t\t    \"--single-branch\" :\n+\t\t\t\t    \"--no-single-branch\");\n+\n+\t\tstrvec_push(&cp.args, \"--\");\n+\t\tstrvec_push(&cp.args, clone_data->url);\n+\t\tstrvec_push(&cp.args, clone_data->path);\n+\n+\t\tcp.git_cmd = 1;\n+\t\tprepare_submodule_repo_env(&cp.env_array);\n+\t\tcp.no_stdin = 1;\n+\n+\t\tif(run_command(&cp))\n+\t\t\tdie(_(\"clone of '%s' into submodule path '%s' failed\"),\n+\t\t\t    clone_data->url, clone_data->path);\n+\t} else {\n+\t\tif (clone_data->require_init && !access(clone_data->path, X_OK) &&\n+\t\t    !is_empty_dir(clone_data->path))\n+\t\t\tdie(_(\"directory not empty: '%s'\"), clone_data->path);\n+\t\tif (safe_create_leading_directories_const(clone_data->path) < 0)\n+\t\t\tdie(_(\"could not create directory '%s'\"), clone_data->path);\n+\t\tstrbuf_addf(&sb, \"%s/index\", sm_gitdir);\n+\t\tunlink_or_warn(sb.buf);\n+\t\tstrbuf_reset(&sb);\n+\t}\n+\n+\tconnect_work_tree_and_git_dir(clone_data->path, sm_gitdir, 0);\n+\n+\tp = git_pathdup_submodule(clone_data->path, \"config\");\n+\tif (!p)\n+\t\tdie(_(\"could not get submodule directory for '%s'\"), clone_data->path);\n+\n+\t/* setup alternateLocation and alternateErrorStrategy in the cloned submodule if needed */\n+\tgit_config_get_string(\"submodule.alternateLocation\", &sm_alternate);\n+\tif (sm_alternate)\n+\t\tgit_config_set_in_file(p, \"submodule.alternateLocation\",\n+\t\t\t\t       sm_alternate);\n+\tgit_config_get_string(\"submodule.alternateErrorStrategy\", &error_strategy);\n+\tif (error_strategy)\n+\t\tgit_config_set_in_file(p, \"submodule.alternateErrorStrategy\",\n+\t\t\t\t       error_strategy);\n+\n+\tfree(sm_alternate);\n+\tfree(error_strategy);\n+\n+\tstrbuf_release(&sb);\n+\tfree(sm_gitdir);\n+\tfree(p);\n+\treturn 0;\n+}\n+\n static int module_clone(int argc, const char **argv, const char *prefix)\n {\n-\tconst char *name = NULL, *url = NULL, *depth = NULL;\n-\tint quiet = 0;\n-\tint progress = 0;\n-\tchar *p, *path = NULL, *sm_gitdir;\n-\tstruct strbuf sb = STRBUF_INIT;\n-\tstruct string_list reference = STRING_LIST_INIT_NODUP;\n-\tint dissociate = 0, require_init = 0;\n-\tchar *sm_alternate = NULL, *error_strategy = NULL;\n-\tint single_branch = -1;\n+\tint dissociate = 0, quiet = 0, progress = 0, require_init = 0;\n+\tstruct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;\n \n \tstruct option module_clone_options[] = {\n-\t\tOPT_STRING(0, \"prefix\", &prefix,\n+\t\tOPT_STRING(0, \"prefix\", &clone_data.prefix,\n \t\t\t   N_(\"path\"),\n \t\t\t   N_(\"alternative anchor for relative paths\")),\n-\t\tOPT_STRING(0, \"path\", &path,\n+\t\tOPT_STRING(0, \"path\", &clone_data.path,\n \t\t\t   N_(\"path\"),\n \t\t\t   N_(\"where the new submodule will be cloned to\")),\n-\t\tOPT_STRING(0, \"name\", &name,\n+\t\tOPT_STRING(0, \"name\", &clone_data.name,\n \t\t\t   N_(\"string\"),\n \t\t\t   N_(\"name of the new submodule\")),\n-\t\tOPT_STRING(0, \"url\", &url,\n+\t\tOPT_STRING(0, \"url\", &clone_data.url,\n \t\t\t   N_(\"string\"),\n \t\t\t   N_(\"url where to clone the submodule from\")),\n-\t\tOPT_STRING_LIST(0, \"reference\", &reference,\n+\t\tOPT_STRING_LIST(0, \"reference\", &clone_data.reference,\n \t\t\t   N_(\"repo\"),\n \t\t\t   N_(\"reference repository\")),\n \t\tOPT_BOOL(0, \"dissociate\", &dissociate,\n \t\t\t   N_(\"use --reference only while cloning\")),\n-\t\tOPT_STRING(0, \"depth\", &depth,\n+\t\tOPT_STRING(0, \"depth\", &clone_data.depth,\n \t\t\t   N_(\"string\"),\n \t\t\t   N_(\"depth for shallow clones\")),\n \t\tOPT__QUIET(&quiet, \"Suppress output for cloning a submodule\"),\n@@ -1840,7 +1906,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)\n \t\t\t   N_(\"force cloning progress\")),\n \t\tOPT_BOOL(0, \"require-init\", &require_init,\n \t\t\t   N_(\"disallow cloning into non-empty directory\")),\n-\t\tOPT_BOOL(0, \"single-branch\", &single_branch,\n+\t\tOPT_BOOL(0, \"single-branch\", &clone_data.single_branch,\n \t\t\t N_(\"clone only one branch, HEAD or --branch\")),\n \t\tOPT_END()\n \t};\n@@ -1856,67 +1922,16 @@ static int module_clone(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, module_clone_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n-\tif (argc || !url || !path || !*path)\n+\tclone_data.dissociate = !!dissociate;\n+\tclone_data.quiet = !!quiet;\n+\tclone_data.progress = !!progress;\n+\tclone_data.require_init = !!require_init;\n+\n+\tif (argc || !clone_data.url || !clone_data.path || !*(clone_data.path))\n \t\tusage_with_options(git_submodule_helper_usage,\n \t\t\t\t   module_clone_options);\n \n-\tstrbuf_addf(&sb, \"%s/modules/%s\", get_git_dir(), name);\n-\tsm_gitdir = absolute_pathdup(sb.buf);\n-\tstrbuf_reset(&sb);\n-\n-\tif (!is_absolute_path(path)) {\n-\t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), path);\n-\t\tpath = strbuf_detach(&sb, NULL);\n-\t} else\n-\t\tpath = xstrdup(path);\n-\n-\tif (validate_submodule_git_dir(sm_gitdir, name) < 0)\n-\t\tdie(_(\"refusing to create/use '%s' in another submodule's \"\n-\t\t\t\"git dir\"), sm_gitdir);\n-\n-\tif (!file_exists(sm_gitdir)) {\n-\t\tif (safe_create_leading_directories_const(sm_gitdir) < 0)\n-\t\t\tdie(_(\"could not create directory '%s'\"), sm_gitdir);\n-\n-\t\tprepare_possible_alternates(name, &reference);\n-\n-\t\tif (clone_submodule(path, sm_gitdir, url, depth, &reference, dissociate,\n-\t\t\t\t    quiet, progress, single_branch))\n-\t\t\tdie(_(\"clone of '%s' into submodule path '%s' failed\"),\n-\t\t\t    url, path);\n-\t} else {\n-\t\tif (require_init && !access(path, X_OK) && !is_empty_dir(path))\n-\t\t\tdie(_(\"directory not empty: '%s'\"), path);\n-\t\tif (safe_create_leading_directories_const(path) < 0)\n-\t\t\tdie(_(\"could not create directory '%s'\"), path);\n-\t\tstrbuf_addf(&sb, \"%s/index\", sm_gitdir);\n-\t\tunlink_or_warn(sb.buf);\n-\t\tstrbuf_reset(&sb);\n-\t}\n-\n-\tconnect_work_tree_and_git_dir(path, sm_gitdir, 0);\n-\n-\tp = git_pathdup_submodule(path, \"config\");\n-\tif (!p)\n-\t\tdie(_(\"could not get submodule directory for '%s'\"), path);\n-\n-\t/* setup alternateLocation and alternateErrorStrategy in the cloned submodule if needed */\n-\tgit_config_get_string(\"submodule.alternateLocation\", &sm_alternate);\n-\tif (sm_alternate)\n-\t\tgit_config_set_in_file(p, \"submodule.alternateLocation\",\n-\t\t\t\t\t   sm_alternate);\n-\tgit_config_get_string(\"submodule.alternateErrorStrategy\", &error_strategy);\n-\tif (error_strategy)\n-\t\tgit_config_set_in_file(p, \"submodule.alternateErrorStrategy\",\n-\t\t\t\t\t   error_strategy);\n-\n-\tfree(sm_alternate);\n-\tfree(error_strategy);\n-\n-\tstrbuf_release(&sb);\n-\tfree(sm_gitdir);\n-\tfree(path);\n-\tfree(p);\n+\tclone_submodule(&clone_data);\n \treturn 0;\n }\n \n-- \n2.31.1\n\n"},{"id":"427336","messageId":"20210614125157.99426-4-raykar.ath@gmail.com","threadId":"55844","inReplyTo":"20210614125157.99426-1-raykar.ath@gmail.com","subject":"[PATCH v4 3/3] submodule--helper: introduce add-config subcommand","fromName":"Atharva Raykar","fromEmail":"raykar.ath@gmail.com","sentAt":"2021-06-14T12:51:57Z","receivedAt":"2021-06-14T12:55:01Z","isPatch":true,"sender":{"key":"raykar.ath@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24277692?v=4"},"body":"Add a new \"add-config\" subcommand to `git submodule--helper` with the\ngoal of converting part of the shell code in git-submodule.sh related to\n`git submodule add` into C code. This new subcommand sets the\nconfiguration variables of a newly added submodule, by registering the\nurl in local git config, as well as the submodule name and path in the\n.gitmodules file. It also sets 'submodule.<name>.active' to \"true\" if\nthe submodule path has not already been covered by any pathspec\nspecified in 'submodule.active'.\n\nThis is meant to be a faithful conversion from shell to C, with only one\nminor change: A warning is emitted if no value is specified in\n'submodule.active', ie, the config looks like: \"[submodule] active\\n\",\nbecause it is an invalid configuration. It would be helpful to let the\nuser know that the pathspec is unset, and the value of\n'submodule.<name>.active' might be set to 'true' so that they can\nrectify their configuration and prevent future surprises (especially\ngiven that the latter variable has a higher priority than the former).\n\nThe structure of the conditional to check if we need to set the 'active'\ntoggle looks different from the shell version -- but behaves the same.\nThe change was made to decrease code duplication. A comment has been\nadded to explain that only one value of 'submodule.active' is obtained\nto check if we need to call is_submodule_active() at all.\n\nThis is part of a series of changes that will result in all of\n'submodule add' being converted to C.\n\nSigned-off-by: Atharva Raykar <raykar.ath@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Shourya Shukla <shouryashukla.oo@gmail.com>\nBased-on-patch-by: Shourya Shukla <shouryashukla.oo@gmail.com>\nBased-on-patch-by: Prathamesh Chavan <pc44800@gmail.com>\n---\n builtin/submodule--helper.c | 119 ++++++++++++++++++++++++++++++++++++\n git-submodule.sh            |  28 +--------\n 2 files changed, 120 insertions(+), 27 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 6dffaeb6cb..c4b2aa6537 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2935,6 +2935,124 @@ static int add_clone(int argc, const char **argv, const char *prefix)\n \treturn 0;\n }\n \n+static void configure_added_submodule(struct add_data *add_data)\n+{\n+\tchar *key, *submod_pathspec = NULL;\n+\tstruct child_process add_submod = CHILD_PROCESS_INIT;\n+\tstruct child_process add_gitmodules = CHILD_PROCESS_INIT;\n+\tint pathspec_key_exists, activate = 0;\n+\n+\tkey = xstrfmt(\"submodule.%s.url\", add_data->sm_name);\n+\tgit_config_set_gently(key, add_data->realrepo);\n+\tfree(key);\n+\n+\tadd_submod.git_cmd = 1;\n+\tstrvec_pushl(&add_submod.args, \"add\",\n+\t\t     \"--no-warn-embedded-repo\", NULL);\n+\tif (add_data->force)\n+\t\tstrvec_push(&add_submod.args, \"--force\");\n+\tstrvec_pushl(&add_submod.args, \"--\", add_data->sm_path, NULL);\n+\n+\tif (run_command(&add_submod))\n+\t\tdie(_(\"Failed to add submodule '%s'\"), add_data->sm_path);\n+\n+\tkey = xstrfmt(\"submodule.%s.path\", add_data->sm_name);\n+\tconfig_set_in_gitmodules_file_gently(key, add_data->sm_path);\n+\tfree(key);\n+\tkey = xstrfmt(\"submodule.%s.url\", add_data->sm_name);\n+\tconfig_set_in_gitmodules_file_gently(key, add_data->repo);\n+\tfree(key);\n+\tif (add_data->branch) {\n+\t\tkey = xstrfmt(\"submodule.%s.branch\", add_data->sm_path);\n+\t\tconfig_set_in_gitmodules_file_gently(key, add_data->branch);\n+\t\tfree(key);\n+\t}\n+\n+\tadd_gitmodules.git_cmd = 1;\n+\tstrvec_pushl(&add_gitmodules.args,\n+\t\t     \"add\", \"--force\", \"--\", \".gitmodules\", NULL);\n+\n+\tif (run_command(&add_gitmodules))\n+\t\tdie(_(\"Failed to register submodule '%s'\"), add_data->sm_path);\n+\n+\t/*\n+\t * NEEDSWORK: In a multi-working-tree world this needs to be\n+\t * set in the per-worktree config.\n+\t */\n+\tpathspec_key_exists = !git_config_get_string(\"submodule.active\",\n+\t\t\t\t\t\t     &submod_pathspec);\n+\tif (pathspec_key_exists && !submod_pathspec) {\n+\t\twarning(_(\"The submodule.active configuration exists, but the \"\n+\t\t\t  \"pathspec was unset. If the submodule is not already \"\n+\t\t\t  \"active, the value of submodule.%s.active will be \"\n+\t\t\t  \"be set to 'true'.\"), add_data->sm_name);\n+\t\tactivate = 1;\n+\t}\n+\n+\t/*\n+\t * If submodule.active does not exist, or if the pathspec was unset,\n+\t * we will activate this module unconditionally.\n+\t *\n+\t * Otherwise, we ask is_submodule_active(), which iterates\n+\t * through all the values of 'submodule.active' to determine\n+\t * if this module is already active.\n+\t */\n+\tif (!pathspec_key_exists || activate ||\n+\t    !is_submodule_active(the_repository, add_data->sm_path)) {\n+\t\tkey = xstrfmt(\"submodule.%s.active\", add_data->sm_name);\n+\t\tgit_config_set_gently(key, \"true\");\n+\t\tfree(key);\n+\t}\n+}\n+\n+static int add_config(int argc, const char **argv, const char *prefix)\n+{\n+\tint force = 0;\n+\tstruct add_data add_data = ADD_DATA_INIT;\n+\n+\tstruct option options[] = {\n+\t\tOPT_STRING('b', \"branch\", &add_data.branch,\n+\t\t\t   N_(\"branch\"),\n+\t\t\t   N_(\"branch of repository to store in \"\n+\t\t\t      \"the submodule configuration\")),\n+\t\tOPT_STRING(0, \"url\", &add_data.repo,\n+\t\t\t   N_(\"string\"),\n+\t\t\t   N_(\"url to clone submodule from\")),\n+\t\tOPT_STRING(0, \"resolved-url\", &add_data.realrepo,\n+\t\t\t   N_(\"string\"),\n+\t\t\t   N_(\"url to clone the submodule from, after it has \"\n+\t\t\t      \"been dereferenced relative to parent's url, \"\n+\t\t\t      \"in the case where <url> is a relative url\")),\n+\t\tOPT_STRING(0, \"path\", &add_data.sm_path,\n+\t\t\t   N_(\"path\"),\n+\t\t\t   N_(\"where the new submodule will be cloned to\")),\n+\t\tOPT_STRING(0, \"name\", &add_data.sm_name,\n+\t\t\t   N_(\"string\"),\n+\t\t\t   N_(\"name of the new submodule\")),\n+\t\tOPT__FORCE(&force, N_(\"allow adding an otherwise ignored submodule path\"),\n+\t\t\t   PARSE_OPT_NOCOMPLETE),\n+\t\tOPT_END()\n+\t};\n+\n+\tconst char *const usage[] = {\n+\t\tN_(\"git submodule--helper add-config \"\n+\t\t   \"[--force|-f] [--branch|-b <branch>] \"\n+\t\t   \"--url <url> --resolved-url <resolved-url> \"\n+\t\t   \"--path <path> --name <name>\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, usage, 0);\n+\n+\tif (argc != 0)\n+\t\tusage_with_options(usage, options);\n+\n+\tadd_data.force = !!force;\n+\tconfigure_added_submodule(&add_data);\n+\n+\treturn 0;\n+}\n+\n #define SUPPORT_SUPER_PREFIX (1<<0)\n \n struct cmd_struct {\n@@ -2948,6 +3066,7 @@ static struct cmd_struct commands[] = {\n \t{\"name\", module_name, 0},\n \t{\"clone\", module_clone, 0},\n \t{\"add-clone\", add_clone, 0},\n+\t{\"add-config\", add_config, 0},\n \t{\"update-module-mode\", module_update_module_mode, 0},\n \t{\"update-clone\", update_clone, 0},\n \t{\"ensure-core-worktree\", ensure_core_worktree, 0},\ndiff --git a/git-submodule.sh b/git-submodule.sh\nindex f71e1e5495..9826378fa6 100755\n--- a/git-submodule.sh\n+++ b/git-submodule.sh\n@@ -242,33 +242,7 @@ cmd_add()\n \tfi\n \n \tgit 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\n-\tgit config submodule.\"$sm_name\".url \"$realrepo\"\n-\n-\tgit add --no-warn-embedded-repo $force \"$sm_path\" ||\n-\tdie \"$(eval_gettext \"Failed to add submodule '\\$sm_path'\")\"\n-\n-\tgit submodule--helper config submodule.\"$sm_name\".path \"$sm_path\" &&\n-\tgit submodule--helper config submodule.\"$sm_name\".url \"$repo\" &&\n-\tif test -n \"$branch\"\n-\tthen\n-\t\tgit submodule--helper config submodule.\"$sm_name\".branch \"$branch\"\n-\tfi &&\n-\tgit add --force .gitmodules ||\n-\tdie \"$(eval_gettext \"Failed to register submodule '\\$sm_path'\")\"\n-\n-\t# NEEDSWORK: In a multi-working-tree world, this needs to be\n-\t# set in the per-worktree config.\n-\tif git config --get submodule.active >/dev/null\n-\tthen\n-\t\t# If the submodule being adding isn't already covered by the\n-\t\t# current configured pathspec, set the submodule's active flag\n-\t\tif ! git submodule--helper is-active \"$sm_path\"\n-\t\tthen\n-\t\t\tgit config submodule.\"$sm_name\".active \"true\"\n-\t\tfi\n-\telse\n-\t\tgit config submodule.\"$sm_name\".active \"true\"\n-\tfi\n+\tgit submodule--helper add-config ${force:+--force} ${branch:+--branch \"$branch\"} --url \"$repo\" --resolved-url \"$realrepo\" --path \"$sm_path\" --name \"$sm_name\"\n }\n \n #\n-- \n2.31.1\n\n"},{"id":"427337","messageId":"20210614125157.99426-3-raykar.ath@gmail.com","threadId":"55844","inReplyTo":"20210614125157.99426-1-raykar.ath@gmail.com","subject":"[PATCH v4 2/3] submodule--helper: introduce add-clone subcommand","fromName":"Atharva Raykar","fromEmail":"raykar.ath@gmail.com","sentAt":"2021-06-14T12:51:56Z","receivedAt":"2021-06-14T12:55:53Z","isPatch":true,"sender":{"key":"raykar.ath@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24277692?v=4"},"body":"Let's add a new \"add-clone\" subcommand to `git submodule--helper` with\nthe goal of converting part of the shell code in git-submodule.sh\nrelated to `git submodule add` into C code. This new subcommand clones\nthe repository that is to be added, and checks out to the appropriate\nbranch.\n\nThis is meant to be a faithful conversion that leaves the behaviour of\n'submodule add' unchanged. The only minor change is that if a submodule name has\nbeen supplied with a name that clashes with a local submodule, the message shown\nto the user (\"A git directory for 'foo' is found locally...\") is prepended with\n\"error\" for clarity.\n\nThis is part of a series of changes that will result in all of 'submodule add'\nbeing converted to C.\n\nSigned-off-by: Atharva Raykar <raykar.ath@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Shourya Shukla <shouryashukla.oo@gmail.com>\nBased-on-patch-by: Shourya Shukla <shouryashukla.oo@gmail.com>\nBased-on-patch-by: Prathamesh Chavan <pc44800@gmail.com>\nHelped-by: Đoàn Trần Công Danh <congdanhqx@gmail.com>\n---\n builtin/submodule--helper.c | 176 ++++++++++++++++++++++++++++++++++++\n git-submodule.sh            |  38 +-------\n 2 files changed, 177 insertions(+), 37 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 321b623d81..6dffaeb6cb 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2760,6 +2760,181 @@ static int module_set_branch(int argc, const char **argv, const char *prefix)\n \treturn !!ret;\n }\n \n+struct add_data {\n+\tconst char *prefix;\n+\tconst char *branch;\n+\tconst char *reference_path;\n+\tconst char *sm_path;\n+\tconst char *sm_name;\n+\tconst char *repo;\n+\tconst char *realrepo;\n+\tint depth;\n+\tunsigned int force: 1;\n+\tunsigned int quiet: 1;\n+\tunsigned int progress: 1;\n+\tunsigned int dissociate: 1;\n+};\n+#define ADD_DATA_INIT { .depth = -1 }\n+\n+static void show_fetch_remotes(FILE *output, const char *sm_name, const char *git_dir_path)\n+{\n+\tstruct child_process cp_remote = CHILD_PROCESS_INIT;\n+\tstruct strbuf sb_remote_out = STRBUF_INIT;\n+\n+\tcp_remote.git_cmd = 1;\n+\tstrvec_pushf(&cp_remote.env_array,\n+\t\t     \"GIT_DIR=%s\", git_dir_path);\n+\tstrvec_push(&cp_remote.env_array, \"GIT_WORK_TREE=.\");\n+\tstrvec_pushl(&cp_remote.args, \"remote\", \"-v\", NULL);\n+\tif (!capture_command(&cp_remote, &sb_remote_out, 0)) {\n+\t\tchar *next_line;\n+\t\tchar *line = sb_remote_out.buf;\n+\t\twhile ((next_line = strchr(line, '\\n')) != NULL) {\n+\t\t\tsize_t len = next_line - line;\n+\t\t\tif (strip_suffix_mem(line, &len, \" (fetch)\"))\n+\t\t\t\tfprintf(output, \"  %.*s\\n\", (int)len, line);\n+\t\t\tline = next_line + 1;\n+\t\t}\n+\t}\n+\n+\tstrbuf_release(&sb_remote_out);\n+}\n+\n+static int add_submodule(const struct add_data *add_data)\n+{\n+\tchar *submod_gitdir_path;\n+\tstruct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;\n+\n+\t/* perhaps the path already exists and is already a git repo, else clone it */\n+\tif (is_directory(add_data->sm_path)) {\n+\t\tstruct strbuf sm_path = STRBUF_INIT;\n+\t\tstrbuf_addstr(&sm_path, add_data->sm_path);\n+\t\tsubmod_gitdir_path = xstrfmt(\"%s/.git\", add_data->sm_path);\n+\t\tif (is_nonbare_repository_dir(&sm_path))\n+\t\t\tprintf(_(\"Adding existing repo at '%s' to the index\\n\"),\n+\t\t\t       add_data->sm_path);\n+\t\telse\n+\t\t\tdie(_(\"'%s' already exists and is not a valid git repo\"),\n+\t\t\t    add_data->sm_path);\n+\t\tstrbuf_release(&sm_path);\n+\t\tfree(submod_gitdir_path);\n+\t} else {\n+\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\t\tsubmod_gitdir_path = xstrfmt(\".git/modules/%s\", add_data->sm_name);\n+\n+\t\tif (is_directory(submod_gitdir_path)) {\n+\t\t\tif (!add_data->force) {\n+\t\t\t\tfprintf(stderr, _(\"A git directory for '%s' is found \"\n+\t\t\t\t\t\t  \"locally with remote(s):\"),\n+\t\t\t\t\tadd_data->sm_name);\n+\t\t\t\tshow_fetch_remotes(stderr, add_data->sm_name,\n+\t\t\t\t\t\t   submod_gitdir_path);\n+\t\t\t\tfree(submod_gitdir_path);\n+\t\t\t\tdie(_(\"If you want to reuse this local git \"\n+\t\t\t\t      \"directory instead of cloning again from\\n\"\n+\t\t\t\t      \"  %s\\n\"\n+\t\t\t\t      \"use the '--force' option. If the local git \"\n+\t\t\t\t      \"directory is not the correct repo\\n\"\n+\t\t\t\t      \"or if you are unsure what this means, choose \"\n+\t\t\t\t      \"another name with the '--name' option.\\n\"),\n+\t\t\t\t    add_data->realrepo);\n+\t\t\t} else {\n+\t\t\t\tprintf(_(\"Reactivating local git directory for \"\n+\t\t\t\t\t \"submodule '%s'\\n\"), add_data->sm_name);\n+\t\t\t}\n+\t\t}\n+\t\tfree(submod_gitdir_path);\n+\n+\t\tclone_data.prefix = add_data->prefix;\n+\t\tclone_data.path = add_data->sm_path;\n+\t\tclone_data.name = add_data->sm_name;\n+\t\tclone_data.url = add_data->realrepo;\n+\t\tclone_data.quiet = add_data->quiet;\n+\t\tclone_data.progress = add_data->progress;\n+\t\tif (add_data->reference_path)\n+\t\t\tstring_list_append(&clone_data.reference,\n+\t\t\t\t\t   xstrdup(add_data->reference_path));\n+\t\tclone_data.dissociate = add_data->dissociate;\n+\t\tif (add_data->depth >= 0)\n+\t\t\tclone_data.depth = xstrfmt(\"%d\", add_data->depth);\n+\n+\t\tif (clone_submodule(&clone_data))\n+\t\t\treturn -1;\n+\n+\t\tprepare_submodule_repo_env(&cp.env_array);\n+\t\tcp.git_cmd = 1;\n+\t\tcp.dir = add_data->sm_path;\n+\t\tstrvec_pushl(&cp.args, \"checkout\", \"-f\", \"-q\", NULL);\n+\n+\t\tif (add_data->branch) {\n+\t\t\tstrvec_pushl(&cp.args, \"-B\", add_data->branch, NULL);\n+\t\t\tstrvec_pushf(&cp.args, \"origin/%s\", add_data->branch);\n+\t\t}\n+\n+\t\tif (run_command(&cp))\n+\t\t\tdie(_(\"unable to checkout submodule '%s'\"), add_data->sm_path);\n+\t}\n+\treturn 0;\n+}\n+\n+static int add_clone(int argc, const char **argv, const char *prefix)\n+{\n+\tint force = 0, quiet = 0, dissociate = 0, progress = 0;\n+\tstruct add_data add_data = ADD_DATA_INIT;\n+\n+\tstruct option options[] = {\n+\t\tOPT_STRING('b', \"branch\", &add_data.branch,\n+\t\t\t   N_(\"branch\"),\n+\t\t\t   N_(\"branch of repository to checkout on cloning\")),\n+\t\tOPT_STRING(0, \"prefix\", &add_data.prefix,\n+\t\t\t   N_(\"path\"),\n+\t\t\t   N_(\"alternative anchor for relative paths\")),\n+\t\tOPT_STRING(0, \"path\", &add_data.sm_path,\n+\t\t\t   N_(\"path\"),\n+\t\t\t   N_(\"where the new submodule will be cloned to\")),\n+\t\tOPT_STRING(0, \"name\", &add_data.sm_name,\n+\t\t\t   N_(\"string\"),\n+\t\t\t   N_(\"name of the new submodule\")),\n+\t\tOPT_STRING(0, \"url\", &add_data.realrepo,\n+\t\t\t   N_(\"string\"),\n+\t\t\t   N_(\"url where to clone the submodule from\")),\n+\t\tOPT_STRING(0, \"reference\", &add_data.reference_path,\n+\t\t\t   N_(\"repo\"),\n+\t\t\t   N_(\"reference repository\")),\n+\t\tOPT_BOOL(0, \"dissociate\", &dissociate,\n+\t\t\t N_(\"use --reference only while cloning\")),\n+\t\tOPT_INTEGER(0, \"depth\", &add_data.depth,\n+\t\t\t    N_(\"depth for shallow clones\")),\n+\t\tOPT_BOOL(0, \"progress\", &progress,\n+\t\t\t N_(\"force cloning progress\")),\n+\t\tOPT__FORCE(&force, N_(\"allow adding an otherwise ignored submodule path\"),\n+\t\t\t   PARSE_OPT_NOCOMPLETE),\n+\t\tOPT__QUIET(&quiet, \"suppress output for cloning a submodule\"),\n+\t\tOPT_END()\n+\t};\n+\n+\tconst char *const usage[] = {\n+\t\tN_(\"git submodule--helper add-clone [<options>...] \"\n+\t\t   \"--url <url> --path <path> --name <name>\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, usage, 0);\n+\n+\tif (argc != 0)\n+\t\tusage_with_options(usage, options);\n+\n+\tadd_data.progress = !!progress;\n+\tadd_data.dissociate = !!dissociate;\n+\tadd_data.force = !!force;\n+\tadd_data.quiet = !!quiet;\n+\n+\tif (add_submodule(&add_data))\n+\t\treturn 1;\n+\n+\treturn 0;\n+}\n+\n #define SUPPORT_SUPER_PREFIX (1<<0)\n \n struct cmd_struct {\n@@ -2772,6 +2947,7 @@ static struct cmd_struct commands[] = {\n \t{\"list\", module_list, 0},\n \t{\"name\", module_name, 0},\n \t{\"clone\", module_clone, 0},\n+\t{\"add-clone\", add_clone, 0},\n \t{\"update-module-mode\", module_update_module_mode, 0},\n \t{\"update-clone\", update_clone, 0},\n \t{\"ensure-core-worktree\", ensure_core_worktree, 0},\ndiff --git a/git-submodule.sh b/git-submodule.sh\nindex 4678378424..f71e1e5495 100755\n--- a/git-submodule.sh\n+++ b/git-submodule.sh\n@@ -241,43 +241,7 @@ cmd_add()\n \t\tdie \"$(eval_gettext \"'$sm_name' is not a valid submodule name\")\"\n \tfi\n \n-\t# perhaps the path exists and is already a git repo, else clone it\n-\tif test -e \"$sm_path\"\n-\tthen\n-\t\tif test -d \"$sm_path\"/.git || test -f \"$sm_path\"/.git\n-\t\tthen\n-\t\t\teval_gettextln \"Adding existing repo at '\\$sm_path' to the index\"\n-\t\telse\n-\t\t\tdie \"$(eval_gettext \"'\\$sm_path' already exists and is not a valid git repo\")\"\n-\t\tfi\n-\n-\telse\n-\t\tif test -d \".git/modules/$sm_name\"\n-\t\tthen\n-\t\t\tif test -z \"$force\"\n-\t\t\tthen\n-\t\t\t\teval_gettextln >&2 \"A git directory for '\\$sm_name' is found locally with remote(s):\"\n-\t\t\t\tGIT_DIR=\".git/modules/$sm_name\" GIT_WORK_TREE=. git remote -v | grep '(fetch)' | sed -e s,^,\"  \", -e s,' (fetch)',, >&2\n-\t\t\t\tdie \"$(eval_gettextln \"\\\n-If you want to reuse this local git directory instead of cloning again from\n-  \\$realrepo\n-use the '--force' option. If the local git directory is not the correct repo\n-or you are unsure what this means choose another name with the '--name' option.\")\"\n-\t\t\telse\n-\t\t\t\teval_gettextln \"Reactivating local git directory for submodule '\\$sm_name'.\"\n-\t\t\tfi\n-\t\tfi\n-\t\tgit 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\n-\t\t(\n-\t\t\tsanitize_submodule_env\n-\t\t\tcd \"$sm_path\" &&\n-\t\t\t# ash fails to wordsplit ${branch:+-b \"$branch\"...}\n-\t\t\tcase \"$branch\" in\n-\t\t\t'') git checkout -f -q ;;\n-\t\t\t?*) git checkout -f -q -B \"$branch\" \"origin/$branch\" ;;\n-\t\t\tesac\n-\t\t) || die \"$(eval_gettext \"Unable to checkout submodule '\\$sm_path'\")\"\n-\tfi\n+\tgit 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\n \tgit config submodule.\"$sm_name\".url \"$realrepo\"\n \n \tgit add --no-warn-embedded-repo $force \"$sm_path\" ||\n-- \n2.31.1\n\n"},{"id":"427380","messageId":"gohp6kh7i0xm08.fsf@cpm12071.fritz.box","threadId":"55844","inReplyTo":"20210614125157.99426-4-raykar.ath@gmail.com","subject":"Re: [PATCH v4 3/3] submodule--helper: introduce add-config subcommand","fromName":"Rafael Silva","fromEmail":"rafaeloliveira.cs@gmail.com","sentAt":"2021-06-14T19:51:53Z","receivedAt":"2021-06-14T19:52:58Z","isPatch":true,"sender":{"key":"rafaeloliveira.cs@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5935135?v=4"},"body":"\nAtharva Raykar <raykar.ath@gmail.com> writes:\n\n> ---\n>  builtin/submodule--helper.c | 119 ++++++++++++++++++++++++++++++++++++\n>  git-submodule.sh            |  28 +--------\n>  2 files changed, 120 insertions(+), 27 deletions(-)\n>\n\nI do not have enough expertise to judge the entire content of this\npatch. I would like, however, to propose a slight code change for the\nsake of readability.\n\n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> index 6dffaeb6cb..c4b2aa6537 100644\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -2935,6 +2935,124 @@ static int add_clone(int argc, const char **argv, const char *prefix)\n>  \treturn 0;\n>  }\n>  \n> +static void configure_added_submodule(struct add_data *add_data)\n> +{\n> +\tchar *key, *submod_pathspec = NULL;\n> +\tstruct child_process add_submod = CHILD_PROCESS_INIT;\n> +\tstruct child_process add_gitmodules = CHILD_PROCESS_INIT;\n> +\tint pathspec_key_exists, activate = 0;\n> +\n> +\tkey = xstrfmt(\"submodule.%s.url\", add_data->sm_name);\n> +\tgit_config_set_gently(key, add_data->realrepo);\n> +\tfree(key);\n> +\n> +\tadd_submod.git_cmd = 1;\n> +\tstrvec_pushl(&add_submod.args, \"add\",\n> +\t\t     \"--no-warn-embedded-repo\", NULL);\n> +\tif (add_data->force)\n> +\t\tstrvec_push(&add_submod.args, \"--force\");\n> +\tstrvec_pushl(&add_submod.args, \"--\", add_data->sm_path, NULL);\n> +\n> +\tif (run_command(&add_submod))\n> +\t\tdie(_(\"Failed to add submodule '%s'\"), add_data->sm_path);\n> +\n> +\tkey = xstrfmt(\"submodule.%s.path\", add_data->sm_name);\n> +\tconfig_set_in_gitmodules_file_gently(key, add_data->sm_path);\n> +\tfree(key);\n\nThis above three lines of code is very similar to the two operations that\nfollows (including the one inside the `if (add_data->branch)`\ncondition. So [ ... ]\n\n> +\tkey = xstrfmt(\"submodule.%s.url\", add_data->sm_name);\n> +\tconfig_set_in_gitmodules_file_gently(key, add_data->repo);\n> +\tfree(key);\n> +\tif (add_data->branch) {\n> +\t\tkey = xstrfmt(\"submodule.%s.branch\", add_data->sm_path);\n> +\t\tconfig_set_in_gitmodules_file_gently(key, add_data->branch);\n> +\t\tfree(key);\n> +\t}\n> +\n\n[ ... ] it might be worth to write a small wrapper that will perform: (1)\n`xstrfmt()` on the specified config section, (2) set the configuration\nin the file and (3) free()'ing the variable inside the wrapper. Thus,\nmost of these code will become one liners that is easier to read (given\nthe function is properly named :) ).\n\nAfter abstracting the code on the wrapper, this code will become\nsomething like:\n\n\n    function_properly_named(\"submodule.%s.path\", add_data->sm_name, add_data->sm_path);\n    function_properly_named(\"submodule.%s.url\", add_data->sm_name, add_data->repo);\n    if (add_data->branch)\n         function_properly_named(\"submodule.%s.branch\", add_data->sm_path, add_data->branch);\n\n\nJust as an example, here's a diff to demonstrate the argument:\n\n-- >8 --\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 65f79fbd53..48ea909f51 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2934,6 +2934,14 @@ static int add_clone(int argc, const char **argv, const char *prefix)\n \treturn 0;\n }\n \n+void add_config_in_submodules_file(const char *keyfmt, const char *submodule,\n+\t\t\t  const char *value)\n+{\n+\tchar *key = xstrfmt(keyfmt, submodule);\n+\tconfig_set_in_gitmodules_file_gently(key, value);\n+\tfree(key);\n+}\n+\n static void configure_added_submodule(struct add_data *add_data)\n {\n \tchar *key, *submod_pathspec = NULL;\n@@ -2955,17 +2963,10 @@ static void configure_added_submodule(struct add_data *add_data)\n \tif (run_command(&add_submod))\n \t\tdie(_(\"Failed to add submodule '%s'\"), add_data->sm_path);\n \n-\tkey = xstrfmt(\"submodule.%s.path\", add_data->sm_name);\n-\tconfig_set_in_gitmodules_file_gently(key, add_data->sm_path);\n-\tfree(key);\n-\tkey = xstrfmt(\"submodule.%s.url\", add_data->sm_name);\n-\tconfig_set_in_gitmodules_file_gently(key, add_data->repo);\n-\tfree(key);\n-\tif (add_data->branch) {\n-\t\tkey = xstrfmt(\"submodule.%s.branch\", add_data->sm_path);\n-\t\tconfig_set_in_gitmodules_file_gently(key, add_data->branch);\n-\t\tfree(key);\n-\t}\n+\tadd_config_in_submodules_file(\"submodule.%s.path\", add_data->sm_name, add_data->sm_path);\n+\tadd_config_in_submodules_file(\"submodule.%s.url\", add_data->sm_name, add_data->repo);\n+\tif (add_data->branch)\n+\t\tadd_config_in_submodules_file(\"submodule.%s.branch\", add_data->sm_path, add_data->branch);\n \n \tadd_gitmodules.git_cmd = 1;\n \tstrvec_pushl(&add_gitmodules.args,\n\n-- >8 --\n\nA proper name than \"add_config_in_submodules_file\" should be considered - I'm\nnot very good in naming things.\n\nThese change does (should) not change the behavior of code, even though\nI believe it make the code simpler to read, I do not have strong\nopinions about it. So, take this proposal as you wish. \n\n-- \nThanks\nRafael\n"},{"id":"427381","messageId":"CAPig+cQQXEU2QvP6PGB7v=RGQDiu_s0kctFyqSHWvLe2ep0w9Q@mail.gmail.com","threadId":"55844","inReplyTo":"gohp6kh7i0xm08.fsf@cpm12071.fritz.box","subject":"Re: [PATCH v4 3/3] submodule--helper: introduce add-config subcommand","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-06-14T20:12:46Z","receivedAt":"2021-06-14T20:13:12Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Jun 14, 2021 at 3:53 PM Rafael Silva\n<rafaeloliveira.cs@gmail.com> wrote:\n> Just as an example, here's a diff to demonstrate the argument:\n> -- >8 --\n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> @@ -2934,6 +2934,14 @@ static int add_clone(int argc, const char **argv, const char *prefix)\n> +void add_config_in_submodules_file(const char *keyfmt, const char *submodule,\n> +                         const char *value)\n> +{\n> +       char *key = xstrfmt(keyfmt, submodule);\n> +       config_set_in_gitmodules_file_gently(key, value);\n> +       free(key);\n> +}\n\nThe new function should be `static`, of course.\n"},{"id":"427408","messageId":"xmqqzgvrwzsv.fsf@gitster.g","threadId":"55844","inReplyTo":"20210614125157.99426-2-raykar.ath@gmail.com","subject":"Re: [PATCH v4 1/3] submodule--helper: refactor module_clone()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-06-15T03:51:28Z","receivedAt":"2021-06-15T03:51:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Atharva Raykar <raykar.ath@gmail.com> writes:\n\n> Separate out the core logic of module_clone() from the flag\n> parsing---this way we can call the equivalent of the `submodule--helper\n> clone` subcommand directly within C, without needing to push arguments\n> in a strvec.\n> ---\n\nMissing sign-off?\n\n>  builtin/submodule--helper.c | 241 +++++++++++++++++++-----------------\n>  1 file changed, 128 insertions(+), 113 deletions(-)\n>\n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> index d55f6262e9..321b623d81 100644\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -1658,45 +1658,20 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n>  \treturn 0;\n>  }\n>  \n\n\n> +struct module_clone_data {\n> +\tconst char* prefix;\n\nAsterisk sticks to identifier not type in this codebase, i.e.\n\n\tconst char *prefix;\n\nOther than that, the split makes sense, especially for the purpose\nof more easily reusing this helper without having to unparse and\nreparse the option strings.\n\n"},{"id":"427419","messageId":"E2277B26-8179-46B0-A390-AD3457A034C5@gmail.com","threadId":"55844","inReplyTo":"gohp6kh7i0xm08.fsf@cpm12071.fritz.box","subject":"Re: [PATCH v4 3/3] submodule--helper: introduce add-config subcommand","fromName":"Atharva Raykar","fromEmail":"raykar.ath@gmail.com","sentAt":"2021-06-15T07:09:30Z","receivedAt":"2021-06-15T07:09:39Z","isPatch":true,"sender":{"key":"raykar.ath@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24277692?v=4"},"body":"On 15-Jun-2021, at 01:21, Rafael Silva <rafaeloliveira.cs@gmail.com> wrote:\n> \n> \n> Atharva Raykar <raykar.ath@gmail.com> writes:\n> \n>> ---\n>> builtin/submodule--helper.c | 119 ++++++++++++++++++++++++++++++++++++\n>> git-submodule.sh            |  28 +--------\n>> 2 files changed, 120 insertions(+), 27 deletions(-)\n>> \n> \n> I do not have enough expertise to judge the entire content of this\n> patch. I would like, however, to propose a slight code change for the\n> sake of readability.\n> \n>> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n>> index 6dffaeb6cb..c4b2aa6537 100644\n>> --- a/builtin/submodule--helper.c\n>> +++ b/builtin/submodule--helper.c\n>> @@ -2935,6 +2935,124 @@ static int add_clone(int argc, const char **argv, const char *prefix)\n>> \treturn 0;\n>> }\n>> \n>> +static void configure_added_submodule(struct add_data *add_data)\n>> +{\n>> +\tchar *key, *submod_pathspec = NULL;\n>> +\tstruct child_process add_submod = CHILD_PROCESS_INIT;\n>> +\tstruct child_process add_gitmodules = CHILD_PROCESS_INIT;\n>> +\tint pathspec_key_exists, activate = 0;\n>> +\n>> +\tkey = xstrfmt(\"submodule.%s.url\", add_data->sm_name);\n>> +\tgit_config_set_gently(key, add_data->realrepo);\n>> +\tfree(key);\n>> +\n>> +\tadd_submod.git_cmd = 1;\n>> +\tstrvec_pushl(&add_submod.args, \"add\",\n>> +\t\t     \"--no-warn-embedded-repo\", NULL);\n>> +\tif (add_data->force)\n>> +\t\tstrvec_push(&add_submod.args, \"--force\");\n>> +\tstrvec_pushl(&add_submod.args, \"--\", add_data->sm_path, NULL);\n>> +\n>> +\tif (run_command(&add_submod))\n>> +\t\tdie(_(\"Failed to add submodule '%s'\"), add_data->sm_path);\n>> +\n>> +\tkey = xstrfmt(\"submodule.%s.path\", add_data->sm_name);\n>> +\tconfig_set_in_gitmodules_file_gently(key, add_data->sm_path);\n>> +\tfree(key);\n> \n> This above three lines of code is very similar to the two operations that\n> follows (including the one inside the `if (add_data->branch)`\n> condition. So [ ... ]\n> \n>> +\tkey = xstrfmt(\"submodule.%s.url\", add_data->sm_name);\n>> +\tconfig_set_in_gitmodules_file_gently(key, add_data->repo);\n>> +\tfree(key);\n>> +\tif (add_data->branch) {\n>> +\t\tkey = xstrfmt(\"submodule.%s.branch\", add_data->sm_path);\n>> +\t\tconfig_set_in_gitmodules_file_gently(key, add_data->branch);\n>> +\t\tfree(key);\n>> +\t}\n>> +\n> \n> [ ... ] it might be worth to write a small wrapper that will perform: (1)\n> `xstrfmt()` on the specified config section, (2) set the configuration\n> in the file and (3) free()'ing the variable inside the wrapper. Thus,\n> most of these code will become one liners that is easier to read (given\n> the function is properly named :) ).\n> \n> After abstracting the code on the wrapper, this code will become\n> something like:\n> \n> \n>    function_properly_named(\"submodule.%s.path\", add_data->sm_name, add_data->sm_path);\n>    function_properly_named(\"submodule.%s.url\", add_data->sm_name, add_data->repo);\n>    if (add_data->branch)\n>         function_properly_named(\"submodule.%s.branch\", add_data->sm_path, add_data->branch);\n> \n> \n> Just as an example, here's a diff to demonstrate the argument:\n> \n> -- >8 --\n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> index 65f79fbd53..48ea909f51 100644\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -2934,6 +2934,14 @@ static int add_clone(int argc, const char **argv, const char *prefix)\n> \treturn 0;\n> }\n> \n> +void add_config_in_submodules_file(const char *keyfmt, const char *submodule,\n> +\t\t\t  const char *value)\n> +{\n> +\tchar *key = xstrfmt(keyfmt, submodule);\n> +\tconfig_set_in_gitmodules_file_gently(key, value);\n> +\tfree(key);\n> +}\n> +\n> static void configure_added_submodule(struct add_data *add_data)\n> {\n> \tchar *key, *submod_pathspec = NULL;\n> @@ -2955,17 +2963,10 @@ static void configure_added_submodule(struct add_data *add_data)\n> \tif (run_command(&add_submod))\n> \t\tdie(_(\"Failed to add submodule '%s'\"), add_data->sm_path);\n> \n> -\tkey = xstrfmt(\"submodule.%s.path\", add_data->sm_name);\n> -\tconfig_set_in_gitmodules_file_gently(key, add_data->sm_path);\n> -\tfree(key);\n> -\tkey = xstrfmt(\"submodule.%s.url\", add_data->sm_name);\n> -\tconfig_set_in_gitmodules_file_gently(key, add_data->repo);\n> -\tfree(key);\n> -\tif (add_data->branch) {\n> -\t\tkey = xstrfmt(\"submodule.%s.branch\", add_data->sm_path);\n> -\t\tconfig_set_in_gitmodules_file_gently(key, add_data->branch);\n> -\t\tfree(key);\n> -\t}\n> +\tadd_config_in_submodules_file(\"submodule.%s.path\", add_data->sm_name, add_data->sm_path);\n> +\tadd_config_in_submodules_file(\"submodule.%s.url\", add_data->sm_name, add_data->repo);\n> +\tif (add_data->branch)\n> +\t\tadd_config_in_submodules_file(\"submodule.%s.branch\", add_data->sm_path, add_data->branch);\n> \n> \tadd_gitmodules.git_cmd = 1;\n> \tstrvec_pushl(&add_gitmodules.args,\n> \n> -- >8 --\n> \n> A proper name than \"add_config_in_submodules_file\" should be considered - I'm\n> not very good in naming things.\n> \n> These change does (should) not change the behavior of code, even though\n> I believe it make the code simpler to read, I do not have strong\n> opinions about it. So, take this proposal as you wish. \n\nI agree with you, this will make the code simpler to read. It also\nmade me realise one thing that I did not replicate exactly from the\nshell code.\n\nThe original shell code calls 'module_config()', which does an extra\ncheck to see if writing to '.gitmodules' is okay. I did not perform\nthis check, and including that in the wrapper you propose will be a\ngood idea.\n\n> -- \n> Thanks\n> Rafael\n\n"},{"id":"427425","messageId":"393DB02F-5E7F-4558-9FB6-A1753F22A8E2@gmail.com","threadId":"55844","inReplyTo":"xmqqzgvrwzsv.fsf@gitster.g","subject":"Re: [PATCH v4 1/3] submodule--helper: refactor module_clone()","fromName":"Atharva Raykar","fromEmail":"raykar.ath@gmail.com","sentAt":"2021-06-15T09:03:46Z","receivedAt":"2021-06-15T09:03:52Z","isPatch":true,"sender":{"key":"raykar.ath@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24277692?v=4"},"body":"On 15-Jun-2021, at 09:21, Junio C Hamano <gitster@pobox.com> wrote:\n> \n> Atharva Raykar <raykar.ath@gmail.com> writes:\n> \n>> Separate out the core logic of module_clone() from the flag\n>> parsing---this way we can call the equivalent of the `submodule--helper\n>> clone` subcommand directly within C, without needing to push arguments\n>> in a strvec.\n>> ---\n> \n> Missing sign-off?\n\nYes, I missed out all the trailers.\n\n>> builtin/submodule--helper.c | 241 +++++++++++++++++++-----------------\n>> 1 file changed, 128 insertions(+), 113 deletions(-)\n>> \n>> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n>> index d55f6262e9..321b623d81 100644\n>> --- a/builtin/submodule--helper.c\n>> +++ b/builtin/submodule--helper.c\n>> @@ -1658,45 +1658,20 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n>> \treturn 0;\n>> }\n>> \n> \n> \n>> +struct module_clone_data {\n>> +\tconst char* prefix;\n> \n> Asterisk sticks to identifier not type in this codebase, i.e.\n> \n> \tconst char *prefix;\n> \n\nWill fix and resend.\n\n> Other than that, the split makes sense, especially for the purpose\n> of more easily reusing this helper without having to unparse and\n> reparse the option strings.\n> \n\n"},{"id":"427431","messageId":"gohp6k35tjqxix.fsf@gmail.com","threadId":"55844","inReplyTo":"CAPig+cQQXEU2QvP6PGB7v=RGQDiu_s0kctFyqSHWvLe2ep0w9Q@mail.gmail.com","subject":"Re: [PATCH v4 3/3] submodule--helper: introduce add-config subcommand","fromName":"Rafael Silva","fromEmail":"rafaeloliveira.cs@gmail.com","sentAt":"2021-06-15T09:37:11Z","receivedAt":"2021-06-15T09:38:04Z","isPatch":true,"sender":{"key":"rafaeloliveira.cs@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5935135?v=4"},"body":"\nEric Sunshine <sunshine@sunshineco.com> writes:\n\n> On Mon, Jun 14, 2021 at 3:53 PM Rafael Silva\n> <rafaeloliveira.cs@gmail.com> wrote:\n>> Just as an example, here's a diff to demonstrate the argument:\n>> -- >8 --\n>> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n>> @@ -2934,6 +2934,14 @@ static int add_clone(int argc, const char **argv, const char *prefix)\n>> +void add_config_in_submodules_file(const char *keyfmt, const char *submodule,\n>> +                         const char *value)\n>> +{\n>> +       char *key = xstrfmt(keyfmt, submodule);\n>> +       config_set_in_gitmodules_file_gently(key, value);\n>> +       free(key);\n>> +}\n>\n> The new function should be `static`, of course.\n\nGood catch! Indeed, it should be `static`. \n\n-- \nThanks\nRafael\n"},{"id":"427432","messageId":"20210615093810.27930-1-raykar.ath@gmail.com","threadId":"55844","inReplyTo":"20210614125157.99426-1-raykar.ath@gmail.com","subject":"[GSoC] [PATCH v5 0/3] submodule--helper: introduce subcommands for sh to C conversion","fromName":"Atharva Raykar","fromEmail":"raykar.ath@gmail.com","sentAt":"2021-06-15T09:38:07Z","receivedAt":"2021-06-15T09:38:41Z","isPatch":true,"sender":{"key":"raykar.ath@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24277692?v=4"},"body":"Only two changes since v4:\n - Add missing trailers and s.o.b in [1/3]\n - In [3/3] introduce a wrapper function called 'config_submodule_in_gitmodules'\n   that sets the 'submodule.<name>.<var>' configuration, and before doing so,\n   checks if it is okay to write to '.gitmodules', which was the original\n   behaviour of the shell version.\n\nAtharva Raykar (3):\n  submodule--helper: refactor module_clone()\n  submodule--helper: introduce add-clone subcommand\n  submodule--helper: introduce add-config subcommand\n\n builtin/submodule--helper.c | 542 ++++++++++++++++++++++++++++--------\n git-submodule.sh            |  66 +----\n 2 files changed, 431 insertions(+), 177 deletions(-)\n\n-- \n2.31.1\n\n"},{"id":"427433","messageId":"20210615093810.27930-2-raykar.ath@gmail.com","threadId":"55844","inReplyTo":"20210615093810.27930-1-raykar.ath@gmail.com","subject":"[PATCH v5 1/3] submodule--helper: refactor module_clone()","fromName":"Atharva Raykar","fromEmail":"raykar.ath@gmail.com","sentAt":"2021-06-15T09:38:08Z","receivedAt":"2021-06-15T09:38:47Z","isPatch":true,"sender":{"key":"raykar.ath@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24277692?v=4"},"body":"Separate out the core logic of module_clone() from the flag\nparsing---this way we can call the equivalent of the `submodule--helper\nclone` subcommand directly within C, without needing to push arguments\nin a strvec.\n\nSigned-off-by: Atharva Raykar <raykar.ath@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Shourya Shukla <shouryashukla.oo@gmail.com>\nSuggested-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin/submodule--helper.c | 241 +++++++++++++++++++-----------------\n 1 file changed, 128 insertions(+), 113 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex d55f6262e9..ae246a35f9 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1658,45 +1658,20 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n \treturn 0;\n }\n \n-static int clone_submodule(const char *path, const char *gitdir, const char *url,\n-\t\t\t   const char *depth, struct string_list *reference, int dissociate,\n-\t\t\t   int quiet, int progress, int single_branch)\n-{\n-\tstruct child_process cp = CHILD_PROCESS_INIT;\n-\n-\tstrvec_push(&cp.args, \"clone\");\n-\tstrvec_push(&cp.args, \"--no-checkout\");\n-\tif (quiet)\n-\t\tstrvec_push(&cp.args, \"--quiet\");\n-\tif (progress)\n-\t\tstrvec_push(&cp.args, \"--progress\");\n-\tif (depth && *depth)\n-\t\tstrvec_pushl(&cp.args, \"--depth\", depth, NULL);\n-\tif (reference->nr) {\n-\t\tstruct string_list_item *item;\n-\t\tfor_each_string_list_item(item, reference)\n-\t\t\tstrvec_pushl(&cp.args, \"--reference\",\n-\t\t\t\t     item->string, NULL);\n-\t}\n-\tif (dissociate)\n-\t\tstrvec_push(&cp.args, \"--dissociate\");\n-\tif (gitdir && *gitdir)\n-\t\tstrvec_pushl(&cp.args, \"--separate-git-dir\", gitdir, NULL);\n-\tif (single_branch >= 0)\n-\t\tstrvec_push(&cp.args, single_branch ?\n-\t\t\t\t\t  \"--single-branch\" :\n-\t\t\t\t\t  \"--no-single-branch\");\n-\n-\tstrvec_push(&cp.args, \"--\");\n-\tstrvec_push(&cp.args, url);\n-\tstrvec_push(&cp.args, path);\n-\n-\tcp.git_cmd = 1;\n-\tprepare_submodule_repo_env(&cp.env_array);\n-\tcp.no_stdin = 1;\n-\n-\treturn run_command(&cp);\n-}\n+struct module_clone_data {\n+\tconst char *prefix;\n+\tconst char *path;\n+\tconst char *name;\n+\tconst char *url;\n+\tconst char *depth;\n+\tstruct string_list reference;\n+\tunsigned int quiet: 1;\n+\tunsigned int progress: 1;\n+\tunsigned int dissociate: 1;\n+\tunsigned int require_init: 1;\n+\tint single_branch;\n+};\n+#define MODULE_CLONE_DATA_INIT { .reference = STRING_LIST_INIT_NODUP, .single_branch = -1 }\n \n struct submodule_alternate_setup {\n \tconst char *submodule_name;\n@@ -1802,37 +1777,128 @@ static void prepare_possible_alternates(const char *sm_name,\n \tfree(error_strategy);\n }\n \n-static int module_clone(int argc, const char **argv, const char *prefix)\n+static int clone_submodule(struct module_clone_data *clone_data)\n {\n-\tconst char *name = NULL, *url = NULL, *depth = NULL;\n-\tint quiet = 0;\n-\tint progress = 0;\n-\tchar *p, *path = NULL, *sm_gitdir;\n-\tstruct strbuf sb = STRBUF_INIT;\n-\tstruct string_list reference = STRING_LIST_INIT_NODUP;\n-\tint dissociate = 0, require_init = 0;\n+\tchar *p, *sm_gitdir;\n \tchar *sm_alternate = NULL, *error_strategy = NULL;\n-\tint single_branch = -1;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\n+\tstrbuf_addf(&sb, \"%s/modules/%s\", get_git_dir(), clone_data->name);\n+\tsm_gitdir = absolute_pathdup(sb.buf);\n+\tstrbuf_reset(&sb);\n+\n+\tif (!is_absolute_path(clone_data->path)) {\n+\t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), clone_data->path);\n+\t\tclone_data->path = strbuf_detach(&sb, NULL);\n+\t} else {\n+\t\tclone_data->path = xstrdup(clone_data->path);\n+\t}\n+\n+\tif (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)\n+\t\tdie(_(\"refusing to create/use '%s' in another submodule's \"\n+\t\t      \"git dir\"), sm_gitdir);\n+\n+\tif (!file_exists(sm_gitdir)) {\n+\t\tif (safe_create_leading_directories_const(sm_gitdir) < 0)\n+\t\t\tdie(_(\"could not create directory '%s'\"), sm_gitdir);\n+\n+\t\tprepare_possible_alternates(clone_data->name, &clone_data->reference);\n+\n+\t\tstrvec_push(&cp.args, \"clone\");\n+\t\tstrvec_push(&cp.args, \"--no-checkout\");\n+\t\tif (clone_data->quiet)\n+\t\t\tstrvec_push(&cp.args, \"--quiet\");\n+\t\tif (clone_data->progress)\n+\t\t\tstrvec_push(&cp.args, \"--progress\");\n+\t\tif (clone_data->depth && *(clone_data->depth))\n+\t\t\tstrvec_pushl(&cp.args, \"--depth\", clone_data->depth, NULL);\n+\t\tif (clone_data->reference.nr) {\n+\t\t\tstruct string_list_item *item;\n+\t\t\tfor_each_string_list_item(item, &clone_data->reference)\n+\t\t\t\tstrvec_pushl(&cp.args, \"--reference\",\n+\t\t\t\t\t     item->string, NULL);\n+\t\t}\n+\t\tif (clone_data->dissociate)\n+\t\t\tstrvec_push(&cp.args, \"--dissociate\");\n+\t\tif (sm_gitdir && *sm_gitdir)\n+\t\t\tstrvec_pushl(&cp.args, \"--separate-git-dir\", sm_gitdir, NULL);\n+\t\tif (clone_data->single_branch >= 0)\n+\t\t\tstrvec_push(&cp.args, clone_data->single_branch ?\n+\t\t\t\t    \"--single-branch\" :\n+\t\t\t\t    \"--no-single-branch\");\n+\n+\t\tstrvec_push(&cp.args, \"--\");\n+\t\tstrvec_push(&cp.args, clone_data->url);\n+\t\tstrvec_push(&cp.args, clone_data->path);\n+\n+\t\tcp.git_cmd = 1;\n+\t\tprepare_submodule_repo_env(&cp.env_array);\n+\t\tcp.no_stdin = 1;\n+\n+\t\tif(run_command(&cp))\n+\t\t\tdie(_(\"clone of '%s' into submodule path '%s' failed\"),\n+\t\t\t    clone_data->url, clone_data->path);\n+\t} else {\n+\t\tif (clone_data->require_init && !access(clone_data->path, X_OK) &&\n+\t\t    !is_empty_dir(clone_data->path))\n+\t\t\tdie(_(\"directory not empty: '%s'\"), clone_data->path);\n+\t\tif (safe_create_leading_directories_const(clone_data->path) < 0)\n+\t\t\tdie(_(\"could not create directory '%s'\"), clone_data->path);\n+\t\tstrbuf_addf(&sb, \"%s/index\", sm_gitdir);\n+\t\tunlink_or_warn(sb.buf);\n+\t\tstrbuf_reset(&sb);\n+\t}\n+\n+\tconnect_work_tree_and_git_dir(clone_data->path, sm_gitdir, 0);\n+\n+\tp = git_pathdup_submodule(clone_data->path, \"config\");\n+\tif (!p)\n+\t\tdie(_(\"could not get submodule directory for '%s'\"), clone_data->path);\n+\n+\t/* setup alternateLocation and alternateErrorStrategy in the cloned submodule if needed */\n+\tgit_config_get_string(\"submodule.alternateLocation\", &sm_alternate);\n+\tif (sm_alternate)\n+\t\tgit_config_set_in_file(p, \"submodule.alternateLocation\",\n+\t\t\t\t       sm_alternate);\n+\tgit_config_get_string(\"submodule.alternateErrorStrategy\", &error_strategy);\n+\tif (error_strategy)\n+\t\tgit_config_set_in_file(p, \"submodule.alternateErrorStrategy\",\n+\t\t\t\t       error_strategy);\n+\n+\tfree(sm_alternate);\n+\tfree(error_strategy);\n+\n+\tstrbuf_release(&sb);\n+\tfree(sm_gitdir);\n+\tfree(p);\n+\treturn 0;\n+}\n+\n+static int module_clone(int argc, const char **argv, const char *prefix)\n+{\n+\tint dissociate = 0, quiet = 0, progress = 0, require_init = 0;\n+\tstruct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;\n \n \tstruct option module_clone_options[] = {\n-\t\tOPT_STRING(0, \"prefix\", &prefix,\n+\t\tOPT_STRING(0, \"prefix\", &clone_data.prefix,\n \t\t\t   N_(\"path\"),\n \t\t\t   N_(\"alternative anchor for relative paths\")),\n-\t\tOPT_STRING(0, \"path\", &path,\n+\t\tOPT_STRING(0, \"path\", &clone_data.path,\n \t\t\t   N_(\"path\"),\n \t\t\t   N_(\"where the new submodule will be cloned to\")),\n-\t\tOPT_STRING(0, \"name\", &name,\n+\t\tOPT_STRING(0, \"name\", &clone_data.name,\n \t\t\t   N_(\"string\"),\n \t\t\t   N_(\"name of the new submodule\")),\n-\t\tOPT_STRING(0, \"url\", &url,\n+\t\tOPT_STRING(0, \"url\", &clone_data.url,\n \t\t\t   N_(\"string\"),\n \t\t\t   N_(\"url where to clone the submodule from\")),\n-\t\tOPT_STRING_LIST(0, \"reference\", &reference,\n+\t\tOPT_STRING_LIST(0, \"reference\", &clone_data.reference,\n \t\t\t   N_(\"repo\"),\n \t\t\t   N_(\"reference repository\")),\n \t\tOPT_BOOL(0, \"dissociate\", &dissociate,\n \t\t\t   N_(\"use --reference only while cloning\")),\n-\t\tOPT_STRING(0, \"depth\", &depth,\n+\t\tOPT_STRING(0, \"depth\", &clone_data.depth,\n \t\t\t   N_(\"string\"),\n \t\t\t   N_(\"depth for shallow clones\")),\n \t\tOPT__QUIET(&quiet, \"Suppress output for cloning a submodule\"),\n@@ -1840,7 +1906,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)\n \t\t\t   N_(\"force cloning progress\")),\n \t\tOPT_BOOL(0, \"require-init\", &require_init,\n \t\t\t   N_(\"disallow cloning into non-empty directory\")),\n-\t\tOPT_BOOL(0, \"single-branch\", &single_branch,\n+\t\tOPT_BOOL(0, \"single-branch\", &clone_data.single_branch,\n \t\t\t N_(\"clone only one branch, HEAD or --branch\")),\n \t\tOPT_END()\n \t};\n@@ -1856,67 +1922,16 @@ static int module_clone(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, module_clone_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n-\tif (argc || !url || !path || !*path)\n+\tclone_data.dissociate = !!dissociate;\n+\tclone_data.quiet = !!quiet;\n+\tclone_data.progress = !!progress;\n+\tclone_data.require_init = !!require_init;\n+\n+\tif (argc || !clone_data.url || !clone_data.path || !*(clone_data.path))\n \t\tusage_with_options(git_submodule_helper_usage,\n \t\t\t\t   module_clone_options);\n \n-\tstrbuf_addf(&sb, \"%s/modules/%s\", get_git_dir(), name);\n-\tsm_gitdir = absolute_pathdup(sb.buf);\n-\tstrbuf_reset(&sb);\n-\n-\tif (!is_absolute_path(path)) {\n-\t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), path);\n-\t\tpath = strbuf_detach(&sb, NULL);\n-\t} else\n-\t\tpath = xstrdup(path);\n-\n-\tif (validate_submodule_git_dir(sm_gitdir, name) < 0)\n-\t\tdie(_(\"refusing to create/use '%s' in another submodule's \"\n-\t\t\t\"git dir\"), sm_gitdir);\n-\n-\tif (!file_exists(sm_gitdir)) {\n-\t\tif (safe_create_leading_directories_const(sm_gitdir) < 0)\n-\t\t\tdie(_(\"could not create directory '%s'\"), sm_gitdir);\n-\n-\t\tprepare_possible_alternates(name, &reference);\n-\n-\t\tif (clone_submodule(path, sm_gitdir, url, depth, &reference, dissociate,\n-\t\t\t\t    quiet, progress, single_branch))\n-\t\t\tdie(_(\"clone of '%s' into submodule path '%s' failed\"),\n-\t\t\t    url, path);\n-\t} else {\n-\t\tif (require_init && !access(path, X_OK) && !is_empty_dir(path))\n-\t\t\tdie(_(\"directory not empty: '%s'\"), path);\n-\t\tif (safe_create_leading_directories_const(path) < 0)\n-\t\t\tdie(_(\"could not create directory '%s'\"), path);\n-\t\tstrbuf_addf(&sb, \"%s/index\", sm_gitdir);\n-\t\tunlink_or_warn(sb.buf);\n-\t\tstrbuf_reset(&sb);\n-\t}\n-\n-\tconnect_work_tree_and_git_dir(path, sm_gitdir, 0);\n-\n-\tp = git_pathdup_submodule(path, \"config\");\n-\tif (!p)\n-\t\tdie(_(\"could not get submodule directory for '%s'\"), path);\n-\n-\t/* setup alternateLocation and alternateErrorStrategy in the cloned submodule if needed */\n-\tgit_config_get_string(\"submodule.alternateLocation\", &sm_alternate);\n-\tif (sm_alternate)\n-\t\tgit_config_set_in_file(p, \"submodule.alternateLocation\",\n-\t\t\t\t\t   sm_alternate);\n-\tgit_config_get_string(\"submodule.alternateErrorStrategy\", &error_strategy);\n-\tif (error_strategy)\n-\t\tgit_config_set_in_file(p, \"submodule.alternateErrorStrategy\",\n-\t\t\t\t\t   error_strategy);\n-\n-\tfree(sm_alternate);\n-\tfree(error_strategy);\n-\n-\tstrbuf_release(&sb);\n-\tfree(sm_gitdir);\n-\tfree(path);\n-\tfree(p);\n+\tclone_submodule(&clone_data);\n \treturn 0;\n }\n \n-- \n2.31.1\n\n"},{"id":"427434","messageId":"20210615093810.27930-3-raykar.ath@gmail.com","threadId":"55844","inReplyTo":"20210615093810.27930-1-raykar.ath@gmail.com","subject":"[PATCH v5 2/3] submodule--helper: introduce add-clone subcommand","fromName":"Atharva Raykar","fromEmail":"raykar.ath@gmail.com","sentAt":"2021-06-15T09:38:09Z","receivedAt":"2021-06-15T09:38:55Z","isPatch":true,"sender":{"key":"raykar.ath@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24277692?v=4"},"body":"Let's add a new \"add-clone\" subcommand to `git submodule--helper` with\nthe goal of converting part of the shell code in git-submodule.sh\nrelated to `git submodule add` into C code. This new subcommand clones\nthe repository that is to be added, and checks out to the appropriate\nbranch.\n\nThis is meant to be a faithful conversion that leaves the behaviour of\n'submodule add' unchanged. The only minor change is that if a submodule name has\nbeen supplied with a name that clashes with a local submodule, the message shown\nto the user (\"A git directory for 'foo' is found locally...\") is prepended with\n\"error\" for clarity.\n\nThis is part of a series of changes that will result in all of 'submodule add'\nbeing converted to C.\n\nSigned-off-by: Atharva Raykar <raykar.ath@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Shourya Shukla <shouryashukla.oo@gmail.com>\nBased-on-patch-by: Shourya Shukla <shouryashukla.oo@gmail.com>\nBased-on-patch-by: Prathamesh Chavan <pc44800@gmail.com>\nHelped-by: Đoàn Trần Công Danh <congdanhqx@gmail.com>\n---\n builtin/submodule--helper.c | 176 ++++++++++++++++++++++++++++++++++++\n git-submodule.sh            |  38 +-------\n 2 files changed, 177 insertions(+), 37 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex ae246a35f9..82c3a9aaa5 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2760,6 +2760,181 @@ static int module_set_branch(int argc, const char **argv, const char *prefix)\n \treturn !!ret;\n }\n \n+struct add_data {\n+\tconst char *prefix;\n+\tconst char *branch;\n+\tconst char *reference_path;\n+\tconst char *sm_path;\n+\tconst char *sm_name;\n+\tconst char *repo;\n+\tconst char *realrepo;\n+\tint depth;\n+\tunsigned int force: 1;\n+\tunsigned int quiet: 1;\n+\tunsigned int progress: 1;\n+\tunsigned int dissociate: 1;\n+};\n+#define ADD_DATA_INIT { .depth = -1 }\n+\n+static void show_fetch_remotes(FILE *output, const char *sm_name, const char *git_dir_path)\n+{\n+\tstruct child_process cp_remote = CHILD_PROCESS_INIT;\n+\tstruct strbuf sb_remote_out = STRBUF_INIT;\n+\n+\tcp_remote.git_cmd = 1;\n+\tstrvec_pushf(&cp_remote.env_array,\n+\t\t     \"GIT_DIR=%s\", git_dir_path);\n+\tstrvec_push(&cp_remote.env_array, \"GIT_WORK_TREE=.\");\n+\tstrvec_pushl(&cp_remote.args, \"remote\", \"-v\", NULL);\n+\tif (!capture_command(&cp_remote, &sb_remote_out, 0)) {\n+\t\tchar *next_line;\n+\t\tchar *line = sb_remote_out.buf;\n+\t\twhile ((next_line = strchr(line, '\\n')) != NULL) {\n+\t\t\tsize_t len = next_line - line;\n+\t\t\tif (strip_suffix_mem(line, &len, \" (fetch)\"))\n+\t\t\t\tfprintf(output, \"  %.*s\\n\", (int)len, line);\n+\t\t\tline = next_line + 1;\n+\t\t}\n+\t}\n+\n+\tstrbuf_release(&sb_remote_out);\n+}\n+\n+static int add_submodule(const struct add_data *add_data)\n+{\n+\tchar *submod_gitdir_path;\n+\tstruct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;\n+\n+\t/* perhaps the path already exists and is already a git repo, else clone it */\n+\tif (is_directory(add_data->sm_path)) {\n+\t\tstruct strbuf sm_path = STRBUF_INIT;\n+\t\tstrbuf_addstr(&sm_path, add_data->sm_path);\n+\t\tsubmod_gitdir_path = xstrfmt(\"%s/.git\", add_data->sm_path);\n+\t\tif (is_nonbare_repository_dir(&sm_path))\n+\t\t\tprintf(_(\"Adding existing repo at '%s' to the index\\n\"),\n+\t\t\t       add_data->sm_path);\n+\t\telse\n+\t\t\tdie(_(\"'%s' already exists and is not a valid git repo\"),\n+\t\t\t    add_data->sm_path);\n+\t\tstrbuf_release(&sm_path);\n+\t\tfree(submod_gitdir_path);\n+\t} else {\n+\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\t\tsubmod_gitdir_path = xstrfmt(\".git/modules/%s\", add_data->sm_name);\n+\n+\t\tif (is_directory(submod_gitdir_path)) {\n+\t\t\tif (!add_data->force) {\n+\t\t\t\tfprintf(stderr, _(\"A git directory for '%s' is found \"\n+\t\t\t\t\t\t  \"locally with remote(s):\"),\n+\t\t\t\t\tadd_data->sm_name);\n+\t\t\t\tshow_fetch_remotes(stderr, add_data->sm_name,\n+\t\t\t\t\t\t   submod_gitdir_path);\n+\t\t\t\tfree(submod_gitdir_path);\n+\t\t\t\tdie(_(\"If you want to reuse this local git \"\n+\t\t\t\t      \"directory instead of cloning again from\\n\"\n+\t\t\t\t      \"  %s\\n\"\n+\t\t\t\t      \"use the '--force' option. If the local git \"\n+\t\t\t\t      \"directory is not the correct repo\\n\"\n+\t\t\t\t      \"or if you are unsure what this means, choose \"\n+\t\t\t\t      \"another name with the '--name' option.\\n\"),\n+\t\t\t\t    add_data->realrepo);\n+\t\t\t} else {\n+\t\t\t\tprintf(_(\"Reactivating local git directory for \"\n+\t\t\t\t\t \"submodule '%s'\\n\"), add_data->sm_name);\n+\t\t\t}\n+\t\t}\n+\t\tfree(submod_gitdir_path);\n+\n+\t\tclone_data.prefix = add_data->prefix;\n+\t\tclone_data.path = add_data->sm_path;\n+\t\tclone_data.name = add_data->sm_name;\n+\t\tclone_data.url = add_data->realrepo;\n+\t\tclone_data.quiet = add_data->quiet;\n+\t\tclone_data.progress = add_data->progress;\n+\t\tif (add_data->reference_path)\n+\t\t\tstring_list_append(&clone_data.reference,\n+\t\t\t\t\t   xstrdup(add_data->reference_path));\n+\t\tclone_data.dissociate = add_data->dissociate;\n+\t\tif (add_data->depth >= 0)\n+\t\t\tclone_data.depth = xstrfmt(\"%d\", add_data->depth);\n+\n+\t\tif (clone_submodule(&clone_data))\n+\t\t\treturn -1;\n+\n+\t\tprepare_submodule_repo_env(&cp.env_array);\n+\t\tcp.git_cmd = 1;\n+\t\tcp.dir = add_data->sm_path;\n+\t\tstrvec_pushl(&cp.args, \"checkout\", \"-f\", \"-q\", NULL);\n+\n+\t\tif (add_data->branch) {\n+\t\t\tstrvec_pushl(&cp.args, \"-B\", add_data->branch, NULL);\n+\t\t\tstrvec_pushf(&cp.args, \"origin/%s\", add_data->branch);\n+\t\t}\n+\n+\t\tif (run_command(&cp))\n+\t\t\tdie(_(\"unable to checkout submodule '%s'\"), add_data->sm_path);\n+\t}\n+\treturn 0;\n+}\n+\n+static int add_clone(int argc, const char **argv, const char *prefix)\n+{\n+\tint force = 0, quiet = 0, dissociate = 0, progress = 0;\n+\tstruct add_data add_data = ADD_DATA_INIT;\n+\n+\tstruct option options[] = {\n+\t\tOPT_STRING('b', \"branch\", &add_data.branch,\n+\t\t\t   N_(\"branch\"),\n+\t\t\t   N_(\"branch of repository to checkout on cloning\")),\n+\t\tOPT_STRING(0, \"prefix\", &add_data.prefix,\n+\t\t\t   N_(\"path\"),\n+\t\t\t   N_(\"alternative anchor for relative paths\")),\n+\t\tOPT_STRING(0, \"path\", &add_data.sm_path,\n+\t\t\t   N_(\"path\"),\n+\t\t\t   N_(\"where the new submodule will be cloned to\")),\n+\t\tOPT_STRING(0, \"name\", &add_data.sm_name,\n+\t\t\t   N_(\"string\"),\n+\t\t\t   N_(\"name of the new submodule\")),\n+\t\tOPT_STRING(0, \"url\", &add_data.realrepo,\n+\t\t\t   N_(\"string\"),\n+\t\t\t   N_(\"url where to clone the submodule from\")),\n+\t\tOPT_STRING(0, \"reference\", &add_data.reference_path,\n+\t\t\t   N_(\"repo\"),\n+\t\t\t   N_(\"reference repository\")),\n+\t\tOPT_BOOL(0, \"dissociate\", &dissociate,\n+\t\t\t N_(\"use --reference only while cloning\")),\n+\t\tOPT_INTEGER(0, \"depth\", &add_data.depth,\n+\t\t\t    N_(\"depth for shallow clones\")),\n+\t\tOPT_BOOL(0, \"progress\", &progress,\n+\t\t\t N_(\"force cloning progress\")),\n+\t\tOPT__FORCE(&force, N_(\"allow adding an otherwise ignored submodule path\"),\n+\t\t\t   PARSE_OPT_NOCOMPLETE),\n+\t\tOPT__QUIET(&quiet, \"suppress output for cloning a submodule\"),\n+\t\tOPT_END()\n+\t};\n+\n+\tconst char *const usage[] = {\n+\t\tN_(\"git submodule--helper add-clone [<options>...] \"\n+\t\t   \"--url <url> --path <path> --name <name>\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, usage, 0);\n+\n+\tif (argc != 0)\n+\t\tusage_with_options(usage, options);\n+\n+\tadd_data.progress = !!progress;\n+\tadd_data.dissociate = !!dissociate;\n+\tadd_data.force = !!force;\n+\tadd_data.quiet = !!quiet;\n+\n+\tif (add_submodule(&add_data))\n+\t\treturn 1;\n+\n+\treturn 0;\n+}\n+\n #define SUPPORT_SUPER_PREFIX (1<<0)\n \n struct cmd_struct {\n@@ -2772,6 +2947,7 @@ static struct cmd_struct commands[] = {\n \t{\"list\", module_list, 0},\n \t{\"name\", module_name, 0},\n \t{\"clone\", module_clone, 0},\n+\t{\"add-clone\", add_clone, 0},\n \t{\"update-module-mode\", module_update_module_mode, 0},\n \t{\"update-clone\", update_clone, 0},\n \t{\"ensure-core-worktree\", ensure_core_worktree, 0},\ndiff --git a/git-submodule.sh b/git-submodule.sh\nindex 4678378424..f71e1e5495 100755\n--- a/git-submodule.sh\n+++ b/git-submodule.sh\n@@ -241,43 +241,7 @@ cmd_add()\n \t\tdie \"$(eval_gettext \"'$sm_name' is not a valid submodule name\")\"\n \tfi\n \n-\t# perhaps the path exists and is already a git repo, else clone it\n-\tif test -e \"$sm_path\"\n-\tthen\n-\t\tif test -d \"$sm_path\"/.git || test -f \"$sm_path\"/.git\n-\t\tthen\n-\t\t\teval_gettextln \"Adding existing repo at '\\$sm_path' to the index\"\n-\t\telse\n-\t\t\tdie \"$(eval_gettext \"'\\$sm_path' already exists and is not a valid git repo\")\"\n-\t\tfi\n-\n-\telse\n-\t\tif test -d \".git/modules/$sm_name\"\n-\t\tthen\n-\t\t\tif test -z \"$force\"\n-\t\t\tthen\n-\t\t\t\teval_gettextln >&2 \"A git directory for '\\$sm_name' is found locally with remote(s):\"\n-\t\t\t\tGIT_DIR=\".git/modules/$sm_name\" GIT_WORK_TREE=. git remote -v | grep '(fetch)' | sed -e s,^,\"  \", -e s,' (fetch)',, >&2\n-\t\t\t\tdie \"$(eval_gettextln \"\\\n-If you want to reuse this local git directory instead of cloning again from\n-  \\$realrepo\n-use the '--force' option. If the local git directory is not the correct repo\n-or you are unsure what this means choose another name with the '--name' option.\")\"\n-\t\t\telse\n-\t\t\t\teval_gettextln \"Reactivating local git directory for submodule '\\$sm_name'.\"\n-\t\t\tfi\n-\t\tfi\n-\t\tgit 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\n-\t\t(\n-\t\t\tsanitize_submodule_env\n-\t\t\tcd \"$sm_path\" &&\n-\t\t\t# ash fails to wordsplit ${branch:+-b \"$branch\"...}\n-\t\t\tcase \"$branch\" in\n-\t\t\t'') git checkout -f -q ;;\n-\t\t\t?*) git checkout -f -q -B \"$branch\" \"origin/$branch\" ;;\n-\t\t\tesac\n-\t\t) || die \"$(eval_gettext \"Unable to checkout submodule '\\$sm_path'\")\"\n-\tfi\n+\tgit 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\n \tgit config submodule.\"$sm_name\".url \"$realrepo\"\n \n \tgit add --no-warn-embedded-repo $force \"$sm_path\" ||\n-- \n2.31.1\n\n"},{"id":"427435","messageId":"20210615093810.27930-4-raykar.ath@gmail.com","threadId":"55844","inReplyTo":"20210615093810.27930-1-raykar.ath@gmail.com","subject":"[PATCH v5 3/3] submodule--helper: introduce add-config subcommand","fromName":"Atharva Raykar","fromEmail":"raykar.ath@gmail.com","sentAt":"2021-06-15T09:38:10Z","receivedAt":"2021-06-15T09:39:03Z","isPatch":true,"sender":{"key":"raykar.ath@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24277692?v=4"},"body":"Add a new \"add-config\" subcommand to `git submodule--helper` with the\ngoal of converting part of the shell code in git-submodule.sh related to\n`git submodule add` into C code. This new subcommand sets the\nconfiguration variables of a newly added submodule, by registering the\nurl in local git config, as well as the submodule name and path in the\n.gitmodules file. It also sets 'submodule.<name>.active' to \"true\" if\nthe submodule path has not already been covered by any pathspec\nspecified in 'submodule.active'.\n\nThis is meant to be a faithful conversion from shell to C, with only one\nminor change: A warning is emitted if no value is specified in\n'submodule.active', ie, the config looks like: \"[submodule] active\\n\",\nbecause it is an invalid configuration. It would be helpful to let the\nuser know that the pathspec is unset, and the value of\n'submodule.<name>.active' might be set to 'true' so that they can\nrectify their configuration and prevent future surprises (especially\ngiven that the latter variable has a higher priority than the former).\n\nThe structure of the conditional to check if we need to set the 'active'\ntoggle looks different from the shell version -- but behaves the same.\nThe change was made to decrease code duplication. A comment has been\nadded to explain that only one value of 'submodule.active' is obtained\nto check if we need to call is_submodule_active() at all.\n\nThis is part of a series of changes that will result in all of\n'submodule add' being converted to C.\n\nSigned-off-by: Atharva Raykar <raykar.ath@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Shourya Shukla <shouryashukla.oo@gmail.com>\nBased-on-patch-by: Shourya Shukla <shouryashukla.oo@gmail.com>\nBased-on-patch-by: Prathamesh Chavan <pc44800@gmail.com>\n---\n builtin/submodule--helper.c | 125 ++++++++++++++++++++++++++++++++++++\n git-submodule.sh            |  28 +-------\n 2 files changed, 126 insertions(+), 27 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 82c3a9aaa5..e6a75f9f7e 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2935,6 +2935,130 @@ static int add_clone(int argc, const char **argv, const char *prefix)\n \treturn 0;\n }\n \n+static void config_submodule_in_gitmodules(const char *name, const char *var, const char *value)\n+{\n+\tchar *key;\n+\n+\tif (!is_writing_gitmodules_ok())\n+\t\tdie(_(\"please make sure that the .gitmodules file is in the working tree\"));\n+\n+\tkey = xstrfmt(\"submodule.%s.%s\", name, var);\n+\tconfig_set_in_gitmodules_file_gently(key, value);\n+\tfree(key);\n+}\n+\n+static void configure_added_submodule(struct add_data *add_data)\n+{\n+\tchar *key, *submod_pathspec = NULL;\n+\tstruct child_process add_submod = CHILD_PROCESS_INIT;\n+\tstruct child_process add_gitmodules = CHILD_PROCESS_INIT;\n+\tint pathspec_key_exists, activate = 0;\n+\n+\tkey = xstrfmt(\"submodule.%s.url\", add_data->sm_name);\n+\tgit_config_set_gently(key, add_data->realrepo);\n+\tfree(key);\n+\n+\tadd_submod.git_cmd = 1;\n+\tstrvec_pushl(&add_submod.args, \"add\",\n+\t\t     \"--no-warn-embedded-repo\", NULL);\n+\tif (add_data->force)\n+\t\tstrvec_push(&add_submod.args, \"--force\");\n+\tstrvec_pushl(&add_submod.args, \"--\", add_data->sm_path, NULL);\n+\n+\tif (run_command(&add_submod))\n+\t\tdie(_(\"Failed to add submodule '%s'\"), add_data->sm_path);\n+\n+\tconfig_submodule_in_gitmodules(add_data->sm_name, \"path\", add_data->sm_path);\n+\tconfig_submodule_in_gitmodules(add_data->sm_name, \"url\", add_data->repo);\n+\tif (add_data->branch)\n+\t\tconfig_submodule_in_gitmodules(add_data->sm_name,\n+\t\t\t\t\t       \"branch\", add_data->branch);\n+\n+\tadd_gitmodules.git_cmd = 1;\n+\tstrvec_pushl(&add_gitmodules.args,\n+\t\t     \"add\", \"--force\", \"--\", \".gitmodules\", NULL);\n+\n+\tif (run_command(&add_gitmodules))\n+\t\tdie(_(\"Failed to register submodule '%s'\"), add_data->sm_path);\n+\n+\t/*\n+\t * NEEDSWORK: In a multi-working-tree world this needs to be\n+\t * set in the per-worktree config.\n+\t */\n+\tpathspec_key_exists = !git_config_get_string(\"submodule.active\",\n+\t\t\t\t\t\t     &submod_pathspec);\n+\tif (pathspec_key_exists && !submod_pathspec) {\n+\t\twarning(_(\"The submodule.active configuration exists, but the \"\n+\t\t\t  \"pathspec was unset. If the submodule is not already \"\n+\t\t\t  \"active, the value of submodule.%s.active will be \"\n+\t\t\t  \"be set to 'true'.\"), add_data->sm_name);\n+\t\tactivate = 1;\n+\t}\n+\n+\t/*\n+\t * If submodule.active does not exist, or if the pathspec was unset,\n+\t * we will activate this module unconditionally.\n+\t *\n+\t * Otherwise, we ask is_submodule_active(), which iterates\n+\t * through all the values of 'submodule.active' to determine\n+\t * if this module is already active.\n+\t */\n+\tif (!pathspec_key_exists || activate ||\n+\t    !is_submodule_active(the_repository, add_data->sm_path)) {\n+\t\tkey = xstrfmt(\"submodule.%s.active\", add_data->sm_name);\n+\t\tgit_config_set_gently(key, \"true\");\n+\t\tfree(key);\n+\t}\n+}\n+\n+static int add_config(int argc, const char **argv, const char *prefix)\n+{\n+\tint force = 0;\n+\tstruct add_data add_data = ADD_DATA_INIT;\n+\n+\tstruct option options[] = {\n+\t\tOPT_STRING('b', \"branch\", &add_data.branch,\n+\t\t\t   N_(\"branch\"),\n+\t\t\t   N_(\"branch of repository to store in \"\n+\t\t\t      \"the submodule configuration\")),\n+\t\tOPT_STRING(0, \"url\", &add_data.repo,\n+\t\t\t   N_(\"string\"),\n+\t\t\t   N_(\"url to clone submodule from\")),\n+\t\tOPT_STRING(0, \"resolved-url\", &add_data.realrepo,\n+\t\t\t   N_(\"string\"),\n+\t\t\t   N_(\"url to clone the submodule from, after it has \"\n+\t\t\t      \"been dereferenced relative to parent's url, \"\n+\t\t\t      \"in the case where <url> is a relative url\")),\n+\t\tOPT_STRING(0, \"path\", &add_data.sm_path,\n+\t\t\t   N_(\"path\"),\n+\t\t\t   N_(\"where the new submodule will be cloned to\")),\n+\t\tOPT_STRING(0, \"name\", &add_data.sm_name,\n+\t\t\t   N_(\"string\"),\n+\t\t\t   N_(\"name of the new submodule\")),\n+\t\tOPT__FORCE(&force, N_(\"allow adding an otherwise ignored submodule path\"),\n+\t\t\t   PARSE_OPT_NOCOMPLETE),\n+\t\tOPT_END()\n+\t};\n+\n+\tconst char *const usage[] = {\n+\t\tN_(\"git submodule--helper add-config \"\n+\t\t   \"[--force|-f] [--branch|-b <branch>] \"\n+\t\t   \"--url <url> --resolved-url <resolved-url> \"\n+\t\t   \"--path <path> --name <name>\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, usage, 0);\n+\n+\tif (argc != 0)\n+\t\tusage_with_options(usage, options);\n+\n+\tadd_data.force = !!force;\n+\tconfigure_added_submodule(&add_data);\n+\n+\treturn 0;\n+}\n+\n #define SUPPORT_SUPER_PREFIX (1<<0)\n \n struct cmd_struct {\n@@ -2948,6 +3072,7 @@ static struct cmd_struct commands[] = {\n \t{\"name\", module_name, 0},\n \t{\"clone\", module_clone, 0},\n \t{\"add-clone\", add_clone, 0},\n+\t{\"add-config\", add_config, 0},\n \t{\"update-module-mode\", module_update_module_mode, 0},\n \t{\"update-clone\", update_clone, 0},\n \t{\"ensure-core-worktree\", ensure_core_worktree, 0},\ndiff --git a/git-submodule.sh b/git-submodule.sh\nindex f71e1e5495..9826378fa6 100755\n--- a/git-submodule.sh\n+++ b/git-submodule.sh\n@@ -242,33 +242,7 @@ cmd_add()\n \tfi\n \n \tgit 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\n-\tgit config submodule.\"$sm_name\".url \"$realrepo\"\n-\n-\tgit add --no-warn-embedded-repo $force \"$sm_path\" ||\n-\tdie \"$(eval_gettext \"Failed to add submodule '\\$sm_path'\")\"\n-\n-\tgit submodule--helper config submodule.\"$sm_name\".path \"$sm_path\" &&\n-\tgit submodule--helper config submodule.\"$sm_name\".url \"$repo\" &&\n-\tif test -n \"$branch\"\n-\tthen\n-\t\tgit submodule--helper config submodule.\"$sm_name\".branch \"$branch\"\n-\tfi &&\n-\tgit add --force .gitmodules ||\n-\tdie \"$(eval_gettext \"Failed to register submodule '\\$sm_path'\")\"\n-\n-\t# NEEDSWORK: In a multi-working-tree world, this needs to be\n-\t# set in the per-worktree config.\n-\tif git config --get submodule.active >/dev/null\n-\tthen\n-\t\t# If the submodule being adding isn't already covered by the\n-\t\t# current configured pathspec, set the submodule's active flag\n-\t\tif ! git submodule--helper is-active \"$sm_path\"\n-\t\tthen\n-\t\t\tgit config submodule.\"$sm_name\".active \"true\"\n-\t\tfi\n-\telse\n-\t\tgit config submodule.\"$sm_name\".active \"true\"\n-\tfi\n+\tgit submodule--helper add-config ${force:+--force} ${branch:+--branch \"$branch\"} --url \"$repo\" --resolved-url \"$realrepo\" --path \"$sm_path\" --name \"$sm_name\"\n }\n \n #\n-- \n2.31.1\n\n"},{"id":"427482","messageId":"20210615145745.33382-1-raykar.ath@gmail.com","threadId":"55844","inReplyTo":"20210615093810.27930-1-raykar.ath@gmail.com","subject":"[GSoC] [PATCH v6 0/3] submodule--helper: introduce subcommands for sh to C conversion","fromName":"Atharva Raykar","fromEmail":"raykar.ath@gmail.com","sentAt":"2021-06-15T14:57:42Z","receivedAt":"2021-06-15T14:58:04Z","isPatch":true,"sender":{"key":"raykar.ath@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24277692?v=4"},"body":"In [2/3]:\nI noticed that my previous code fails to parse prefixes properly when it has not\nbeen explicitly stated by the subcommand caller.\n\nAtharva Raykar (3):\n  submodule--helper: refactor module_clone()\n  submodule--helper: introduce add-clone subcommand\n  submodule--helper: introduce add-config subcommand\n\n builtin/submodule--helper.c | 543 ++++++++++++++++++++++++++++--------\n git-submodule.sh            |  66 +----\n 2 files changed, 432 insertions(+), 177 deletions(-)\n\nRange-diff against v5:\n1:  068568f997 = 1:  068568f997 submodule--helper: refactor module_clone()\n1:  10734cf91a ! 2:  06b2075580 submodule--helper: introduce add-clone subcommand\n    @@ builtin/submodule--helper.c: static int module_set_branch(int argc, const char *\n     +\t\tOPT_STRING('b', \"branch\", &add_data.branch,\n     +\t\t\t   N_(\"branch\"),\n     +\t\t\t   N_(\"branch of repository to checkout on cloning\")),\n    -+\t\tOPT_STRING(0, \"prefix\", &add_data.prefix,\n    ++\t\tOPT_STRING(0, \"prefix\", &prefix,\n     +\t\t\t   N_(\"path\"),\n     +\t\t\t   N_(\"alternative anchor for relative paths\")),\n     +\t\tOPT_STRING(0, \"path\", &add_data.sm_path,\n    @@ builtin/submodule--helper.c: static int module_set_branch(int argc, const char *\n     +\tif (argc != 0)\n     +\t\tusage_with_options(usage, options);\n     +\n    ++\tadd_data.prefix = prefix;\n     +\tadd_data.progress = !!progress;\n     +\tadd_data.dissociate = !!dissociate;\n     +\tadd_data.force = !!force;\n2:  383f0b6217 = 3:  3e827a3858 submodule--helper: introduce add-config subcommand\n\n--\n2.31.1\n\n"},{"id":"427483","messageId":"20210615145745.33382-2-raykar.ath@gmail.com","threadId":"55844","inReplyTo":"20210615145745.33382-1-raykar.ath@gmail.com","subject":"[PATCH v6 1/3] submodule--helper: refactor module_clone()","fromName":"Atharva Raykar","fromEmail":"raykar.ath@gmail.com","sentAt":"2021-06-15T14:57:43Z","receivedAt":"2021-06-15T14:58:10Z","isPatch":true,"sender":{"key":"raykar.ath@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24277692?v=4"},"body":"Separate out the core logic of module_clone() from the flag\nparsing---this way we can call the equivalent of the `submodule--helper\nclone` subcommand directly within C, without needing to push arguments\nin a strvec.\n\nSigned-off-by: Atharva Raykar <raykar.ath@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Shourya Shukla <shouryashukla.oo@gmail.com>\nSuggested-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin/submodule--helper.c | 241 +++++++++++++++++++-----------------\n 1 file changed, 128 insertions(+), 113 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex d55f6262e9..ae246a35f9 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1658,45 +1658,20 @@ static int module_deinit(int argc, const char **argv, const char *prefix)\n \treturn 0;\n }\n \n-static int clone_submodule(const char *path, const char *gitdir, const char *url,\n-\t\t\t   const char *depth, struct string_list *reference, int dissociate,\n-\t\t\t   int quiet, int progress, int single_branch)\n-{\n-\tstruct child_process cp = CHILD_PROCESS_INIT;\n-\n-\tstrvec_push(&cp.args, \"clone\");\n-\tstrvec_push(&cp.args, \"--no-checkout\");\n-\tif (quiet)\n-\t\tstrvec_push(&cp.args, \"--quiet\");\n-\tif (progress)\n-\t\tstrvec_push(&cp.args, \"--progress\");\n-\tif (depth && *depth)\n-\t\tstrvec_pushl(&cp.args, \"--depth\", depth, NULL);\n-\tif (reference->nr) {\n-\t\tstruct string_list_item *item;\n-\t\tfor_each_string_list_item(item, reference)\n-\t\t\tstrvec_pushl(&cp.args, \"--reference\",\n-\t\t\t\t     item->string, NULL);\n-\t}\n-\tif (dissociate)\n-\t\tstrvec_push(&cp.args, \"--dissociate\");\n-\tif (gitdir && *gitdir)\n-\t\tstrvec_pushl(&cp.args, \"--separate-git-dir\", gitdir, NULL);\n-\tif (single_branch >= 0)\n-\t\tstrvec_push(&cp.args, single_branch ?\n-\t\t\t\t\t  \"--single-branch\" :\n-\t\t\t\t\t  \"--no-single-branch\");\n-\n-\tstrvec_push(&cp.args, \"--\");\n-\tstrvec_push(&cp.args, url);\n-\tstrvec_push(&cp.args, path);\n-\n-\tcp.git_cmd = 1;\n-\tprepare_submodule_repo_env(&cp.env_array);\n-\tcp.no_stdin = 1;\n-\n-\treturn run_command(&cp);\n-}\n+struct module_clone_data {\n+\tconst char *prefix;\n+\tconst char *path;\n+\tconst char *name;\n+\tconst char *url;\n+\tconst char *depth;\n+\tstruct string_list reference;\n+\tunsigned int quiet: 1;\n+\tunsigned int progress: 1;\n+\tunsigned int dissociate: 1;\n+\tunsigned int require_init: 1;\n+\tint single_branch;\n+};\n+#define MODULE_CLONE_DATA_INIT { .reference = STRING_LIST_INIT_NODUP, .single_branch = -1 }\n \n struct submodule_alternate_setup {\n \tconst char *submodule_name;\n@@ -1802,37 +1777,128 @@ static void prepare_possible_alternates(const char *sm_name,\n \tfree(error_strategy);\n }\n \n+static int clone_submodule(struct module_clone_data *clone_data)\n+{\n+\tchar *p, *sm_gitdir;\n+\tchar *sm_alternate = NULL, *error_strategy = NULL;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\n+\tstrbuf_addf(&sb, \"%s/modules/%s\", get_git_dir(), clone_data->name);\n+\tsm_gitdir = absolute_pathdup(sb.buf);\n+\tstrbuf_reset(&sb);\n+\n+\tif (!is_absolute_path(clone_data->path)) {\n+\t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), clone_data->path);\n+\t\tclone_data->path = strbuf_detach(&sb, NULL);\n+\t} else {\n+\t\tclone_data->path = xstrdup(clone_data->path);\n+\t}\n+\n+\tif (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)\n+\t\tdie(_(\"refusing to create/use '%s' in another submodule's \"\n+\t\t      \"git dir\"), sm_gitdir);\n+\n+\tif (!file_exists(sm_gitdir)) {\n+\t\tif (safe_create_leading_directories_const(sm_gitdir) < 0)\n+\t\t\tdie(_(\"could not create directory '%s'\"), sm_gitdir);\n+\n+\t\tprepare_possible_alternates(clone_data->name, &clone_data->reference);\n+\n+\t\tstrvec_push(&cp.args, \"clone\");\n+\t\tstrvec_push(&cp.args, \"--no-checkout\");\n+\t\tif (clone_data->quiet)\n+\t\t\tstrvec_push(&cp.args, \"--quiet\");\n+\t\tif (clone_data->progress)\n+\t\t\tstrvec_push(&cp.args, \"--progress\");\n+\t\tif (clone_data->depth && *(clone_data->depth))\n+\t\t\tstrvec_pushl(&cp.args, \"--depth\", clone_data->depth, NULL);\n+\t\tif (clone_data->reference.nr) {\n+\t\t\tstruct string_list_item *item;\n+\t\t\tfor_each_string_list_item(item, &clone_data->reference)\n+\t\t\t\tstrvec_pushl(&cp.args, \"--reference\",\n+\t\t\t\t\t     item->string, NULL);\n+\t\t}\n+\t\tif (clone_data->dissociate)\n+\t\t\tstrvec_push(&cp.args, \"--dissociate\");\n+\t\tif (sm_gitdir && *sm_gitdir)\n+\t\t\tstrvec_pushl(&cp.args, \"--separate-git-dir\", sm_gitdir, NULL);\n+\t\tif (clone_data->single_branch >= 0)\n+\t\t\tstrvec_push(&cp.args, clone_data->single_branch ?\n+\t\t\t\t    \"--single-branch\" :\n+\t\t\t\t    \"--no-single-branch\");\n+\n+\t\tstrvec_push(&cp.args, \"--\");\n+\t\tstrvec_push(&cp.args, clone_data->url);\n+\t\tstrvec_push(&cp.args, clone_data->path);\n+\n+\t\tcp.git_cmd = 1;\n+\t\tprepare_submodule_repo_env(&cp.env_array);\n+\t\tcp.no_stdin = 1;\n+\n+\t\tif(run_command(&cp))\n+\t\t\tdie(_(\"clone of '%s' into submodule path '%s' failed\"),\n+\t\t\t    clone_data->url, clone_data->path);\n+\t} else {\n+\t\tif (clone_data->require_init && !access(clone_data->path, X_OK) &&\n+\t\t    !is_empty_dir(clone_data->path))\n+\t\t\tdie(_(\"directory not empty: '%s'\"), clone_data->path);\n+\t\tif (safe_create_leading_directories_const(clone_data->path) < 0)\n+\t\t\tdie(_(\"could not create directory '%s'\"), clone_data->path);\n+\t\tstrbuf_addf(&sb, \"%s/index\", sm_gitdir);\n+\t\tunlink_or_warn(sb.buf);\n+\t\tstrbuf_reset(&sb);\n+\t}\n+\n+\tconnect_work_tree_and_git_dir(clone_data->path, sm_gitdir, 0);\n+\n+\tp = git_pathdup_submodule(clone_data->path, \"config\");\n+\tif (!p)\n+\t\tdie(_(\"could not get submodule directory for '%s'\"), clone_data->path);\n+\n+\t/* setup alternateLocation and alternateErrorStrategy in the cloned submodule if needed */\n+\tgit_config_get_string(\"submodule.alternateLocation\", &sm_alternate);\n+\tif (sm_alternate)\n+\t\tgit_config_set_in_file(p, \"submodule.alternateLocation\",\n+\t\t\t\t       sm_alternate);\n+\tgit_config_get_string(\"submodule.alternateErrorStrategy\", &error_strategy);\n+\tif (error_strategy)\n+\t\tgit_config_set_in_file(p, \"submodule.alternateErrorStrategy\",\n+\t\t\t\t       error_strategy);\n+\n+\tfree(sm_alternate);\n+\tfree(error_strategy);\n+\n+\tstrbuf_release(&sb);\n+\tfree(sm_gitdir);\n+\tfree(p);\n+\treturn 0;\n+}\n+\n static int module_clone(int argc, const char **argv, const char *prefix)\n {\n-\tconst char *name = NULL, *url = NULL, *depth = NULL;\n-\tint quiet = 0;\n-\tint progress = 0;\n-\tchar *p, *path = NULL, *sm_gitdir;\n-\tstruct strbuf sb = STRBUF_INIT;\n-\tstruct string_list reference = STRING_LIST_INIT_NODUP;\n-\tint dissociate = 0, require_init = 0;\n-\tchar *sm_alternate = NULL, *error_strategy = NULL;\n-\tint single_branch = -1;\n+\tint dissociate = 0, quiet = 0, progress = 0, require_init = 0;\n+\tstruct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;\n \n \tstruct option module_clone_options[] = {\n-\t\tOPT_STRING(0, \"prefix\", &prefix,\n+\t\tOPT_STRING(0, \"prefix\", &clone_data.prefix,\n \t\t\t   N_(\"path\"),\n \t\t\t   N_(\"alternative anchor for relative paths\")),\n-\t\tOPT_STRING(0, \"path\", &path,\n+\t\tOPT_STRING(0, \"path\", &clone_data.path,\n \t\t\t   N_(\"path\"),\n \t\t\t   N_(\"where the new submodule will be cloned to\")),\n-\t\tOPT_STRING(0, \"name\", &name,\n+\t\tOPT_STRING(0, \"name\", &clone_data.name,\n \t\t\t   N_(\"string\"),\n \t\t\t   N_(\"name of the new submodule\")),\n-\t\tOPT_STRING(0, \"url\", &url,\n+\t\tOPT_STRING(0, \"url\", &clone_data.url,\n \t\t\t   N_(\"string\"),\n \t\t\t   N_(\"url where to clone the submodule from\")),\n-\t\tOPT_STRING_LIST(0, \"reference\", &reference,\n+\t\tOPT_STRING_LIST(0, \"reference\", &clone_data.reference,\n \t\t\t   N_(\"repo\"),\n \t\t\t   N_(\"reference repository\")),\n \t\tOPT_BOOL(0, \"dissociate\", &dissociate,\n \t\t\t   N_(\"use --reference only while cloning\")),\n-\t\tOPT_STRING(0, \"depth\", &depth,\n+\t\tOPT_STRING(0, \"depth\", &clone_data.depth,\n \t\t\t   N_(\"string\"),\n \t\t\t   N_(\"depth for shallow clones\")),\n \t\tOPT__QUIET(&quiet, \"Suppress output for cloning a submodule\"),\n@@ -1840,7 +1906,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)\n \t\t\t   N_(\"force cloning progress\")),\n \t\tOPT_BOOL(0, \"require-init\", &require_init,\n \t\t\t   N_(\"disallow cloning into non-empty directory\")),\n-\t\tOPT_BOOL(0, \"single-branch\", &single_branch,\n+\t\tOPT_BOOL(0, \"single-branch\", &clone_data.single_branch,\n \t\t\t N_(\"clone only one branch, HEAD or --branch\")),\n \t\tOPT_END()\n \t};\n@@ -1856,67 +1922,16 @@ static int module_clone(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, module_clone_options,\n \t\t\t     git_submodule_helper_usage, 0);\n \n-\tif (argc || !url || !path || !*path)\n+\tclone_data.dissociate = !!dissociate;\n+\tclone_data.quiet = !!quiet;\n+\tclone_data.progress = !!progress;\n+\tclone_data.require_init = !!require_init;\n+\n+\tif (argc || !clone_data.url || !clone_data.path || !*(clone_data.path))\n \t\tusage_with_options(git_submodule_helper_usage,\n \t\t\t\t   module_clone_options);\n \n-\tstrbuf_addf(&sb, \"%s/modules/%s\", get_git_dir(), name);\n-\tsm_gitdir = absolute_pathdup(sb.buf);\n-\tstrbuf_reset(&sb);\n-\n-\tif (!is_absolute_path(path)) {\n-\t\tstrbuf_addf(&sb, \"%s/%s\", get_git_work_tree(), path);\n-\t\tpath = strbuf_detach(&sb, NULL);\n-\t} else\n-\t\tpath = xstrdup(path);\n-\n-\tif (validate_submodule_git_dir(sm_gitdir, name) < 0)\n-\t\tdie(_(\"refusing to create/use '%s' in another submodule's \"\n-\t\t\t\"git dir\"), sm_gitdir);\n-\n-\tif (!file_exists(sm_gitdir)) {\n-\t\tif (safe_create_leading_directories_const(sm_gitdir) < 0)\n-\t\t\tdie(_(\"could not create directory '%s'\"), sm_gitdir);\n-\n-\t\tprepare_possible_alternates(name, &reference);\n-\n-\t\tif (clone_submodule(path, sm_gitdir, url, depth, &reference, dissociate,\n-\t\t\t\t    quiet, progress, single_branch))\n-\t\t\tdie(_(\"clone of '%s' into submodule path '%s' failed\"),\n-\t\t\t    url, path);\n-\t} else {\n-\t\tif (require_init && !access(path, X_OK) && !is_empty_dir(path))\n-\t\t\tdie(_(\"directory not empty: '%s'\"), path);\n-\t\tif (safe_create_leading_directories_const(path) < 0)\n-\t\t\tdie(_(\"could not create directory '%s'\"), path);\n-\t\tstrbuf_addf(&sb, \"%s/index\", sm_gitdir);\n-\t\tunlink_or_warn(sb.buf);\n-\t\tstrbuf_reset(&sb);\n-\t}\n-\n-\tconnect_work_tree_and_git_dir(path, sm_gitdir, 0);\n-\n-\tp = git_pathdup_submodule(path, \"config\");\n-\tif (!p)\n-\t\tdie(_(\"could not get submodule directory for '%s'\"), path);\n-\n-\t/* setup alternateLocation and alternateErrorStrategy in the cloned submodule if needed */\n-\tgit_config_get_string(\"submodule.alternateLocation\", &sm_alternate);\n-\tif (sm_alternate)\n-\t\tgit_config_set_in_file(p, \"submodule.alternateLocation\",\n-\t\t\t\t\t   sm_alternate);\n-\tgit_config_get_string(\"submodule.alternateErrorStrategy\", &error_strategy);\n-\tif (error_strategy)\n-\t\tgit_config_set_in_file(p, \"submodule.alternateErrorStrategy\",\n-\t\t\t\t\t   error_strategy);\n-\n-\tfree(sm_alternate);\n-\tfree(error_strategy);\n-\n-\tstrbuf_release(&sb);\n-\tfree(sm_gitdir);\n-\tfree(path);\n-\tfree(p);\n+\tclone_submodule(&clone_data);\n \treturn 0;\n }\n \n-- \n2.31.1\n\n"},{"id":"427484","messageId":"20210615145745.33382-3-raykar.ath@gmail.com","threadId":"55844","inReplyTo":"20210615145745.33382-1-raykar.ath@gmail.com","subject":"[PATCH v6 2/3] submodule--helper: introduce add-clone subcommand","fromName":"Atharva Raykar","fromEmail":"raykar.ath@gmail.com","sentAt":"2021-06-15T14:57:44Z","receivedAt":"2021-06-15T14:58:19Z","isPatch":true,"sender":{"key":"raykar.ath@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24277692?v=4"},"body":"Let's add a new \"add-clone\" subcommand to `git submodule--helper` with\nthe goal of converting part of the shell code in git-submodule.sh\nrelated to `git submodule add` into C code. This new subcommand clones\nthe repository that is to be added, and checks out to the appropriate\nbranch.\n\nThis is meant to be a faithful conversion that leaves the behaviour of\n'submodule add' unchanged. The only minor change is that if a submodule name has\nbeen supplied with a name that clashes with a local submodule, the message shown\nto the user (\"A git directory for 'foo' is found locally...\") is prepended with\n\"error\" for clarity.\n\nThis is part of a series of changes that will result in all of 'submodule add'\nbeing converted to C.\n\nSigned-off-by: Atharva Raykar <raykar.ath@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Shourya Shukla <shouryashukla.oo@gmail.com>\nBased-on-patch-by: Shourya Shukla <shouryashukla.oo@gmail.com>\nBased-on-patch-by: Prathamesh Chavan <pc44800@gmail.com>\nHelped-by: Đoàn Trần Công Danh <congdanhqx@gmail.com>\n---\n builtin/submodule--helper.c | 177 ++++++++++++++++++++++++++++++++++++\n git-submodule.sh            |  38 +-------\n 2 files changed, 178 insertions(+), 37 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex ae246a35f9..6d52a73a57 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2760,6 +2760,182 @@ static int module_set_branch(int argc, const char **argv, const char *prefix)\n \treturn !!ret;\n }\n \n+struct add_data {\n+\tconst char *prefix;\n+\tconst char *branch;\n+\tconst char *reference_path;\n+\tconst char *sm_path;\n+\tconst char *sm_name;\n+\tconst char *repo;\n+\tconst char *realrepo;\n+\tint depth;\n+\tunsigned int force: 1;\n+\tunsigned int quiet: 1;\n+\tunsigned int progress: 1;\n+\tunsigned int dissociate: 1;\n+};\n+#define ADD_DATA_INIT { .depth = -1 }\n+\n+static void show_fetch_remotes(FILE *output, const char *sm_name, const char *git_dir_path)\n+{\n+\tstruct child_process cp_remote = CHILD_PROCESS_INIT;\n+\tstruct strbuf sb_remote_out = STRBUF_INIT;\n+\n+\tcp_remote.git_cmd = 1;\n+\tstrvec_pushf(&cp_remote.env_array,\n+\t\t     \"GIT_DIR=%s\", git_dir_path);\n+\tstrvec_push(&cp_remote.env_array, \"GIT_WORK_TREE=.\");\n+\tstrvec_pushl(&cp_remote.args, \"remote\", \"-v\", NULL);\n+\tif (!capture_command(&cp_remote, &sb_remote_out, 0)) {\n+\t\tchar *next_line;\n+\t\tchar *line = sb_remote_out.buf;\n+\t\twhile ((next_line = strchr(line, '\\n')) != NULL) {\n+\t\t\tsize_t len = next_line - line;\n+\t\t\tif (strip_suffix_mem(line, &len, \" (fetch)\"))\n+\t\t\t\tfprintf(output, \"  %.*s\\n\", (int)len, line);\n+\t\t\tline = next_line + 1;\n+\t\t}\n+\t}\n+\n+\tstrbuf_release(&sb_remote_out);\n+}\n+\n+static int add_submodule(const struct add_data *add_data)\n+{\n+\tchar *submod_gitdir_path;\n+\tstruct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;\n+\n+\t/* perhaps the path already exists and is already a git repo, else clone it */\n+\tif (is_directory(add_data->sm_path)) {\n+\t\tstruct strbuf sm_path = STRBUF_INIT;\n+\t\tstrbuf_addstr(&sm_path, add_data->sm_path);\n+\t\tsubmod_gitdir_path = xstrfmt(\"%s/.git\", add_data->sm_path);\n+\t\tif (is_nonbare_repository_dir(&sm_path))\n+\t\t\tprintf(_(\"Adding existing repo at '%s' to the index\\n\"),\n+\t\t\t       add_data->sm_path);\n+\t\telse\n+\t\t\tdie(_(\"'%s' already exists and is not a valid git repo\"),\n+\t\t\t    add_data->sm_path);\n+\t\tstrbuf_release(&sm_path);\n+\t\tfree(submod_gitdir_path);\n+\t} else {\n+\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\t\tsubmod_gitdir_path = xstrfmt(\".git/modules/%s\", add_data->sm_name);\n+\n+\t\tif (is_directory(submod_gitdir_path)) {\n+\t\t\tif (!add_data->force) {\n+\t\t\t\tfprintf(stderr, _(\"A git directory for '%s' is found \"\n+\t\t\t\t\t\t  \"locally with remote(s):\"),\n+\t\t\t\t\tadd_data->sm_name);\n+\t\t\t\tshow_fetch_remotes(stderr, add_data->sm_name,\n+\t\t\t\t\t\t   submod_gitdir_path);\n+\t\t\t\tfree(submod_gitdir_path);\n+\t\t\t\tdie(_(\"If you want to reuse this local git \"\n+\t\t\t\t      \"directory instead of cloning again from\\n\"\n+\t\t\t\t      \"  %s\\n\"\n+\t\t\t\t      \"use the '--force' option. If the local git \"\n+\t\t\t\t      \"directory is not the correct repo\\n\"\n+\t\t\t\t      \"or if you are unsure what this means, choose \"\n+\t\t\t\t      \"another name with the '--name' option.\\n\"),\n+\t\t\t\t    add_data->realrepo);\n+\t\t\t} else {\n+\t\t\t\tprintf(_(\"Reactivating local git directory for \"\n+\t\t\t\t\t \"submodule '%s'\\n\"), add_data->sm_name);\n+\t\t\t}\n+\t\t}\n+\t\tfree(submod_gitdir_path);\n+\n+\t\tclone_data.prefix = add_data->prefix;\n+\t\tclone_data.path = add_data->sm_path;\n+\t\tclone_data.name = add_data->sm_name;\n+\t\tclone_data.url = add_data->realrepo;\n+\t\tclone_data.quiet = add_data->quiet;\n+\t\tclone_data.progress = add_data->progress;\n+\t\tif (add_data->reference_path)\n+\t\t\tstring_list_append(&clone_data.reference,\n+\t\t\t\t\t   xstrdup(add_data->reference_path));\n+\t\tclone_data.dissociate = add_data->dissociate;\n+\t\tif (add_data->depth >= 0)\n+\t\t\tclone_data.depth = xstrfmt(\"%d\", add_data->depth);\n+\n+\t\tif (clone_submodule(&clone_data))\n+\t\t\treturn -1;\n+\n+\t\tprepare_submodule_repo_env(&cp.env_array);\n+\t\tcp.git_cmd = 1;\n+\t\tcp.dir = add_data->sm_path;\n+\t\tstrvec_pushl(&cp.args, \"checkout\", \"-f\", \"-q\", NULL);\n+\n+\t\tif (add_data->branch) {\n+\t\t\tstrvec_pushl(&cp.args, \"-B\", add_data->branch, NULL);\n+\t\t\tstrvec_pushf(&cp.args, \"origin/%s\", add_data->branch);\n+\t\t}\n+\n+\t\tif (run_command(&cp))\n+\t\t\tdie(_(\"unable to checkout submodule '%s'\"), add_data->sm_path);\n+\t}\n+\treturn 0;\n+}\n+\n+static int add_clone(int argc, const char **argv, const char *prefix)\n+{\n+\tint force = 0, quiet = 0, dissociate = 0, progress = 0;\n+\tstruct add_data add_data = ADD_DATA_INIT;\n+\n+\tstruct option options[] = {\n+\t\tOPT_STRING('b', \"branch\", &add_data.branch,\n+\t\t\t   N_(\"branch\"),\n+\t\t\t   N_(\"branch of repository to checkout on cloning\")),\n+\t\tOPT_STRING(0, \"prefix\", &prefix,\n+\t\t\t   N_(\"path\"),\n+\t\t\t   N_(\"alternative anchor for relative paths\")),\n+\t\tOPT_STRING(0, \"path\", &add_data.sm_path,\n+\t\t\t   N_(\"path\"),\n+\t\t\t   N_(\"where the new submodule will be cloned to\")),\n+\t\tOPT_STRING(0, \"name\", &add_data.sm_name,\n+\t\t\t   N_(\"string\"),\n+\t\t\t   N_(\"name of the new submodule\")),\n+\t\tOPT_STRING(0, \"url\", &add_data.realrepo,\n+\t\t\t   N_(\"string\"),\n+\t\t\t   N_(\"url where to clone the submodule from\")),\n+\t\tOPT_STRING(0, \"reference\", &add_data.reference_path,\n+\t\t\t   N_(\"repo\"),\n+\t\t\t   N_(\"reference repository\")),\n+\t\tOPT_BOOL(0, \"dissociate\", &dissociate,\n+\t\t\t N_(\"use --reference only while cloning\")),\n+\t\tOPT_INTEGER(0, \"depth\", &add_data.depth,\n+\t\t\t    N_(\"depth for shallow clones\")),\n+\t\tOPT_BOOL(0, \"progress\", &progress,\n+\t\t\t N_(\"force cloning progress\")),\n+\t\tOPT__FORCE(&force, N_(\"allow adding an otherwise ignored submodule path\"),\n+\t\t\t   PARSE_OPT_NOCOMPLETE),\n+\t\tOPT__QUIET(&quiet, \"suppress output for cloning a submodule\"),\n+\t\tOPT_END()\n+\t};\n+\n+\tconst char *const usage[] = {\n+\t\tN_(\"git submodule--helper add-clone [<options>...] \"\n+\t\t   \"--url <url> --path <path> --name <name>\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, usage, 0);\n+\n+\tif (argc != 0)\n+\t\tusage_with_options(usage, options);\n+\n+\tadd_data.prefix = prefix;\n+\tadd_data.progress = !!progress;\n+\tadd_data.dissociate = !!dissociate;\n+\tadd_data.force = !!force;\n+\tadd_data.quiet = !!quiet;\n+\n+\tif (add_submodule(&add_data))\n+\t\treturn 1;\n+\n+\treturn 0;\n+}\n+\n #define SUPPORT_SUPER_PREFIX (1<<0)\n \n struct cmd_struct {\n@@ -2772,6 +2948,7 @@ static struct cmd_struct commands[] = {\n \t{\"list\", module_list, 0},\n \t{\"name\", module_name, 0},\n \t{\"clone\", module_clone, 0},\n+\t{\"add-clone\", add_clone, 0},\n \t{\"update-module-mode\", module_update_module_mode, 0},\n \t{\"update-clone\", update_clone, 0},\n \t{\"ensure-core-worktree\", ensure_core_worktree, 0},\ndiff --git a/git-submodule.sh b/git-submodule.sh\nindex 4678378424..f71e1e5495 100755\n--- a/git-submodule.sh\n+++ b/git-submodule.sh\n@@ -241,43 +241,7 @@ cmd_add()\n \t\tdie \"$(eval_gettext \"'$sm_name' is not a valid submodule name\")\"\n \tfi\n \n-\t# perhaps the path exists and is already a git repo, else clone it\n-\tif test -e \"$sm_path\"\n-\tthen\n-\t\tif test -d \"$sm_path\"/.git || test -f \"$sm_path\"/.git\n-\t\tthen\n-\t\t\teval_gettextln \"Adding existing repo at '\\$sm_path' to the index\"\n-\t\telse\n-\t\t\tdie \"$(eval_gettext \"'\\$sm_path' already exists and is not a valid git repo\")\"\n-\t\tfi\n-\n-\telse\n-\t\tif test -d \".git/modules/$sm_name\"\n-\t\tthen\n-\t\t\tif test -z \"$force\"\n-\t\t\tthen\n-\t\t\t\teval_gettextln >&2 \"A git directory for '\\$sm_name' is found locally with remote(s):\"\n-\t\t\t\tGIT_DIR=\".git/modules/$sm_name\" GIT_WORK_TREE=. git remote -v | grep '(fetch)' | sed -e s,^,\"  \", -e s,' (fetch)',, >&2\n-\t\t\t\tdie \"$(eval_gettextln \"\\\n-If you want to reuse this local git directory instead of cloning again from\n-  \\$realrepo\n-use the '--force' option. If the local git directory is not the correct repo\n-or you are unsure what this means choose another name with the '--name' option.\")\"\n-\t\t\telse\n-\t\t\t\teval_gettextln \"Reactivating local git directory for submodule '\\$sm_name'.\"\n-\t\t\tfi\n-\t\tfi\n-\t\tgit 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\n-\t\t(\n-\t\t\tsanitize_submodule_env\n-\t\t\tcd \"$sm_path\" &&\n-\t\t\t# ash fails to wordsplit ${branch:+-b \"$branch\"...}\n-\t\t\tcase \"$branch\" in\n-\t\t\t'') git checkout -f -q ;;\n-\t\t\t?*) git checkout -f -q -B \"$branch\" \"origin/$branch\" ;;\n-\t\t\tesac\n-\t\t) || die \"$(eval_gettext \"Unable to checkout submodule '\\$sm_path'\")\"\n-\tfi\n+\tgit 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\n \tgit config submodule.\"$sm_name\".url \"$realrepo\"\n \n \tgit add --no-warn-embedded-repo $force \"$sm_path\" ||\n-- \n2.31.1\n\n"},{"id":"427485","messageId":"20210615145745.33382-4-raykar.ath@gmail.com","threadId":"55844","inReplyTo":"20210615145745.33382-1-raykar.ath@gmail.com","subject":"[PATCH v6 3/3] submodule--helper: introduce add-config subcommand","fromName":"Atharva Raykar","fromEmail":"raykar.ath@gmail.com","sentAt":"2021-06-15T14:57:45Z","receivedAt":"2021-06-15T14:58:25Z","isPatch":true,"sender":{"key":"raykar.ath@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24277692?v=4"},"body":"Add a new \"add-config\" subcommand to `git submodule--helper` with the\ngoal of converting part of the shell code in git-submodule.sh related to\n`git submodule add` into C code. This new subcommand sets the\nconfiguration variables of a newly added submodule, by registering the\nurl in local git config, as well as the submodule name and path in the\n.gitmodules file. It also sets 'submodule.<name>.active' to \"true\" if\nthe submodule path has not already been covered by any pathspec\nspecified in 'submodule.active'.\n\nThis is meant to be a faithful conversion from shell to C, with only one\nminor change: A warning is emitted if no value is specified in\n'submodule.active', ie, the config looks like: \"[submodule] active\\n\",\nbecause it is an invalid configuration. It would be helpful to let the\nuser know that the pathspec is unset, and the value of\n'submodule.<name>.active' might be set to 'true' so that they can\nrectify their configuration and prevent future surprises (especially\ngiven that the latter variable has a higher priority than the former).\n\nThe structure of the conditional to check if we need to set the 'active'\ntoggle looks different from the shell version -- but behaves the same.\nThe change was made to decrease code duplication. A comment has been\nadded to explain that only one value of 'submodule.active' is obtained\nto check if we need to call is_submodule_active() at all.\n\nThis is part of a series of changes that will result in all of\n'submodule add' being converted to C.\n\nSigned-off-by: Atharva Raykar <raykar.ath@gmail.com>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Shourya Shukla <shouryashukla.oo@gmail.com>\nBased-on-patch-by: Shourya Shukla <shouryashukla.oo@gmail.com>\nBased-on-patch-by: Prathamesh Chavan <pc44800@gmail.com>\n---\n builtin/submodule--helper.c | 125 ++++++++++++++++++++++++++++++++++++\n git-submodule.sh            |  28 +-------\n 2 files changed, 126 insertions(+), 27 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 6d52a73a57..bce1e06d74 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2936,6 +2936,130 @@ static int add_clone(int argc, const char **argv, const char *prefix)\n \treturn 0;\n }\n \n+static void config_submodule_in_gitmodules(const char *name, const char *var, const char *value)\n+{\n+\tchar *key;\n+\n+\tif (!is_writing_gitmodules_ok())\n+\t\tdie(_(\"please make sure that the .gitmodules file is in the working tree\"));\n+\n+\tkey = xstrfmt(\"submodule.%s.%s\", name, var);\n+\tconfig_set_in_gitmodules_file_gently(key, value);\n+\tfree(key);\n+}\n+\n+static void configure_added_submodule(struct add_data *add_data)\n+{\n+\tchar *key, *submod_pathspec = NULL;\n+\tstruct child_process add_submod = CHILD_PROCESS_INIT;\n+\tstruct child_process add_gitmodules = CHILD_PROCESS_INIT;\n+\tint pathspec_key_exists, activate = 0;\n+\n+\tkey = xstrfmt(\"submodule.%s.url\", add_data->sm_name);\n+\tgit_config_set_gently(key, add_data->realrepo);\n+\tfree(key);\n+\n+\tadd_submod.git_cmd = 1;\n+\tstrvec_pushl(&add_submod.args, \"add\",\n+\t\t     \"--no-warn-embedded-repo\", NULL);\n+\tif (add_data->force)\n+\t\tstrvec_push(&add_submod.args, \"--force\");\n+\tstrvec_pushl(&add_submod.args, \"--\", add_data->sm_path, NULL);\n+\n+\tif (run_command(&add_submod))\n+\t\tdie(_(\"Failed to add submodule '%s'\"), add_data->sm_path);\n+\n+\tconfig_submodule_in_gitmodules(add_data->sm_name, \"path\", add_data->sm_path);\n+\tconfig_submodule_in_gitmodules(add_data->sm_name, \"url\", add_data->repo);\n+\tif (add_data->branch)\n+\t\tconfig_submodule_in_gitmodules(add_data->sm_name,\n+\t\t\t\t\t       \"branch\", add_data->branch);\n+\n+\tadd_gitmodules.git_cmd = 1;\n+\tstrvec_pushl(&add_gitmodules.args,\n+\t\t     \"add\", \"--force\", \"--\", \".gitmodules\", NULL);\n+\n+\tif (run_command(&add_gitmodules))\n+\t\tdie(_(\"Failed to register submodule '%s'\"), add_data->sm_path);\n+\n+\t/*\n+\t * NEEDSWORK: In a multi-working-tree world this needs to be\n+\t * set in the per-worktree config.\n+\t */\n+\tpathspec_key_exists = !git_config_get_string(\"submodule.active\",\n+\t\t\t\t\t\t     &submod_pathspec);\n+\tif (pathspec_key_exists && !submod_pathspec) {\n+\t\twarning(_(\"The submodule.active configuration exists, but the \"\n+\t\t\t  \"pathspec was unset. If the submodule is not already \"\n+\t\t\t  \"active, the value of submodule.%s.active will be \"\n+\t\t\t  \"be set to 'true'.\"), add_data->sm_name);\n+\t\tactivate = 1;\n+\t}\n+\n+\t/*\n+\t * If submodule.active does not exist, or if the pathspec was unset,\n+\t * we will activate this module unconditionally.\n+\t *\n+\t * Otherwise, we ask is_submodule_active(), which iterates\n+\t * through all the values of 'submodule.active' to determine\n+\t * if this module is already active.\n+\t */\n+\tif (!pathspec_key_exists || activate ||\n+\t    !is_submodule_active(the_repository, add_data->sm_path)) {\n+\t\tkey = xstrfmt(\"submodule.%s.active\", add_data->sm_name);\n+\t\tgit_config_set_gently(key, \"true\");\n+\t\tfree(key);\n+\t}\n+}\n+\n+static int add_config(int argc, const char **argv, const char *prefix)\n+{\n+\tint force = 0;\n+\tstruct add_data add_data = ADD_DATA_INIT;\n+\n+\tstruct option options[] = {\n+\t\tOPT_STRING('b', \"branch\", &add_data.branch,\n+\t\t\t   N_(\"branch\"),\n+\t\t\t   N_(\"branch of repository to store in \"\n+\t\t\t      \"the submodule configuration\")),\n+\t\tOPT_STRING(0, \"url\", &add_data.repo,\n+\t\t\t   N_(\"string\"),\n+\t\t\t   N_(\"url to clone submodule from\")),\n+\t\tOPT_STRING(0, \"resolved-url\", &add_data.realrepo,\n+\t\t\t   N_(\"string\"),\n+\t\t\t   N_(\"url to clone the submodule from, after it has \"\n+\t\t\t      \"been dereferenced relative to parent's url, \"\n+\t\t\t      \"in the case where <url> is a relative url\")),\n+\t\tOPT_STRING(0, \"path\", &add_data.sm_path,\n+\t\t\t   N_(\"path\"),\n+\t\t\t   N_(\"where the new submodule will be cloned to\")),\n+\t\tOPT_STRING(0, \"name\", &add_data.sm_name,\n+\t\t\t   N_(\"string\"),\n+\t\t\t   N_(\"name of the new submodule\")),\n+\t\tOPT__FORCE(&force, N_(\"allow adding an otherwise ignored submodule path\"),\n+\t\t\t   PARSE_OPT_NOCOMPLETE),\n+\t\tOPT_END()\n+\t};\n+\n+\tconst char *const usage[] = {\n+\t\tN_(\"git submodule--helper add-config \"\n+\t\t   \"[--force|-f] [--branch|-b <branch>] \"\n+\t\t   \"--url <url> --resolved-url <resolved-url> \"\n+\t\t   \"--path <path> --name <name>\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, usage, 0);\n+\n+\tif (argc != 0)\n+\t\tusage_with_options(usage, options);\n+\n+\tadd_data.force = !!force;\n+\tconfigure_added_submodule(&add_data);\n+\n+\treturn 0;\n+}\n+\n #define SUPPORT_SUPER_PREFIX (1<<0)\n \n struct cmd_struct {\n@@ -2949,6 +3073,7 @@ static struct cmd_struct commands[] = {\n \t{\"name\", module_name, 0},\n \t{\"clone\", module_clone, 0},\n \t{\"add-clone\", add_clone, 0},\n+\t{\"add-config\", add_config, 0},\n \t{\"update-module-mode\", module_update_module_mode, 0},\n \t{\"update-clone\", update_clone, 0},\n \t{\"ensure-core-worktree\", ensure_core_worktree, 0},\ndiff --git a/git-submodule.sh b/git-submodule.sh\nindex f71e1e5495..9826378fa6 100755\n--- a/git-submodule.sh\n+++ b/git-submodule.sh\n@@ -242,33 +242,7 @@ cmd_add()\n \tfi\n \n \tgit 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\n-\tgit config submodule.\"$sm_name\".url \"$realrepo\"\n-\n-\tgit add --no-warn-embedded-repo $force \"$sm_path\" ||\n-\tdie \"$(eval_gettext \"Failed to add submodule '\\$sm_path'\")\"\n-\n-\tgit submodule--helper config submodule.\"$sm_name\".path \"$sm_path\" &&\n-\tgit submodule--helper config submodule.\"$sm_name\".url \"$repo\" &&\n-\tif test -n \"$branch\"\n-\tthen\n-\t\tgit submodule--helper config submodule.\"$sm_name\".branch \"$branch\"\n-\tfi &&\n-\tgit add --force .gitmodules ||\n-\tdie \"$(eval_gettext \"Failed to register submodule '\\$sm_path'\")\"\n-\n-\t# NEEDSWORK: In a multi-working-tree world, this needs to be\n-\t# set in the per-worktree config.\n-\tif git config --get submodule.active >/dev/null\n-\tthen\n-\t\t# If the submodule being adding isn't already covered by the\n-\t\t# current configured pathspec, set the submodule's active flag\n-\t\tif ! git submodule--helper is-active \"$sm_path\"\n-\t\tthen\n-\t\t\tgit config submodule.\"$sm_name\".active \"true\"\n-\t\tfi\n-\telse\n-\t\tgit config submodule.\"$sm_name\".active \"true\"\n-\tfi\n+\tgit submodule--helper add-config ${force:+--force} ${branch:+--branch \"$branch\"} --url \"$repo\" --resolved-url \"$realrepo\" --path \"$sm_path\" --name \"$sm_name\"\n }\n \n #\n-- \n2.31.1\n\n"}]}