threads / patch / 56219

patch, 8 partssubmodule: convert the rest of 'add' to C

Subject: [GSoC] [PATCH 0/8] submodule: convert the rest of 'add' to C

## tl;dr

78 messages between Aug 5, 2021 and Sep 8, 2021. Diffs are folded; open one to read it.

replies: 77people: 6as markdown or json

Atharva Raykar· Aug 5, 2021, 07:19 UTC · lore
NOTE: This series uses the change introduced by 'ar/submodule-add-config'[1]

This series completes the conversion of all the important shell logic in 'submodule add' to C, by wrapping it in a submodule--helper builtin subcommand called 'add'.

The first 4 patches are preparatory patches. The refactors mostly involve exposing interfaces to C that were only previously usable as shell subcommands.

Then we have a patch that translates the shell code to C, faithfully reproducing the behaviour before the conversion.

The last 3 patches are cleanup patches. Our conversions have introduced a lot of dead code, all of them being 'submodule--helper' subcommands that have no further use, as we have C interfaces for these already. We remove these subcommands.

A question about the cache API used in [PATCH 5/8]:
  What is the difference between 'read_cache()' and 'read_cache_preload()'? [2]
  Which one is more appropriate for use in 'die_on_index_match()'?

Fetch-it-Via: git fetch https://github.com/tfidfwastaken/git submodule-helper-add-list-1

Footnotes =========

[1] https://lore.kernel.org/git/20210801063352.50813-1-raykar.ath@gmail.com/ [2] More about this question has been detailed in this section of my blog: http://atharvaraykar.me/gitnotes/week5#some-challenges-with-the-changes-that-are-cooking

I'll quote it here for convenience:
Show 15 quoted lines
> Before iterating through the cache entries of the index, you need to populate
> it.
>
> There’s two functions for this: read_cache() and read_cache_preload(). I have
> used the latter in my code. The thing is, when I swap it with the former, I
> could not find any change in the behaviour of my code. They appear to function
> equivalently.
>
> I understand that the *_preload() variant takes a pathspec which preloads index
> contents that match the pathspec in parallel. I don’t know what passing NULL to
> it does. Moreover, does this imply that read_cache() loads the cache on-demand,
> ie, it does no preloading?
>
> I am not sure about what exactly are their differences, and when is one variant
> preferred over the other.
Atharva Raykar (8):
  submodule--helper: refactor resolve_relative_url() helper
  submodule--helper: remove repeated code in sync_submodule()
  dir: libify and export helper functions from clone.c
  submodule--helper: remove constness of sm_path
  submodule--helper: convert the bulk of cmd_add() to C
  submodule--helper: remove add-clone subcommand
  submodule--helper: remove add-config subcommand
  submodule--helper: remove resolve-relative-url subcommand
 builtin/clone.c             | 118 +-------------
 builtin/submodule--helper.c | 304 +++++++++++++++++++-----------------
 dir.c                       | 114 ++++++++++++++
 dir.h                       |   3 +
 git-submodule.sh            |  96 +-----------
 5 files changed, 278 insertions(+), 357 deletions(-)
-- 
2.32.0
Atharva Raykar· Aug 5, 2021, 07:19 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH 1/8] submodule--helper: refactor resolve_relative_url() helper

Refactor the helper function to resolve a relative url, by reusing the existing `compute_submodule_clone_url()` function.

`compute_submodule_clone_url()` performs the same work that `resolve_relative_url()` is doing, so we eliminate this code repetition by moving the former function's definition up, and calling it inside `resolve_relative_url()`.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 61 +++++++++++++++----------------------
 1 file changed, 25 insertions(+), 36 deletions(-)
Show changes to builtin/submodule--helper.c +25 −36
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 2d2d8ac637..f4b496bac6 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -199,33 +199,46 @@ static char *relative_url(const char *remote_url,
 	return strbuf_detach(&sb, NULL);
 }
 
+static char *compute_submodule_clone_url(const char *rel_url, const char *up_path, int quiet)
+{
+	char *remoteurl, *relurl;
+	char *remote = get_default_remote();
+	struct strbuf remotesb = STRBUF_INIT;
+
+	strbuf_addf(&remotesb, "remote.%s.url", remote);
+	if (git_config_get_string(remotesb.buf, &remoteurl)) {
+		if (!quiet)
+			warning(_("could not look up configuration '%s'. "
+				  "Assuming this repository is its own "
+				  "authoritative upstream."),
+				remotesb.buf);
+		remoteurl = xgetcwd();
+	}
+	relurl = relative_url(remoteurl, rel_url, up_path);
+
+	free(remote);
+	free(remoteurl);
+	strbuf_release(&remotesb);
+
+	return relurl;
+}
+
 static int resolve_relative_url(int argc, const char **argv, const char *prefix)
 {
-	char *remoteurl = NULL;
-	char *remote = get_default_remote();
 	const char *up_path = NULL;
 	char *res;
 	const char *url;
-	struct strbuf sb = STRBUF_INIT;
 
 	if (argc != 2 && argc != 3)
 		die("resolve-relative-url only accepts one or two arguments");
 
 	url = argv[1];
-	strbuf_addf(&sb, "remote.%s.url", remote);
-	free(remote);
-
-	if (git_config_get_string(sb.buf, &remoteurl))
-		/* the repository is its own authoritative upstream */
-		remoteurl = xgetcwd();
-
 	if (argc == 3)
 		up_path = argv[2];
 
-	res = relative_url(remoteurl, url, up_path);
+	res = compute_submodule_clone_url(url, up_path, 1);
 	puts(res);
 	free(res);
-	free(remoteurl);
 	return 0;
 }
 
@@ -590,30 +603,6 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
 	return 0;
 }
 
-static char *compute_submodule_clone_url(const char *rel_url, const char *up_path, int quiet)
-{
-	char *remoteurl, *relurl;
-	char *remote = get_default_remote();
-	struct strbuf remotesb = STRBUF_INIT;
-
-	strbuf_addf(&remotesb, "remote.%s.url", remote);
-	if (git_config_get_string(remotesb.buf, &remoteurl)) {
-		if (!quiet)
-			warning(_("could not look up configuration '%s'. "
-				  "Assuming this repository is its own "
-				  "authoritative upstream."),
-				remotesb.buf);
-		remoteurl = xgetcwd();
-	}
-	relurl = relative_url(remoteurl, rel_url, up_path);
-
-	free(remote);
-	free(remoteurl);
-	strbuf_release(&remotesb);
-
-	return relurl;
-}
-
 struct init_cb {
 	const char *prefix;
 	unsigned int flags;
-- 
2.32.0
Atharva Raykar· Aug 5, 2021, 07:19 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH 2/8] submodule--helper: remove repeated code in sync_submodule()

This part of `sync_submodule()` is doing the same thing that `compute_submodule_clone_url()` is doing. Let's reuse that helper here.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 16 +++-------------
 1 file changed, 3 insertions(+), 13 deletions(-)
Show changes to builtin/submodule--helper.c +3 −13
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index f4b496bac6..9b676c12f8 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1373,20 +1373,10 @@ static void sync_submodule(const char *path, const char *prefix,
 	if (sub && sub->url) {
 		if (starts_with_dot_dot_slash(sub->url) ||
 		    starts_with_dot_slash(sub->url)) {
-			char *remote_url, *up_path;
-			char *remote = get_default_remote();
-			strbuf_addf(&sb, "remote.%s.url", remote);
-
-			if (git_config_get_string(sb.buf, &remote_url))
-				remote_url = xgetcwd();
-
-			up_path = get_up_path(path);
-			sub_origin_url = relative_url(remote_url, sub->url, up_path);
-			super_config_url = relative_url(remote_url, sub->url, NULL);
-
-			free(remote);
+			char *up_path = get_up_path(path);
+			sub_origin_url = compute_submodule_clone_url(sub->url, up_path, 1);
+			super_config_url = compute_submodule_clone_url(sub->url, NULL, 1);
 			free(up_path);
-			free(remote_url);
 		} else {
 			sub_origin_url = xstrdup(sub->url);
 			super_config_url = xstrdup(sub->url);
-- 
2.32.0
Đoàn Trần Công Danh· Aug 6, 2021, 00:53 UTC · re: Atharva Raykar · lore

Re: [GSoC] [PATCH 2/8] submodule--helper: remove repeated code in sync_submodule()

On 2021-08-05 12:49:11+0530, Atharva Raykar <raykar.ath@gmail.com> wrote:
Show 33 quoted lines
> This part of `sync_submodule()` is doing the same thing that
> `compute_submodule_clone_url()` is doing. Let's reuse that helper here.
> 
> Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
> Mentored-by: Christian Couder <christian.couder@gmail.com>
> Mentored-by: Shourya Shukla <periperidip@gmail.com>
> ---
>  builtin/submodule--helper.c | 16 +++-------------
>  1 file changed, 3 insertions(+), 13 deletions(-)
> 
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index f4b496bac6..9b676c12f8 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -1373,20 +1373,10 @@ static void sync_submodule(const char *path, const char *prefix,
>  	if (sub && sub->url) {
>  		if (starts_with_dot_dot_slash(sub->url) ||
>  		    starts_with_dot_slash(sub->url)) {
> -			char *remote_url, *up_path;
> -			char *remote = get_default_remote();
> -			strbuf_addf(&sb, "remote.%s.url", remote);
> -
> -			if (git_config_get_string(sb.buf, &remote_url))
> -				remote_url = xgetcwd();
> -
> -			up_path = get_up_path(path);
> -			sub_origin_url = relative_url(remote_url, sub->url, up_path);
> -			super_config_url = relative_url(remote_url, sub->url, NULL);
> -
> -			free(remote);
> +			char *up_path = get_up_path(path);
> +			sub_origin_url = compute_submodule_clone_url(sub->url, up_path, 1);
> +			super_config_url = compute_submodule_clone_url(sub->url, NULL, 1);

While previous patch is definitely a refactoring, this patch add small overhead to the system, the new code will query (then free()) git_config_get_string() and/or xgetcwd() one more time in the second compute_submodule_clone_url()

I think the abstraction overhead is not that big, though.
Show 8 quoted lines
>  			free(up_path);
> -			free(remote_url);
>  		} else {
>  			sub_origin_url = xstrdup(sub->url);
>  			super_config_url = xstrdup(sub->url);
> -- 
> 2.32.0
> 
-- 
Danh
Christian Couder· Aug 6, 2021, 09:06 UTC · re: Đoàn Trần Công Danh · lore

Re: [GSoC] [PATCH 2/8] submodule--helper: remove repeated code in sync_submodule()

On Fri, Aug 6, 2021 at 2:54 AM Đoàn Trần Công Danh <congdanhqx@gmail.com> wrote:
Show 6 quoted lines
> While previous patch is definitely a refactoring, this patch add small
> overhead to the system, the new code will query (then free())
> git_config_get_string() and/or xgetcwd() one more time in the second
> compute_submodule_clone_url()
>
> I think the abstraction overhead is not that big, though.

Yeah, Junio made basically the same comment. So it would be nice if the commit message could mention we are adding a very small overhead in exchange for code simplification (10 lines removed).

Atharva Raykar· Aug 6, 2021, 10:06 UTC · re: Christian Couder · lore

Re: [GSoC] [PATCH 2/8] submodule--helper: remove repeated code in sync_submodule()

Christian Couder <christian.couder@gmail.com> writes:
Show 12 quoted lines
> On Fri, Aug 6, 2021 at 2:54 AM Đoàn Trần Công Danh <congdanhqx@gmail.com> wrote:
>
>> While previous patch is definitely a refactoring, this patch add small
>> overhead to the system, the new code will query (then free())
>> git_config_get_string() and/or xgetcwd() one more time in the second
>> compute_submodule_clone_url()
>>
>> I think the abstraction overhead is not that big, though.
>
> Yeah, Junio made basically the same comment. So it would be nice if
> the commit message could mention we are adding a very small overhead
> in exchange for code simplification (10 lines removed).
Okay, that makes sense.
Junio C Hamano· Aug 6, 2021, 16:21 UTC · re: Christian Couder · lore

Re: [GSoC] [PATCH 2/8] submodule--helper: remove repeated code in sync_submodule()

Christian Couder <christian.couder@gmail.com> writes:
Show 12 quoted lines
> On Fri, Aug 6, 2021 at 2:54 AM Đoàn Trần Công Danh <congdanhqx@gmail.com> wrote:
>
>> While previous patch is definitely a refactoring, this patch add small
>> overhead to the system, the new code will query (then free())
>> git_config_get_string() and/or xgetcwd() one more time in the second
>> compute_submodule_clone_url()
>>
>> I think the abstraction overhead is not that big, though.
>
> Yeah, Junio made basically the same comment. So it would be nice if
> the commit message could mention we are adding a very small overhead
> in exchange for code simplification (10 lines removed).

If you guys want to, I wouldn't stop, but my comment was just a thinking-aloud observation, not a suggestion to add to the proposed log message.

Thanks.
Atharva Raykar· Aug 5, 2021, 07:19 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH 3/8] dir: libify and export helper functions from clone.c

These functions can be useful to other parts of Git. Let's move them to dir.c, while renaming them to be make their functionality more explicit.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <shouryashukla.oo@gmail.com>
---
 builtin/clone.c | 118 +-----------------------------------------------
 dir.c           | 114 ++++++++++++++++++++++++++++++++++++++++++++++
 dir.h           |   3 ++
 3 files changed, 119 insertions(+), 116 deletions(-)
Show changes to 3 files +119 −116

builtin/clone.c, dir.c, dir.h

diff --git a/builtin/clone.c b/builtin/clone.c
index 66fe66679c..b9b59a838f 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -217,120 +217,6 @@ static char *get_repo_path(const char *repo, int *is_bundle)
 	return canon;
 }
 
-static char *guess_dir_name(const char *repo, int is_bundle, int is_bare)
-{
-	const char *end = repo + strlen(repo), *start, *ptr;
-	size_t len;
-	char *dir;
-
-	/*
-	 * Skip scheme.
-	 */
-	start = strstr(repo, "://");
-	if (start == NULL)
-		start = repo;
-	else
-		start += 3;
-
-	/*
-	 * Skip authentication data. The stripping does happen
-	 * greedily, such that we strip up to the last '@' inside
-	 * the host part.
-	 */
-	for (ptr = start; ptr < end && !is_dir_sep(*ptr); ptr++) {
-		if (*ptr == '@')
-			start = ptr + 1;
-	}
-
-	/*
-	 * Strip trailing spaces, slashes and /.git
-	 */
-	while (start < end && (is_dir_sep(end[-1]) || isspace(end[-1])))
-		end--;
-	if (end - start > 5 && is_dir_sep(end[-5]) &&
-	    !strncmp(end - 4, ".git", 4)) {
-		end -= 5;
-		while (start < end && is_dir_sep(end[-1]))
-			end--;
-	}
-
-	/*
-	 * Strip trailing port number if we've got only a
-	 * hostname (that is, there is no dir separator but a
-	 * colon). This check is required such that we do not
-	 * strip URI's like '/foo/bar:2222.git', which should
-	 * result in a dir '2222' being guessed due to backwards
-	 * compatibility.
-	 */
-	if (memchr(start, '/', end - start) == NULL
-	    && memchr(start, ':', end - start) != NULL) {
-		ptr = end;
-		while (start < ptr && isdigit(ptr[-1]) && ptr[-1] != ':')
-			ptr--;
-		if (start < ptr && ptr[-1] == ':')
-			end = ptr - 1;
-	}
-
-	/*
-	 * Find last component. To remain backwards compatible we
-	 * also regard colons as path separators, such that
-	 * cloning a repository 'foo:bar.git' would result in a
-	 * directory 'bar' being guessed.
-	 */
-	ptr = end;
-	while (start < ptr && !is_dir_sep(ptr[-1]) && ptr[-1] != ':')
-		ptr--;
-	start = ptr;
-
-	/*
-	 * Strip .{bundle,git}.
-	 */
-	len = end - start;
-	strip_suffix_mem(start, &len, is_bundle ? ".bundle" : ".git");
-
-	if (!len || (len == 1 && *start == '/'))
-		die(_("No directory name could be guessed.\n"
-		      "Please specify a directory on the command line"));
-
-	if (is_bare)
-		dir = xstrfmt("%.*s.git", (int)len, start);
-	else
-		dir = xstrndup(start, len);
-	/*
-	 * Replace sequences of 'control' characters and whitespace
-	 * with one ascii space, remove leading and trailing spaces.
-	 */
-	if (*dir) {
-		char *out = dir;
-		int prev_space = 1 /* strip leading whitespace */;
-		for (end = dir; *end; ++end) {
-			char ch = *end;
-			if ((unsigned char)ch < '\x20')
-				ch = '\x20';
-			if (isspace(ch)) {
-				if (prev_space)
-					continue;
-				prev_space = 1;
-			} else
-				prev_space = 0;
-			*out++ = ch;
-		}
-		*out = '\0';
-		if (out > dir && prev_space)
-			out[-1] = '\0';
-	}
-	return dir;
-}
-
-static void strip_trailing_slashes(char *dir)
-{
-	char *end = dir + strlen(dir);
-
-	while (dir < end - 1 && is_dir_sep(end[-1]))
-		end--;
-	*end = '\0';
-}
-
 static int add_one_reference(struct string_list_item *item, void *cb_data)
 {
 	struct strbuf err = STRBUF_INIT;
@@ -1041,8 +927,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 	if (argc == 2)
 		dir = xstrdup(argv[1]);
 	else
-		dir = guess_dir_name(repo_name, is_bundle, option_bare);
-	strip_trailing_slashes(dir);
+		dir = guess_dir_name_from_git_url(repo_name, is_bundle, option_bare);
+	strip_dir_trailing_slashes(dir);
 
 	dest_exists = path_exists(dir);
 	if (dest_exists && !is_empty_dir(dir))
diff --git a/dir.c b/dir.c
index 03c4d21267..84b47c4dbc 100644
--- a/dir.c
+++ b/dir.c
@@ -2970,6 +2970,120 @@ int is_empty_dir(const char *path)
 	return ret;
 }
 
+char *guess_dir_name_from_git_url(const char *repo, int is_bundle, int is_bare)
+{
+	const char *end = repo + strlen(repo), *start, *ptr;
+	size_t len;
+	char *dir;
+
+	/*
+	 * Skip scheme.
+	 */
+	start = strstr(repo, "://");
+	if (start == NULL)
+		start = repo;
+	else
+		start += 3;
+
+	/*
+	 * Skip authentication data. The stripping does happen
+	 * greedily, such that we strip up to the last '@' inside
+	 * the host part.
+	 */
+	for (ptr = start; ptr < end && !is_dir_sep(*ptr); ptr++) {
+		if (*ptr == '@')
+			start = ptr + 1;
+	}
+
+	/*
+	 * Strip trailing spaces, slashes and /.git
+	 */
+	while (start < end && (is_dir_sep(end[-1]) || isspace(end[-1])))
+		end--;
+	if (end - start > 5 && is_dir_sep(end[-5]) &&
+	    !strncmp(end - 4, ".git", 4)) {
+		end -= 5;
+		while (start < end && is_dir_sep(end[-1]))
+			end--;
+	}
+
+	/*
+	 * Strip trailing port number if we've got only a
+	 * hostname (that is, there is no dir separator but a
+	 * colon). This check is required such that we do not
+	 * strip URI's like '/foo/bar:2222.git', which should
+	 * result in a dir '2222' being guessed due to backwards
+	 * compatibility.
+	 */
+	if (memchr(start, '/', end - start) == NULL
+	    && memchr(start, ':', end - start) != NULL) {
+		ptr = end;
+		while (start < ptr && isdigit(ptr[-1]) && ptr[-1] != ':')
+			ptr--;
+		if (start < ptr && ptr[-1] == ':')
+			end = ptr - 1;
+	}
+
+	/*
+	 * Find last component. To remain backwards compatible we
+	 * also regard colons as path separators, such that
+	 * cloning a repository 'foo:bar.git' would result in a
+	 * directory 'bar' being guessed.
+	 */
+	ptr = end;
+	while (start < ptr && !is_dir_sep(ptr[-1]) && ptr[-1] != ':')
+		ptr--;
+	start = ptr;
+
+	/*
+	 * Strip .{bundle,git}.
+	 */
+	len = end - start;
+	strip_suffix_mem(start, &len, is_bundle ? ".bundle" : ".git");
+
+	if (!len || (len == 1 && *start == '/'))
+		die(_("No directory name could be guessed.\n"
+		      "Please specify a directory on the command line"));
+
+	if (is_bare)
+		dir = xstrfmt("%.*s.git", (int)len, start);
+	else
+		dir = xstrndup(start, len);
+	/*
+	 * Replace sequences of 'control' characters and whitespace
+	 * with one ascii space, remove leading and trailing spaces.
+	 */
+	if (*dir) {
+		char *out = dir;
+		int prev_space = 1 /* strip leading whitespace */;
+		for (end = dir; *end; ++end) {
+			char ch = *end;
+			if ((unsigned char)ch < '\x20')
+				ch = '\x20';
+			if (isspace(ch)) {
+				if (prev_space)
+					continue;
+				prev_space = 1;
+			} else
+				prev_space = 0;
+			*out++ = ch;
+		}
+		*out = '\0';
+		if (out > dir && prev_space)
+			out[-1] = '\0';
+	}
+	return dir;
+}
+
+void strip_dir_trailing_slashes(char *dir)
+{
+	char *end = dir + strlen(dir);
+
+	while (dir < end - 1 && is_dir_sep(end[-1]))
+		end--;
+	*end = '\0';
+}
+
 static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up)
 {
 	DIR *dir;
diff --git a/dir.h b/dir.h
index b3e1a54a97..76441dde2d 100644
--- a/dir.h
+++ b/dir.h
@@ -453,6 +453,9 @@ static inline int is_dot_or_dotdot(const char *name)
 
 int is_empty_dir(const char *dir);
 
+char *guess_dir_name_from_git_url(const char *repo, int is_bundle, int is_bare);
+void strip_dir_trailing_slashes(char *dir);
+
 void setup_standard_excludes(struct dir_struct *dir);
 
 char *get_sparse_checkout_filename(void);
-- 
2.32.0
Atharva Raykar· Aug 5, 2021, 07:19 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH 4/8] submodule--helper: remove constness of sm_path

This is needed so that it can be modified by normalize_path_copy() in the next patch.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
Show changes to builtin/submodule--helper.c +1 −1
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 9b676c12f8..99aabf1078 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2748,7 +2748,7 @@ struct add_data {
 	const char *prefix;
 	const char *branch;
 	const char *reference_path;
-	const char *sm_path;
+	char *sm_path;
 	const char *sm_name;
 	const char *repo;
 	const char *realrepo;
-- 
2.32.0
Atharva Raykar· Aug 5, 2021, 07:19 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH 5/8] submodule--helper: convert the bulk of cmd_add() to C

Introduce the 'add' subcommand to `submodule--helper.c` that does all the work 'submodule add' past the parsing of flags.

As with the previous conversions, this is meant to be a faithful conversion with no modification to the behaviour of `submodule add`.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Helped-by: Kaartic Sivaraam <kaartic.sivaraam@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
Based-on-patch-by: Shourya Shukla <periperidip@gmail.com>
Based-on-patch-by: Prathamesh Chavan <pc44800@gmail.com>
---
 builtin/submodule--helper.c | 160 ++++++++++++++++++++++++++++++++++++
 git-submodule.sh            |  96 +---------------------
 2 files changed, 162 insertions(+), 94 deletions(-)
Show changes to 2 files +162 −94

builtin/submodule--helper.c, git-submodule.sh

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 99aabf1078..05ae9ebe50 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -3046,6 +3046,165 @@ static int add_config(int argc, const char **argv, const char *prefix)
 	return 0;
 }
 
+static void die_on_index_match(const char *path, int force)
+{
+	struct pathspec ps;
+	const char *args[] = { path, NULL };
+	parse_pathspec(&ps, 0, PATHSPEC_PREFER_CWD, NULL, args);
+
+	if (read_cache_preload(NULL) < 0)
+		die(_("index file corrupt"));
+
+	if (ps.nr) {
+		int i;
+		char *ps_matched = xcalloc(ps.nr, 1);
+
+		/* TODO: audit for interaction with sparse-index. */
+		ensure_full_index(&the_index);
+
+		/*
+		 * Since there is only one pathspec, we just need
+		 * need to check ps_matched[0] to know if a cache
+		 * entry matched.
+		 */
+		for (i = 0; i < active_nr; i++) {
+			ce_path_match(&the_index, active_cache[i], &ps,
+				      ps_matched);
+
+			if (ps_matched[0]) {
+				if (!force)
+					die(_("'%s' already exists in the index"),
+					    path);
+				if (!S_ISGITLINK(active_cache[i]->ce_mode))
+					die(_("'%s' already exists in the index "
+					      "and is not a submodule"), path);
+				break;
+			}
+		}
+		free(ps_matched);
+	}
+}
+
+static void die_on_repo_without_commits(const char *path)
+{
+	struct strbuf sb = STRBUF_INIT;
+	strbuf_addstr(&sb, path);
+	if (is_nonbare_repository_dir(&sb)) {
+		struct object_id oid;
+		if (resolve_gitlink_ref(path, "HEAD", &oid) < 0)
+			die(_("'%s' does not have a commit checked out"), path);
+	}
+}
+
+static int module_add(int argc, const char **argv, const char *prefix)
+{
+	int force = 0, quiet = 0, progress = 0, dissociate = 0;
+	struct add_data add_data = ADD_DATA_INIT;
+
+	struct option options[] = {
+		OPT_STRING('b', "branch", &add_data.branch, N_("branch"),
+			   N_("branch of repository to add as submodule")),
+		OPT__FORCE(&force, N_("allow adding an otherwise ignored submodule path"),
+			   PARSE_OPT_NOCOMPLETE),
+		OPT__QUIET(&quiet, N_("print only error messages")),
+		OPT_BOOL(0, "progress", &progress, N_("force cloning progress")),
+		OPT_STRING(0, "reference", &add_data.reference_path, N_("repository"),
+			   N_("reference repository")),
+		OPT_BOOL(0, "dissociate", &dissociate, N_("borrow the objects from reference repositories")),
+		OPT_STRING(0, "name", &add_data.sm_name, N_("name"),
+			   N_("sets the submodule’s name to the given string "
+			      "instead of defaulting to its path")),
+		OPT_INTEGER(0, "depth", &add_data.depth, N_("depth for shallow clones")),
+		OPT_END()
+	};
+
+	const char *const usage[] = {
+		N_("git submodule--helper add [<options>] [--] <repository> [<path>]"),
+		NULL
+	};
+
+	argc = parse_options(argc, argv, prefix, options, usage, 0);
+
+	if (!is_writing_gitmodules_ok())
+		die(_("please make sure that the .gitmodules file is in the working tree"));
+
+	if (prefix && *prefix &&
+	    add_data.reference_path && !is_absolute_path(add_data.reference_path))
+		add_data.reference_path = xstrfmt("%s%s", prefix, add_data.reference_path);
+
+	if (argc == 0 || argc > 2)
+		usage_with_options(usage, options);
+
+	add_data.repo = argv[0];
+	if (argc == 1)
+		add_data.sm_path = guess_dir_name_from_git_url(add_data.repo, 0, 0);
+	else
+		add_data.sm_path = xstrdup(argv[1]);
+
+	if (prefix && *prefix && !is_absolute_path(add_data.sm_path))
+		add_data.sm_path = xstrfmt("%s%s", prefix, add_data.sm_path);
+
+	if (starts_with_dot_dot_slash(add_data.repo) ||
+	    starts_with_dot_slash(add_data.repo)) {
+		if (prefix)
+			die(_("Relative path can only be used from the toplevel "
+			      "of the working tree"));
+
+		/* dereference source url relative to parent's url */
+		add_data.realrepo = compute_submodule_clone_url(add_data.repo, NULL, 1);
+	} else if (is_dir_sep(add_data.repo[0]) || strchr(add_data.repo, ':')) {
+		add_data.realrepo = add_data.repo;
+	} else {
+		die(_("repo URL: '%s' must be absolute or begin with ./|../"),
+		    add_data.repo);
+	}
+
+	/*
+	 * normalize path:
+	 * multiple //; leading ./; /./; /../;
+	 */
+	normalize_path_copy(add_data.sm_path, add_data.sm_path);
+	strip_dir_trailing_slashes(add_data.sm_path);
+
+	die_on_index_match(add_data.sm_path, force);
+	die_on_repo_without_commits(add_data.sm_path);
+
+	if (!force) {
+		int exit_code = -1;
+		struct strbuf sb = STRBUF_INIT;
+		struct child_process cp = CHILD_PROCESS_INIT;
+		cp.git_cmd = 1;
+		cp.no_stdout = 1;
+		strvec_pushl(&cp.args, "add", "--dry-run", "--ignore-missing",
+			     "--no-warn-embedded-repo", add_data.sm_path, NULL);
+		if ((exit_code = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {
+			strbuf_complete_line(&sb);
+			fputs(sb.buf, stderr);
+			return exit_code;
+		}
+		strbuf_release(&sb);
+	}
+
+	if(!add_data.sm_name)
+		add_data.sm_name = add_data.sm_path;
+
+	if (check_submodule_name(add_data.sm_name))
+		die(_("'%s' is not a valid submodule name"), add_data.sm_name);
+
+	add_data.prefix = prefix;
+	add_data.force = !!force;
+	add_data.quiet = !!quiet;
+	add_data.progress = !!progress;
+	add_data.dissociate = !!dissociate;
+
+	if (add_submodule(&add_data))
+		return 1;
+	configure_added_submodule(&add_data);
+	free(add_data.sm_path);
+
+	return 0;
+}
+
 #define SUPPORT_SUPER_PREFIX (1<<0)
 
 struct cmd_struct {
@@ -3060,6 +3219,7 @@ static struct cmd_struct commands[] = {
 	{"clone", module_clone, 0},
 	{"add-clone", add_clone, 0},
 	{"add-config", add_config, 0},
+	{"add", module_add, SUPPORT_SUPER_PREFIX},
 	{"update-module-mode", module_update_module_mode, 0},
 	{"update-clone", update_clone, 0},
 	{"ensure-core-worktree", ensure_core_worktree, 0},
diff --git a/git-submodule.sh b/git-submodule.sh
index 8c219ef382..1070540525 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -145,104 +145,12 @@ cmd_add()
 		shift
 	done
 
-	if ! git submodule--helper config --check-writeable >/dev/null 2>&1
+	if test -z "$1"
 	then
-		 die "fatal: $(eval_gettext "please make sure that the .gitmodules file is in the working tree")"
-	fi
-
-	if test -n "$reference_path"
-	then
-		is_absolute_path "$reference_path" ||
-		reference_path="$wt_prefix$reference_path"
-
-		reference="--reference=$reference_path"
-	fi
-
-	repo=$1
-	sm_path=$2
-
-	if test -z "$sm_path"; then
-		sm_path=$(printf '%s\n' "$repo" |
-			sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
-	fi
-
-	if test -z "$repo" || test -z "$sm_path"; then
 		usage
 	fi
 
-	is_absolute_path "$sm_path" || sm_path="$wt_prefix$sm_path"
-
-	# assure repo is absolute or relative to parent
-	case "$repo" in
-	./*|../*)
-		test -z "$wt_prefix" ||
-		die "fatal: $(gettext "Relative path can only be used from the toplevel of the working tree")"
-
-		# dereference source url relative to parent's url
-		realrepo=$(git submodule--helper resolve-relative-url "$repo") || exit
-		;;
-	*:*|/*)
-		# absolute url
-		realrepo=$repo
-		;;
-	*)
-		die "fatal: $(eval_gettext "repo URL: '\$repo' must be absolute or begin with ./|../")"
-	;;
-	esac
-
-	# normalize path:
-	# multiple //; leading ./; /./; /../; trailing /
-	sm_path=$(printf '%s/\n' "$sm_path" |
-		sed -e '
-			s|//*|/|g
-			s|^\(\./\)*||
-			s|/\(\./\)*|/|g
-			:start
-			s|\([^/]*\)/\.\./||
-			tstart
-			s|/*$||
-		')
-	if test -z "$force"
-	then
-		git ls-files --error-unmatch "$sm_path" > /dev/null 2>&1 &&
-		die "fatal: $(eval_gettext "'\$sm_path' already exists in the index")"
-	else
-		git ls-files -s "$sm_path" | sane_grep -v "^160000" > /dev/null 2>&1 &&
-		die "fatal: $(eval_gettext "'\$sm_path' already exists in the index and is not a submodule")"
-	fi
-
-	if test -d "$sm_path" &&
-		test -z $(git -C "$sm_path" rev-parse --show-cdup 2>/dev/null)
-	then
-	    git -C "$sm_path" rev-parse --verify -q HEAD >/dev/null ||
-	    die "fatal: $(eval_gettext "'\$sm_path' does not have a commit checked out")"
-	fi
-
-	if test -z "$force"
-	then
-	    dryerr=$(git add --dry-run --ignore-missing --no-warn-embedded-repo "$sm_path" 2>&1 >/dev/null)
-	    res=$?
-	    if test $res -ne 0
-	    then
-		 echo >&2 "$dryerr"
-		 exit $res
-	    fi
-	fi
-
-	if test -n "$custom_name"
-	then
-		sm_name="$custom_name"
-	else
-		sm_name="$sm_path"
-	fi
-
-	if ! git submodule--helper check-name "$sm_name"
-	then
-		die "fatal: $(eval_gettext "'$sm_name' is not a valid submodule name")"
-	fi
-
-	git submodule--helper add-clone ${GIT_QUIET:+--quiet} ${force:+"--force"} ${progress:+"--progress"} ${branch:+--branch "$branch"} --prefix "$wt_prefix" --path "$sm_path" --name "$sm_name" --url "$realrepo" ${reference:+"$reference"} ${dissociate:+"--dissociate"} ${depth:+"$depth"} || exit
-	git submodule--helper add-config ${force:+--force} ${branch:+--branch "$branch"} --url "$repo" --resolved-url "$realrepo" --path "$sm_path" --name "$sm_name"
+	git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper add ${GIT_QUIET:+--quiet} ${force:+--force} ${progress:+"--progress"} ${branch:+--branch "$branch"} ${reference_path:+--reference "$reference_path"} ${dissociate:+--dissociate} ${custom_name:+--name "$custom_name"} ${depth:+"$depth"} -- "$@"
 }
 
 #
-- 
2.32.0
Atharva Raykar· Aug 5, 2021, 07:19 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH 6/8] submodule--helper: remove add-clone subcommand

We no longer need this subcommand, as all of its functionality is being called by the newly-introduced `module_add()` directly within C.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 60 -------------------------------------
 1 file changed, 60 deletions(-)
Show changes to builtin/submodule--helper.c +0 −60
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 05ae9ebe50..700cc8bbeb 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2860,65 +2860,6 @@ static int add_submodule(const struct add_data *add_data)
 	return 0;
 }
 
-static int add_clone(int argc, const char **argv, const char *prefix)
-{
-	int force = 0, quiet = 0, dissociate = 0, progress = 0;
-	struct add_data add_data = ADD_DATA_INIT;
-
-	struct option options[] = {
-		OPT_STRING('b', "branch", &add_data.branch,
-			   N_("branch"),
-			   N_("branch of repository to checkout on cloning")),
-		OPT_STRING(0, "prefix", &prefix,
-			   N_("path"),
-			   N_("alternative anchor for relative paths")),
-		OPT_STRING(0, "path", &add_data.sm_path,
-			   N_("path"),
-			   N_("where the new submodule will be cloned to")),
-		OPT_STRING(0, "name", &add_data.sm_name,
-			   N_("string"),
-			   N_("name of the new submodule")),
-		OPT_STRING(0, "url", &add_data.realrepo,
-			   N_("string"),
-			   N_("url where to clone the submodule from")),
-		OPT_STRING(0, "reference", &add_data.reference_path,
-			   N_("repo"),
-			   N_("reference repository")),
-		OPT_BOOL(0, "dissociate", &dissociate,
-			 N_("use --reference only while cloning")),
-		OPT_INTEGER(0, "depth", &add_data.depth,
-			    N_("depth for shallow clones")),
-		OPT_BOOL(0, "progress", &progress,
-			 N_("force cloning progress")),
-		OPT__FORCE(&force, N_("allow adding an otherwise ignored submodule path"),
-			   PARSE_OPT_NOCOMPLETE),
-		OPT__QUIET(&quiet, "suppress output for cloning a submodule"),
-		OPT_END()
-	};
-
-	const char *const usage[] = {
-		N_("git submodule--helper add-clone [<options>...] "
-		   "--url <url> --path <path> --name <name>"),
-		NULL
-	};
-
-	argc = parse_options(argc, argv, prefix, options, usage, 0);
-
-	if (argc != 0)
-		usage_with_options(usage, options);
-
-	add_data.prefix = prefix;
-	add_data.progress = !!progress;
-	add_data.dissociate = !!dissociate;
-	add_data.force = !!force;
-	add_data.quiet = !!quiet;
-
-	if (add_submodule(&add_data))
-		return 1;
-
-	return 0;
-}
-
 static int config_submodule_in_gitmodules(const char *name, const char *var, const char *value)
 {
 	char *key;
@@ -3217,7 +3158,6 @@ static struct cmd_struct commands[] = {
 	{"list", module_list, 0},
 	{"name", module_name, 0},
 	{"clone", module_clone, 0},
-	{"add-clone", add_clone, 0},
 	{"add-config", add_config, 0},
 	{"add", module_add, SUPPORT_SUPER_PREFIX},
 	{"update-module-mode", module_update_module_mode, 0},
-- 
2.32.0
Atharva Raykar· Aug 5, 2021, 07:19 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH 8/8] submodule--helper: remove resolve-relative-url subcommand

The shell subcommand `resolve-relative-url` is no longer required, as its last caller has been removed when it was converted to C.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 20 --------------------
 1 file changed, 20 deletions(-)
Show changes to builtin/submodule--helper.c +0 −20
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index da83e8e2a5..514891a22f 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -223,25 +223,6 @@ static char *compute_submodule_clone_url(const char *rel_url, const char *up_pat
 	return relurl;
 }
 
-static int resolve_relative_url(int argc, const char **argv, const char *prefix)
-{
-	const char *up_path = NULL;
-	char *res;
-	const char *url;
-
-	if (argc != 2 && argc != 3)
-		die("resolve-relative-url only accepts one or two arguments");
-
-	url = argv[1];
-	if (argc == 3)
-		up_path = argv[2];
-
-	res = compute_submodule_clone_url(url, up_path, 1);
-	puts(res);
-	free(res);
-	return 0;
-}
-
 static int resolve_relative_url_test(int argc, const char **argv, const char *prefix)
 {
 	char *remoteurl, *res;
@@ -3115,7 +3096,6 @@ static struct cmd_struct commands[] = {
 	{"update-clone", update_clone, 0},
 	{"ensure-core-worktree", ensure_core_worktree, 0},
 	{"relative-path", resolve_relative_path, 0},
-	{"resolve-relative-url", resolve_relative_url, 0},
 	{"resolve-relative-url-test", resolve_relative_url_test, 0},
 	{"foreach", module_foreach, SUPPORT_SUPER_PREFIX},
 	{"init", module_init, SUPPORT_SUPER_PREFIX},
-- 
2.32.0
Atharva Raykar· Aug 5, 2021, 07:19 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH 7/8] submodule--helper: remove add-config subcommand

Also no longer needed is this subcommand, as all of its functionality is being called by the newly-introduced `module_add()` directly within C.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 49 -------------------------------------
 1 file changed, 49 deletions(-)
Show changes to builtin/submodule--helper.c +0 −49
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 700cc8bbeb..da83e8e2a5 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2939,54 +2939,6 @@ static void configure_added_submodule(struct add_data *add_data)
 	}
 }
 
-static int add_config(int argc, const char **argv, const char *prefix)
-{
-	int force = 0;
-	struct add_data add_data = ADD_DATA_INIT;
-
-	struct option options[] = {
-		OPT_STRING('b', "branch", &add_data.branch,
-			   N_("branch"),
-			   N_("branch of repository to store in "
-			      "the submodule configuration")),
-		OPT_STRING(0, "url", &add_data.repo,
-			   N_("string"),
-			   N_("url to clone submodule from")),
-		OPT_STRING(0, "resolved-url", &add_data.realrepo,
-			   N_("string"),
-			   N_("url to clone the submodule from, after it has "
-			      "been dereferenced relative to parent's url, "
-			      "in the case where <url> is a relative url")),
-		OPT_STRING(0, "path", &add_data.sm_path,
-			   N_("path"),
-			   N_("where the new submodule will be cloned to")),
-		OPT_STRING(0, "name", &add_data.sm_name,
-			   N_("string"),
-			   N_("name of the new submodule")),
-		OPT__FORCE(&force, N_("allow adding an otherwise ignored submodule path"),
-			   PARSE_OPT_NOCOMPLETE),
-		OPT_END()
-	};
-
-	const char *const usage[] = {
-		N_("git submodule--helper add-config "
-		   "[--force|-f] [--branch|-b <branch>] "
-		   "--url <url> --resolved-url <resolved-url> "
-		   "--path <path> --name <name>"),
-		NULL
-	};
-
-	argc = parse_options(argc, argv, prefix, options, usage, 0);
-
-	if (argc)
-		usage_with_options(usage, options);
-
-	add_data.force = !!force;
-	configure_added_submodule(&add_data);
-
-	return 0;
-}
-
 static void die_on_index_match(const char *path, int force)
 {
 	struct pathspec ps;
@@ -3158,7 +3110,6 @@ static struct cmd_struct commands[] = {
 	{"list", module_list, 0},
 	{"name", module_name, 0},
 	{"clone", module_clone, 0},
-	{"add-config", add_config, 0},
 	{"add", module_add, SUPPORT_SUPER_PREFIX},
 	{"update-module-mode", module_update_module_mode, 0},
 	{"update-clone", update_clone, 0},
-- 
2.32.0
Atharva Raykar· Aug 5, 2021, 07:40 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v2 0/9] submodule: convert the rest of 'add' to C

I missed the adding the first patch of this series, so I am re-sending with the missing commit included.

No change since v1, except for the inclusion of 75edf24186 (submodule--helper: add options for compute_submodule_clone_url(), 2021-07-06) at the beginning.

I am including the cover letter of v1 as-is, so the discussion can start from here, with minor edits to reflect the accurate counts of the patches:

---8<------8<------8<------8<------8<---
NOTE: This series uses the change introduced by 'ar/submodule-add-config'[1]

This series completes the conversion of all the important shell logic in 'submodule add' to C, by wrapping it in a submodule--helper builtin subcommand called 'add'.

The first 5 patches are preparatory patches. The refactors mostly involve exposing interfaces to C that were only previously usable as shell subcommands.

Then we have a patch that translates the shell code to C, faithfully reproducing the behaviour before the conversion.

The last 3 patches are cleanup patches. Our conversions have introduced a lot of dead code, all of them being 'submodule--helper' subcommands that have no further use, as we have C interfaces for these already. We remove these subcommands.

A question about the cache API used in [PATCH 5/9]:
  What is the difference between 'read_cache()' and 'read_cache_preload()'? [2]
  Which one is more appropriate for use in 'die_on_index_match()'?

Fetch-it-Via: git fetch https://github.com/tfidfwastaken/git submodule-helper-add-list-1

Footnotes =========

[1] https://lore.kernel.org/git/20210801063352.50813-1-raykar.ath@gmail.com/ [2] More about this question has been detailed in this section of my blog: http://atharvaraykar.me/gitnotes/week5#some-challenges-with-the-changes-that-are-cooking

I'll quote it here for convenience:
Show 15 quoted lines
> Before iterating through the cache entries of the index, you need to populate
> it.
>
> There’s two functions for this: read_cache() and read_cache_preload(). I have
> used the latter in my code. The thing is, when I swap it with the former, I
> could not find any change in the behaviour of my code. They appear to function
> equivalently.
>
> I understand that the *_preload() variant takes a pathspec which preloads index
> contents that match the pathspec in parallel. I don’t know what passing NULL to
> it does. Moreover, does this imply that read_cache() loads the cache on-demand,
> ie, it does no preloading?
>
> I am not sure about what exactly are their differences, and when is one variant
> preferred over the other.
--->8------>8------>8------>8------>8---
Atharva Raykar (9):
  submodule--helper: add options for compute_submodule_clone_url()
  submodule--helper: refactor resolve_relative_url() helper
  submodule--helper: remove repeated code in sync_submodule()
  dir: libify and export helper functions from clone.c
  submodule--helper: remove constness of sm_path
  submodule--helper: convert the bulk of cmd_add() to C
  submodule--helper: remove add-clone subcommand
  submodule--helper: remove add-config subcommand
  submodule--helper: remove resolve-relative-url subcommand
 builtin/clone.c             | 118 +-------------
 builtin/submodule--helper.c | 304 +++++++++++++++++++-----------------
 dir.c                       | 114 ++++++++++++++
 dir.h                       |   3 +
 git-submodule.sh            |  96 +-----------
 5 files changed, 280 insertions(+), 355 deletions(-)
-- 
2.32.0
Atharva Raykar· Aug 5, 2021, 07:40 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v2 1/9] submodule--helper: add options for compute_submodule_clone_url()

Let's modify the interface to `compute_submodule_clone_url()` function by adding two more arguments, so that we can reuse this in various parts of `submodule--helper.c` that follow a common pattern, which is--read the remote url configuration of the superproject and then call `relative_url()`.

This function is nearly identical to `resolve_relative_url()`, the only difference being the extra warning message. We can add a quiet flag to it, to suppress that warning when not needed, and then refactor `resolve_relative_url()` by using this function, something we will do in the next patch.

Having this functionality factored out will be useful for converting the rest of `submodule add` in subsequent patches.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)
Show changes to builtin/submodule--helper.c +9 −5
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 6891480013..2d2d8ac637 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -590,7 +590,7 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
 	return 0;
 }
 
-static char *compute_submodule_clone_url(const char *rel_url)
+static char *compute_submodule_clone_url(const char *rel_url, const char *up_path, int quiet)
 {
 	char *remoteurl, *relurl;
 	char *remote = get_default_remote();
@@ -598,10 +598,14 @@ static char *compute_submodule_clone_url(const char *rel_url)
 
 	strbuf_addf(&remotesb, "remote.%s.url", remote);
 	if (git_config_get_string(remotesb.buf, &remoteurl)) {
-		warning(_("could not look up configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf);
+		if (!quiet)
+			warning(_("could not look up configuration '%s'. "
+				  "Assuming this repository is its own "
+				  "authoritative upstream."),
+				remotesb.buf);
 		remoteurl = xgetcwd();
 	}
-	relurl = relative_url(remoteurl, rel_url, NULL);
+	relurl = relative_url(remoteurl, rel_url, up_path);
 
 	free(remote);
 	free(remoteurl);
@@ -660,7 +664,7 @@ static void init_submodule(const char *path, const char *prefix,
 		if (starts_with_dot_dot_slash(url) ||
 		    starts_with_dot_slash(url)) {
 			char *oldurl = url;
-			url = compute_submodule_clone_url(oldurl);
+			url = compute_submodule_clone_url(oldurl, NULL, 0);
 			free(oldurl);
 		}
 
@@ -2134,7 +2138,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
 	if (repo_config_get_string_tmp(the_repository, sb.buf, &url)) {
 		if (starts_with_dot_slash(sub->url) ||
 		    starts_with_dot_dot_slash(sub->url)) {
-			url = compute_submodule_clone_url(sub->url);
+			url = compute_submodule_clone_url(sub->url, NULL, 0);
 			need_free_url = 1;
 		} else
 			url = sub->url;
-- 
2.32.0
Junio C Hamano· Aug 5, 2021, 20:05 UTC · re: Atharva Raykar · lore

Re: [GSoC] [PATCH v2 1/9] submodule--helper: add options for compute_submodule_clone_url()

Atharva Raykar <raykar.ath@gmail.com> writes:
Show 19 quoted lines
> -static char *compute_submodule_clone_url(const char *rel_url)
> +static char *compute_submodule_clone_url(const char *rel_url, const char *up_path, int quiet)
>  {
>  	char *remoteurl, *relurl;
>  	char *remote = get_default_remote();
> @@ -598,10 +598,14 @@ static char *compute_submodule_clone_url(const char *rel_url)
>  
>  	strbuf_addf(&remotesb, "remote.%s.url", remote);
>  	if (git_config_get_string(remotesb.buf, &remoteurl)) {
> -		warning(_("could not look up configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf);
> +		if (!quiet)
> +			warning(_("could not look up configuration '%s'. "
> +				  "Assuming this repository is its own "
> +				  "authoritative upstream."),
> +				remotesb.buf);
>  		remoteurl = xgetcwd();
>  	}
> -	relurl = relative_url(remoteurl, rel_url, NULL);
> +	relurl = relative_url(remoteurl, rel_url, up_path);

OK, so we can optionally operate silently, and also tell relative_url the path the URL should be made relative to.

Existing callers, of course, should pass NULL and 0 as these optional features are new, as we can see below.

Show 20 quoted lines
>  	free(remote);
>  	free(remoteurl);
> @@ -660,7 +664,7 @@ static void init_submodule(const char *path, const char *prefix,
>  		if (starts_with_dot_dot_slash(url) ||
>  		    starts_with_dot_slash(url)) {
>  			char *oldurl = url;
> -			url = compute_submodule_clone_url(oldurl);
> +			url = compute_submodule_clone_url(oldurl, NULL, 0);
>  			free(oldurl);
>  		}
>  
> @@ -2134,7 +2138,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
>  	if (repo_config_get_string_tmp(the_repository, sb.buf, &url)) {
>  		if (starts_with_dot_slash(sub->url) ||
>  		    starts_with_dot_dot_slash(sub->url)) {
> -			url = compute_submodule_clone_url(sub->url);
> +			url = compute_submodule_clone_url(sub->url, NULL, 0);
>  			need_free_url = 1;
>  		} else
>  			url = sub->url;
All makes sense.
Atharva Raykar· Aug 5, 2021, 07:40 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v2 2/9] submodule--helper: refactor resolve_relative_url() helper

Refactor the helper function to resolve a relative url, by reusing the existing `compute_submodule_clone_url()` function.

`compute_submodule_clone_url()` performs the same work that `resolve_relative_url()` is doing, so we eliminate this code repetition by moving the former function's definition up, and calling it inside `resolve_relative_url()`.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 61 +++++++++++++++----------------------
 1 file changed, 25 insertions(+), 36 deletions(-)
Show changes to builtin/submodule--helper.c +25 −36
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 2d2d8ac637..f4b496bac6 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -199,33 +199,46 @@ static char *relative_url(const char *remote_url,
 	return strbuf_detach(&sb, NULL);
 }
 
+static char *compute_submodule_clone_url(const char *rel_url, const char *up_path, int quiet)
+{
+	char *remoteurl, *relurl;
+	char *remote = get_default_remote();
+	struct strbuf remotesb = STRBUF_INIT;
+
+	strbuf_addf(&remotesb, "remote.%s.url", remote);
+	if (git_config_get_string(remotesb.buf, &remoteurl)) {
+		if (!quiet)
+			warning(_("could not look up configuration '%s'. "
+				  "Assuming this repository is its own "
+				  "authoritative upstream."),
+				remotesb.buf);
+		remoteurl = xgetcwd();
+	}
+	relurl = relative_url(remoteurl, rel_url, up_path);
+
+	free(remote);
+	free(remoteurl);
+	strbuf_release(&remotesb);
+
+	return relurl;
+}
+
 static int resolve_relative_url(int argc, const char **argv, const char *prefix)
 {
-	char *remoteurl = NULL;
-	char *remote = get_default_remote();
 	const char *up_path = NULL;
 	char *res;
 	const char *url;
-	struct strbuf sb = STRBUF_INIT;
 
 	if (argc != 2 && argc != 3)
 		die("resolve-relative-url only accepts one or two arguments");
 
 	url = argv[1];
-	strbuf_addf(&sb, "remote.%s.url", remote);
-	free(remote);
-
-	if (git_config_get_string(sb.buf, &remoteurl))
-		/* the repository is its own authoritative upstream */
-		remoteurl = xgetcwd();
-
 	if (argc == 3)
 		up_path = argv[2];
 
-	res = relative_url(remoteurl, url, up_path);
+	res = compute_submodule_clone_url(url, up_path, 1);
 	puts(res);
 	free(res);
-	free(remoteurl);
 	return 0;
 }
 
@@ -590,30 +603,6 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
 	return 0;
 }
 
-static char *compute_submodule_clone_url(const char *rel_url, const char *up_path, int quiet)
-{
-	char *remoteurl, *relurl;
-	char *remote = get_default_remote();
-	struct strbuf remotesb = STRBUF_INIT;
-
-	strbuf_addf(&remotesb, "remote.%s.url", remote);
-	if (git_config_get_string(remotesb.buf, &remoteurl)) {
-		if (!quiet)
-			warning(_("could not look up configuration '%s'. "
-				  "Assuming this repository is its own "
-				  "authoritative upstream."),
-				remotesb.buf);
-		remoteurl = xgetcwd();
-	}
-	relurl = relative_url(remoteurl, rel_url, up_path);
-
-	free(remote);
-	free(remoteurl);
-	strbuf_release(&remotesb);
-
-	return relurl;
-}
-
 struct init_cb {
 	const char *prefix;
 	unsigned int flags;
-- 
2.32.0
Junio C Hamano· Aug 5, 2021, 20:13 UTC · re: Atharva Raykar · lore

Re: [GSoC] [PATCH v2 2/9] submodule--helper: refactor resolve_relative_url() helper

Atharva Raykar <raykar.ath@gmail.com> writes:
Show 7 quoted lines
> Refactor the helper function to resolve a relative url, by reusing the
> existing `compute_submodule_clone_url()` function.
>
> `compute_submodule_clone_url()` performs the same work that
> `resolve_relative_url()` is doing, so we eliminate this code repetition
> by moving the former function's definition up, and calling it inside
> `resolve_relative_url()`.
Looking good.
Show 6 quoted lines
> Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
> Mentored-by: Christian Couder <christian.couder@gmail.com>
> Mentored-by: Shourya Shukla <periperidip@gmail.com>
> ---
>  builtin/submodule--helper.c | 61 +++++++++++++++----------------------
>  1 file changed, 25 insertions(+), 36 deletions(-)

This step is easier to see with "show --patience --color-moved" to see that compute_submodule_clone_url() has only been moved without any change. And the actual change is much smaller than what the diffstat suggests above (11 lines lost, 1 line updated, if I counted correctly).

Nicely done.
Atharva Raykar· Aug 5, 2021, 07:40 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v2 3/9] submodule--helper: remove repeated code in sync_submodule()

This part of `sync_submodule()` is doing the same thing that `compute_submodule_clone_url()` is doing. Let's reuse that helper here.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 16 +++-------------
 1 file changed, 3 insertions(+), 13 deletions(-)
Show changes to builtin/submodule--helper.c +3 −13
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index f4b496bac6..9b676c12f8 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1373,20 +1373,10 @@ static void sync_submodule(const char *path, const char *prefix,
 	if (sub && sub->url) {
 		if (starts_with_dot_dot_slash(sub->url) ||
 		    starts_with_dot_slash(sub->url)) {
-			char *remote_url, *up_path;
-			char *remote = get_default_remote();
-			strbuf_addf(&sb, "remote.%s.url", remote);
-
-			if (git_config_get_string(sb.buf, &remote_url))
-				remote_url = xgetcwd();
-
-			up_path = get_up_path(path);
-			sub_origin_url = relative_url(remote_url, sub->url, up_path);
-			super_config_url = relative_url(remote_url, sub->url, NULL);
-
-			free(remote);
+			char *up_path = get_up_path(path);
+			sub_origin_url = compute_submodule_clone_url(sub->url, up_path, 1);
+			super_config_url = compute_submodule_clone_url(sub->url, NULL, 1);
 			free(up_path);
-			free(remote_url);
 		} else {
 			sub_origin_url = xstrdup(sub->url);
 			super_config_url = xstrdup(sub->url);
-- 
2.32.0
Junio C Hamano· Aug 5, 2021, 20:20 UTC · re: Atharva Raykar · lore

Re: [GSoC] [PATCH v2 3/9] submodule--helper: remove repeated code in sync_submodule()

Atharva Raykar <raykar.ath@gmail.com> writes:
Show 9 quoted lines
> This part of `sync_submodule()` is doing the same thing that
> `compute_submodule_clone_url()` is doing. Let's reuse that helper here.
>
> Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
> Mentored-by: Christian Couder <christian.couder@gmail.com>
> Mentored-by: Shourya Shukla <periperidip@gmail.com>
> ---
>  builtin/submodule--helper.c | 16 +++-------------
>  1 file changed, 3 insertions(+), 13 deletions(-)

The original used the result of get_default_remote() twice before freeing it, but now all that code to allocate and free remote is contained in compute_submodule_clone_url(), which mean swe allocate, use, and free twice. In exchange, our logic here works at a higher level of abstraction, which is a big plus.

Nice.
Show 28 quoted lines
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index f4b496bac6..9b676c12f8 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -1373,20 +1373,10 @@ static void sync_submodule(const char *path, const char *prefix,
>  	if (sub && sub->url) {
>  		if (starts_with_dot_dot_slash(sub->url) ||
>  		    starts_with_dot_slash(sub->url)) {
> -			char *remote_url, *up_path;
> -			char *remote = get_default_remote();
> -			strbuf_addf(&sb, "remote.%s.url", remote);
> -
> -			if (git_config_get_string(sb.buf, &remote_url))
> -				remote_url = xgetcwd();
> -
> -			up_path = get_up_path(path);
> -			sub_origin_url = relative_url(remote_url, sub->url, up_path);
> -			super_config_url = relative_url(remote_url, sub->url, NULL);
> -
> -			free(remote);
> +			char *up_path = get_up_path(path);
> +			sub_origin_url = compute_submodule_clone_url(sub->url, up_path, 1);
> +			super_config_url = compute_submodule_clone_url(sub->url, NULL, 1);
>  			free(up_path);
> -			free(remote_url);
>  		} else {
>  			sub_origin_url = xstrdup(sub->url);
>  			super_config_url = xstrdup(sub->url);
Atharva Raykar· Aug 5, 2021, 07:40 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v2 4/9] dir: libify and export helper functions from clone.c

These functions can be useful to other parts of Git. Let's move them to dir.c, while renaming them to be make their functionality more explicit.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <shouryashukla.oo@gmail.com>
---
 builtin/clone.c | 118 +-----------------------------------------------
 dir.c           | 114 ++++++++++++++++++++++++++++++++++++++++++++++
 dir.h           |   3 ++
 3 files changed, 119 insertions(+), 116 deletions(-)
Show changes to 3 files +119 −116

builtin/clone.c, dir.c, dir.h

diff --git a/builtin/clone.c b/builtin/clone.c
index 66fe66679c..b9b59a838f 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -217,120 +217,6 @@ static char *get_repo_path(const char *repo, int *is_bundle)
 	return canon;
 }
 
-static char *guess_dir_name(const char *repo, int is_bundle, int is_bare)
-{
-	const char *end = repo + strlen(repo), *start, *ptr;
-	size_t len;
-	char *dir;
-
-	/*
-	 * Skip scheme.
-	 */
-	start = strstr(repo, "://");
-	if (start == NULL)
-		start = repo;
-	else
-		start += 3;
-
-	/*
-	 * Skip authentication data. The stripping does happen
-	 * greedily, such that we strip up to the last '@' inside
-	 * the host part.
-	 */
-	for (ptr = start; ptr < end && !is_dir_sep(*ptr); ptr++) {
-		if (*ptr == '@')
-			start = ptr + 1;
-	}
-
-	/*
-	 * Strip trailing spaces, slashes and /.git
-	 */
-	while (start < end && (is_dir_sep(end[-1]) || isspace(end[-1])))
-		end--;
-	if (end - start > 5 && is_dir_sep(end[-5]) &&
-	    !strncmp(end - 4, ".git", 4)) {
-		end -= 5;
-		while (start < end && is_dir_sep(end[-1]))
-			end--;
-	}
-
-	/*
-	 * Strip trailing port number if we've got only a
-	 * hostname (that is, there is no dir separator but a
-	 * colon). This check is required such that we do not
-	 * strip URI's like '/foo/bar:2222.git', which should
-	 * result in a dir '2222' being guessed due to backwards
-	 * compatibility.
-	 */
-	if (memchr(start, '/', end - start) == NULL
-	    && memchr(start, ':', end - start) != NULL) {
-		ptr = end;
-		while (start < ptr && isdigit(ptr[-1]) && ptr[-1] != ':')
-			ptr--;
-		if (start < ptr && ptr[-1] == ':')
-			end = ptr - 1;
-	}
-
-	/*
-	 * Find last component. To remain backwards compatible we
-	 * also regard colons as path separators, such that
-	 * cloning a repository 'foo:bar.git' would result in a
-	 * directory 'bar' being guessed.
-	 */
-	ptr = end;
-	while (start < ptr && !is_dir_sep(ptr[-1]) && ptr[-1] != ':')
-		ptr--;
-	start = ptr;
-
-	/*
-	 * Strip .{bundle,git}.
-	 */
-	len = end - start;
-	strip_suffix_mem(start, &len, is_bundle ? ".bundle" : ".git");
-
-	if (!len || (len == 1 && *start == '/'))
-		die(_("No directory name could be guessed.\n"
-		      "Please specify a directory on the command line"));
-
-	if (is_bare)
-		dir = xstrfmt("%.*s.git", (int)len, start);
-	else
-		dir = xstrndup(start, len);
-	/*
-	 * Replace sequences of 'control' characters and whitespace
-	 * with one ascii space, remove leading and trailing spaces.
-	 */
-	if (*dir) {
-		char *out = dir;
-		int prev_space = 1 /* strip leading whitespace */;
-		for (end = dir; *end; ++end) {
-			char ch = *end;
-			if ((unsigned char)ch < '\x20')
-				ch = '\x20';
-			if (isspace(ch)) {
-				if (prev_space)
-					continue;
-				prev_space = 1;
-			} else
-				prev_space = 0;
-			*out++ = ch;
-		}
-		*out = '\0';
-		if (out > dir && prev_space)
-			out[-1] = '\0';
-	}
-	return dir;
-}
-
-static void strip_trailing_slashes(char *dir)
-{
-	char *end = dir + strlen(dir);
-
-	while (dir < end - 1 && is_dir_sep(end[-1]))
-		end--;
-	*end = '\0';
-}
-
 static int add_one_reference(struct string_list_item *item, void *cb_data)
 {
 	struct strbuf err = STRBUF_INIT;
@@ -1041,8 +927,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 	if (argc == 2)
 		dir = xstrdup(argv[1]);
 	else
-		dir = guess_dir_name(repo_name, is_bundle, option_bare);
-	strip_trailing_slashes(dir);
+		dir = guess_dir_name_from_git_url(repo_name, is_bundle, option_bare);
+	strip_dir_trailing_slashes(dir);
 
 	dest_exists = path_exists(dir);
 	if (dest_exists && !is_empty_dir(dir))
diff --git a/dir.c b/dir.c
index 03c4d21267..84b47c4dbc 100644
--- a/dir.c
+++ b/dir.c
@@ -2970,6 +2970,120 @@ int is_empty_dir(const char *path)
 	return ret;
 }
 
+char *guess_dir_name_from_git_url(const char *repo, int is_bundle, int is_bare)
+{
+	const char *end = repo + strlen(repo), *start, *ptr;
+	size_t len;
+	char *dir;
+
+	/*
+	 * Skip scheme.
+	 */
+	start = strstr(repo, "://");
+	if (start == NULL)
+		start = repo;
+	else
+		start += 3;
+
+	/*
+	 * Skip authentication data. The stripping does happen
+	 * greedily, such that we strip up to the last '@' inside
+	 * the host part.
+	 */
+	for (ptr = start; ptr < end && !is_dir_sep(*ptr); ptr++) {
+		if (*ptr == '@')
+			start = ptr + 1;
+	}
+
+	/*
+	 * Strip trailing spaces, slashes and /.git
+	 */
+	while (start < end && (is_dir_sep(end[-1]) || isspace(end[-1])))
+		end--;
+	if (end - start > 5 && is_dir_sep(end[-5]) &&
+	    !strncmp(end - 4, ".git", 4)) {
+		end -= 5;
+		while (start < end && is_dir_sep(end[-1]))
+			end--;
+	}
+
+	/*
+	 * Strip trailing port number if we've got only a
+	 * hostname (that is, there is no dir separator but a
+	 * colon). This check is required such that we do not
+	 * strip URI's like '/foo/bar:2222.git', which should
+	 * result in a dir '2222' being guessed due to backwards
+	 * compatibility.
+	 */
+	if (memchr(start, '/', end - start) == NULL
+	    && memchr(start, ':', end - start) != NULL) {
+		ptr = end;
+		while (start < ptr && isdigit(ptr[-1]) && ptr[-1] != ':')
+			ptr--;
+		if (start < ptr && ptr[-1] == ':')
+			end = ptr - 1;
+	}
+
+	/*
+	 * Find last component. To remain backwards compatible we
+	 * also regard colons as path separators, such that
+	 * cloning a repository 'foo:bar.git' would result in a
+	 * directory 'bar' being guessed.
+	 */
+	ptr = end;
+	while (start < ptr && !is_dir_sep(ptr[-1]) && ptr[-1] != ':')
+		ptr--;
+	start = ptr;
+
+	/*
+	 * Strip .{bundle,git}.
+	 */
+	len = end - start;
+	strip_suffix_mem(start, &len, is_bundle ? ".bundle" : ".git");
+
+	if (!len || (len == 1 && *start == '/'))
+		die(_("No directory name could be guessed.\n"
+		      "Please specify a directory on the command line"));
+
+	if (is_bare)
+		dir = xstrfmt("%.*s.git", (int)len, start);
+	else
+		dir = xstrndup(start, len);
+	/*
+	 * Replace sequences of 'control' characters and whitespace
+	 * with one ascii space, remove leading and trailing spaces.
+	 */
+	if (*dir) {
+		char *out = dir;
+		int prev_space = 1 /* strip leading whitespace */;
+		for (end = dir; *end; ++end) {
+			char ch = *end;
+			if ((unsigned char)ch < '\x20')
+				ch = '\x20';
+			if (isspace(ch)) {
+				if (prev_space)
+					continue;
+				prev_space = 1;
+			} else
+				prev_space = 0;
+			*out++ = ch;
+		}
+		*out = '\0';
+		if (out > dir && prev_space)
+			out[-1] = '\0';
+	}
+	return dir;
+}
+
+void strip_dir_trailing_slashes(char *dir)
+{
+	char *end = dir + strlen(dir);
+
+	while (dir < end - 1 && is_dir_sep(end[-1]))
+		end--;
+	*end = '\0';
+}
+
 static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up)
 {
 	DIR *dir;
diff --git a/dir.h b/dir.h
index b3e1a54a97..76441dde2d 100644
--- a/dir.h
+++ b/dir.h
@@ -453,6 +453,9 @@ static inline int is_dot_or_dotdot(const char *name)
 
 int is_empty_dir(const char *dir);
 
+char *guess_dir_name_from_git_url(const char *repo, int is_bundle, int is_bare);
+void strip_dir_trailing_slashes(char *dir);
+
 void setup_standard_excludes(struct dir_struct *dir);
 
 char *get_sparse_checkout_filename(void);
-- 
2.32.0
Junio C Hamano· Aug 5, 2021, 20:37 UTC · re: Atharva Raykar · lore

Re: [GSoC] [PATCH v2 4/9] dir: libify and export helper functions from clone.c

Atharva Raykar <raykar.ath@gmail.com> writes:
> These functions can be useful to other parts of Git. Let's move them to
> dir.c, while renaming them to be make their functionality more explicit.

Hmph, guess_dir_name_from_git_url() is not all that more clarifying than the original, at least to me. For a file-scope static helper, it probably was good enough with a short name with no function doc, but we should describe what it does in comments in dir.h and come up with a suitable name, taking input from that description.

Show 8 quoted lines
> Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
> Mentored-by: Christian Couder <christian.couder@gmail.com>
> Mentored-by: Shourya Shukla <shouryashukla.oo@gmail.com>
> ---
>  builtin/clone.c | 118 +-----------------------------------------------
>  dir.c           | 114 ++++++++++++++++++++++++++++++++++++++++++++++
>  dir.h           |   3 ++
>  3 files changed, 119 insertions(+), 116 deletions(-)

Again "show --color-moved" helps to see that these two helper functions are moved across files without any change other than the names, which is good.

Show 8 quoted lines
> @@ -1041,8 +927,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
>  	if (argc == 2)
>  		dir = xstrdup(argv[1]);
>  	else
> -		dir = guess_dir_name(repo_name, is_bundle, option_bare);
> -	strip_trailing_slashes(dir);
> +		dir = guess_dir_name_from_git_url(repo_name, is_bundle, option_bare);
> +	strip_dir_trailing_slashes(dir);

So, what does this new public helper function guess? The name of the function says it guesses a directory name, but it is not just any directory name, but a directory with some specific meaning.

Here repo_name has the URL the user gave "git clone", and even though there are some code before this part that computed on the variable, it hasn't been modified. And "from_git_url" is a good way to indicate that that is one of the input the function uses to guess the name of "the directory with some unknown specific meaning".

I think this codepath wants the new directory to create as the result of "git clone" operation in "dir". So, even though I still do not have a good answer to the earlier "this is not just any directory but with some specific meaning---what is it?" question, adjectives that are appropriate for the "directory" to answer it may be along the lines of "new", "resulting", "cloned", etc.

Show 8 quoted lines
> diff --git a/dir.h b/dir.h
> index b3e1a54a97..76441dde2d 100644
> --- a/dir.h
> +++ b/dir.h
> @@ -453,6 +453,9 @@ static inline int is_dot_or_dotdot(const char *name)
>  
>  int is_empty_dir(const char *dir);
>  

We would want docstring here for the function (and possibly rename the function to clarify what kind of "dir" we are talking about).

Show 6 quoted lines
> +char *guess_dir_name_from_git_url(const char *repo, int is_bundle, int is_bare);
> +void strip_dir_trailing_slashes(char *dir);
> +
>  void setup_standard_excludes(struct dir_struct *dir);
>  
>  char *get_sparse_checkout_filename(void);
Thanks.
Atharva Raykar· Aug 6, 2021, 11:12 UTC · re: Junio C Hamano · lore

Re: [GSoC] [PATCH v2 4/9] dir: libify and export helper functions from clone.c

Junio C Hamano <gitster@pobox.com> writes:
Show 38 quoted lines
> Atharva Raykar <raykar.ath@gmail.com> writes:
>
>> These functions can be useful to other parts of Git. Let's move them to
>> dir.c, while renaming them to be make their functionality more explicit.
>
> Hmph, guess_dir_name_from_git_url() is not all that more clarifying
> than the original, at least to me.  For a file-scope static helper,
> it probably was good enough with a short name with no function doc,
> but we should describe what it does in comments in dir.h and come up
> with a suitable name, taking input from that description.
>
> [...]
>
>> @@ -1041,8 +927,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
>>  	if (argc == 2)
>>  		dir = xstrdup(argv[1]);
>>  	else
>> -		dir = guess_dir_name(repo_name, is_bundle, option_bare);
>> -	strip_trailing_slashes(dir);
>> +		dir = guess_dir_name_from_git_url(repo_name, is_bundle, option_bare);
>> +	strip_dir_trailing_slashes(dir);
>
> So, what does this new public helper function guess?  The name of
> the function says it guesses a directory name, but it is not just
> any directory name, but a directory with some specific meaning.
>
> Here repo_name has the URL the user gave "git clone", and even
> though there are some code before this part that computed on the
> variable, it hasn't been modified.  And "from_git_url" is a good way
> to indicate that that is one of the input the function uses to guess
> the name of "the directory with some unknown specific meaning".
>
> I think this codepath wants the new directory to create as the
> result of "git clone" operation in "dir".  So, even though I still
> do not have a good answer to the earlier "this is not just any
> directory but with some specific meaning---what is it?" question,
> adjectives that are appropriate for the "directory" to answer it
> may be along the lines of "new", "resulting", "cloned", etc.
Naming things is hard...

Maybe the right phrase would be 'target directory'? We are creating a target directory name by looking at the "humanish" part of the Git URL.

I think the intention of all callers of this function is to get a "default" directory name which will be used as the target of some operation in the absence of the user providing one.

So maybe the name could be: 'guess_target_dir_from_git_url()'

This would make sense for any operation now or in the future that wants to reuse this functionality.

Show 11 quoted lines
>> diff --git a/dir.h b/dir.h
>> index b3e1a54a97..76441dde2d 100644
>> --- a/dir.h
>> +++ b/dir.h
>> @@ -453,6 +453,9 @@ static inline int is_dot_or_dotdot(const char *name)
>>
>>  int is_empty_dir(const char *dir);
>>
>
> We would want docstring here for the function (and possibly rename
> the function to clarify what kind of "dir" we are talking about).
Okay, I will add it.
Show 8 quoted lines
>> +char *guess_dir_name_from_git_url(const char *repo, int is_bundle, int is_bare);
>> +void strip_dir_trailing_slashes(char *dir);
>> +
>>  void setup_standard_excludes(struct dir_struct *dir);
>>
>>  char *get_sparse_checkout_filename(void);
>
> Thanks.
Junio C Hamano· Aug 6, 2021, 16:36 UTC · re: Atharva Raykar · lore

Re: [GSoC] [PATCH v2 4/9] dir: libify and export helper functions from clone.c

Atharva Raykar <raykar.ath@gmail.com> writes:
> Naming things is hard...
Absolutely.
Show 8 quoted lines
> Maybe the right phrase would be 'target directory'? We are creating a
> target directory name by looking at the "humanish" part of the Git URL.
>
> I think the intention of all callers of this function is to get a
> "default" directory name which will be used as the target of some
> operation in the absence of the user providing one.
>
> So maybe the name could be: 'guess_target_dir_from_git_url()'
I have no immediate objection to the name.  

Just to see how people (including you) may react to a name from a completely different line of thinking, let me throw this, though.

How does git_url_basename() sound?  

Instead of saying what we'd use it for (i.e. as the name for the directory getting created), we say what we compute. We take a URL-looking thing that is used by Git, and we compute something like basename() but that is tailored for Git (e.g. unlike "basename a/b/c.git" that yields "c.git", we give "c" for "a/b/c.git". Likewise "<scheme>://a/b/c/.git" won't yield ".git", we compute "c").

Having said that, I think guess_target_dir_from_git_url() is clear enough.

> This would make sense for any operation now or in the future that wants
> to reuse this functionality.

That is mostly for you to decide. I can help you sanity check the proposed name(s) with existing callers, but you'd be a better judge for callers you'll be adding ;-)

Atharva Raykar· Aug 7, 2021, 07:15 UTC · re: Junio C Hamano · lore

Re: [GSoC] [PATCH v2 4/9] dir: libify and export helper functions from clone.c

Junio C Hamano <gitster@pobox.com> writes:
Show 32 quoted lines
> Atharva Raykar <raykar.ath@gmail.com> writes:
>
>> Naming things is hard...
>
> Absolutely.
>
>> Maybe the right phrase would be 'target directory'? We are creating a
>> target directory name by looking at the "humanish" part of the Git URL.
>>
>> I think the intention of all callers of this function is to get a
>> "default" directory name which will be used as the target of some
>> operation in the absence of the user providing one.
>>
>> So maybe the name could be: 'guess_target_dir_from_git_url()'
>
> I have no immediate objection to the name.
>
> Just to see how people (including you) may react to a name from a
> completely different line of thinking, let me throw this, though.
>
> How does git_url_basename() sound?
>
> Instead of saying what we'd use it for (i.e. as the name for the
> directory getting created), we say what we compute.  We take a
> URL-looking thing that is used by Git, and we compute something like
> basename() but that is tailored for Git (e.g. unlike "basename
> a/b/c.git" that yields "c.git", we give "c" for "a/b/c.git".
> Likewise "<scheme>://a/b/c/.git" won't yield ".git", we compute
> "c").
>
> Having said that, I think guess_target_dir_from_git_url() is clear
> enough.

Even though the name I suggested is clear enough, I liked your suggestion a lot more. Not only is it more succinct, but it also opens up the future possibility that the basename of a Git URL might be used for purposes other than finding a directory name.

Show 6 quoted lines
>> This would make sense for any operation now or in the future that wants
>> to reuse this functionality.
>
> That is mostly for you to decide.   I can help you sanity check the
> proposed name(s) with existing callers, but you'd be a better judge
> for callers you'll be adding ;-)
Atharva Raykar· Aug 5, 2021, 07:40 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v2 5/9] submodule--helper: remove constness of sm_path

This is needed so that it can be modified by normalize_path_copy() in the next patch.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
Show changes to builtin/submodule--helper.c +1 −1
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 9b676c12f8..99aabf1078 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2748,7 +2748,7 @@ struct add_data {
 	const char *prefix;
 	const char *branch;
 	const char *reference_path;
-	const char *sm_path;
+	char *sm_path;
 	const char *sm_name;
 	const char *repo;
 	const char *realrepo;
-- 
2.32.0
Junio C Hamano· Aug 5, 2021, 20:40 UTC · re: Atharva Raykar · lore

Re: [GSoC] [PATCH v2 5/9] submodule--helper: remove constness of sm_path

Atharva Raykar <raykar.ath@gmail.com> writes:
> This is needed so that it can be modified by normalize_path_copy() in
> the next patch.

This is a hard-to-judge change. With this alone, we cannot tell if somebody has already looked at the member (and possibly saved the pointer elsewhere) before "the next patch" starts modifying the member in the struct. It probably should be done in a single patch to demonstrate why it is needed and how existing users of the field are OK with this change.

Show 21 quoted lines
>
> Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
> Mentored-by: Christian Couder <christian.couder@gmail.com>
> Mentored-by: Shourya Shukla <periperidip@gmail.com>
> ---
>  builtin/submodule--helper.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index 9b676c12f8..99aabf1078 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -2748,7 +2748,7 @@ struct add_data {
>  	const char *prefix;
>  	const char *branch;
>  	const char *reference_path;
> -	const char *sm_path;
> +	char *sm_path;
>  	const char *sm_name;
>  	const char *repo;
>  	const char *realrepo;
Atharva Raykar· Aug 6, 2021, 11:16 UTC · re: Junio C Hamano · lore

Re: [GSoC] [PATCH v2 5/9] submodule--helper: remove constness of sm_path

Junio C Hamano <gitster@pobox.com> writes:
Show 11 quoted lines
> Atharva Raykar <raykar.ath@gmail.com> writes:
>
>> This is needed so that it can be modified by normalize_path_copy() in
>> the next patch.
>
> This is a hard-to-judge change.  With this alone, we cannot tell if
> somebody has already looked at the member (and possibly saved the
> pointer elsewhere) before "the next patch" starts modifying the
> member in the struct.  It probably should be done in a single patch
> to demonstrate why it is needed and how existing users of the field
> are OK with this change.
Okay, I will move this to the next patch.
Atharva Raykar· Aug 5, 2021, 07:40 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v2 6/9] submodule--helper: convert the bulk of cmd_add() to C

Introduce the 'add' subcommand to `submodule--helper.c` that does all the work 'submodule add' past the parsing of flags.

As with the previous conversions, this is meant to be a faithful conversion with no modification to the behaviour of `submodule add`.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Helped-by: Kaartic Sivaraam <kaartic.sivaraam@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
Based-on-patch-by: Shourya Shukla <periperidip@gmail.com>
Based-on-patch-by: Prathamesh Chavan <pc44800@gmail.com>
---
 builtin/submodule--helper.c | 160 ++++++++++++++++++++++++++++++++++++
 git-submodule.sh            |  96 +---------------------
 2 files changed, 162 insertions(+), 94 deletions(-)
Show changes to 2 files +162 −94

builtin/submodule--helper.c, git-submodule.sh

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 99aabf1078..05ae9ebe50 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -3046,6 +3046,165 @@ static int add_config(int argc, const char **argv, const char *prefix)
 	return 0;
 }
 
+static void die_on_index_match(const char *path, int force)
+{
+	struct pathspec ps;
+	const char *args[] = { path, NULL };
+	parse_pathspec(&ps, 0, PATHSPEC_PREFER_CWD, NULL, args);
+
+	if (read_cache_preload(NULL) < 0)
+		die(_("index file corrupt"));
+
+	if (ps.nr) {
+		int i;
+		char *ps_matched = xcalloc(ps.nr, 1);
+
+		/* TODO: audit for interaction with sparse-index. */
+		ensure_full_index(&the_index);
+
+		/*
+		 * Since there is only one pathspec, we just need
+		 * need to check ps_matched[0] to know if a cache
+		 * entry matched.
+		 */
+		for (i = 0; i < active_nr; i++) {
+			ce_path_match(&the_index, active_cache[i], &ps,
+				      ps_matched);
+
+			if (ps_matched[0]) {
+				if (!force)
+					die(_("'%s' already exists in the index"),
+					    path);
+				if (!S_ISGITLINK(active_cache[i]->ce_mode))
+					die(_("'%s' already exists in the index "
+					      "and is not a submodule"), path);
+				break;
+			}
+		}
+		free(ps_matched);
+	}
+}
+
+static void die_on_repo_without_commits(const char *path)
+{
+	struct strbuf sb = STRBUF_INIT;
+	strbuf_addstr(&sb, path);
+	if (is_nonbare_repository_dir(&sb)) {
+		struct object_id oid;
+		if (resolve_gitlink_ref(path, "HEAD", &oid) < 0)
+			die(_("'%s' does not have a commit checked out"), path);
+	}
+}
+
+static int module_add(int argc, const char **argv, const char *prefix)
+{
+	int force = 0, quiet = 0, progress = 0, dissociate = 0;
+	struct add_data add_data = ADD_DATA_INIT;
+
+	struct option options[] = {
+		OPT_STRING('b', "branch", &add_data.branch, N_("branch"),
+			   N_("branch of repository to add as submodule")),
+		OPT__FORCE(&force, N_("allow adding an otherwise ignored submodule path"),
+			   PARSE_OPT_NOCOMPLETE),
+		OPT__QUIET(&quiet, N_("print only error messages")),
+		OPT_BOOL(0, "progress", &progress, N_("force cloning progress")),
+		OPT_STRING(0, "reference", &add_data.reference_path, N_("repository"),
+			   N_("reference repository")),
+		OPT_BOOL(0, "dissociate", &dissociate, N_("borrow the objects from reference repositories")),
+		OPT_STRING(0, "name", &add_data.sm_name, N_("name"),
+			   N_("sets the submodule’s name to the given string "
+			      "instead of defaulting to its path")),
+		OPT_INTEGER(0, "depth", &add_data.depth, N_("depth for shallow clones")),
+		OPT_END()
+	};
+
+	const char *const usage[] = {
+		N_("git submodule--helper add [<options>] [--] <repository> [<path>]"),
+		NULL
+	};
+
+	argc = parse_options(argc, argv, prefix, options, usage, 0);
+
+	if (!is_writing_gitmodules_ok())
+		die(_("please make sure that the .gitmodules file is in the working tree"));
+
+	if (prefix && *prefix &&
+	    add_data.reference_path && !is_absolute_path(add_data.reference_path))
+		add_data.reference_path = xstrfmt("%s%s", prefix, add_data.reference_path);
+
+	if (argc == 0 || argc > 2)
+		usage_with_options(usage, options);
+
+	add_data.repo = argv[0];
+	if (argc == 1)
+		add_data.sm_path = guess_dir_name_from_git_url(add_data.repo, 0, 0);
+	else
+		add_data.sm_path = xstrdup(argv[1]);
+
+	if (prefix && *prefix && !is_absolute_path(add_data.sm_path))
+		add_data.sm_path = xstrfmt("%s%s", prefix, add_data.sm_path);
+
+	if (starts_with_dot_dot_slash(add_data.repo) ||
+	    starts_with_dot_slash(add_data.repo)) {
+		if (prefix)
+			die(_("Relative path can only be used from the toplevel "
+			      "of the working tree"));
+
+		/* dereference source url relative to parent's url */
+		add_data.realrepo = compute_submodule_clone_url(add_data.repo, NULL, 1);
+	} else if (is_dir_sep(add_data.repo[0]) || strchr(add_data.repo, ':')) {
+		add_data.realrepo = add_data.repo;
+	} else {
+		die(_("repo URL: '%s' must be absolute or begin with ./|../"),
+		    add_data.repo);
+	}
+
+	/*
+	 * normalize path:
+	 * multiple //; leading ./; /./; /../;
+	 */
+	normalize_path_copy(add_data.sm_path, add_data.sm_path);
+	strip_dir_trailing_slashes(add_data.sm_path);
+
+	die_on_index_match(add_data.sm_path, force);
+	die_on_repo_without_commits(add_data.sm_path);
+
+	if (!force) {
+		int exit_code = -1;
+		struct strbuf sb = STRBUF_INIT;
+		struct child_process cp = CHILD_PROCESS_INIT;
+		cp.git_cmd = 1;
+		cp.no_stdout = 1;
+		strvec_pushl(&cp.args, "add", "--dry-run", "--ignore-missing",
+			     "--no-warn-embedded-repo", add_data.sm_path, NULL);
+		if ((exit_code = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {
+			strbuf_complete_line(&sb);
+			fputs(sb.buf, stderr);
+			return exit_code;
+		}
+		strbuf_release(&sb);
+	}
+
+	if(!add_data.sm_name)
+		add_data.sm_name = add_data.sm_path;
+
+	if (check_submodule_name(add_data.sm_name))
+		die(_("'%s' is not a valid submodule name"), add_data.sm_name);
+
+	add_data.prefix = prefix;
+	add_data.force = !!force;
+	add_data.quiet = !!quiet;
+	add_data.progress = !!progress;
+	add_data.dissociate = !!dissociate;
+
+	if (add_submodule(&add_data))
+		return 1;
+	configure_added_submodule(&add_data);
+	free(add_data.sm_path);
+
+	return 0;
+}
+
 #define SUPPORT_SUPER_PREFIX (1<<0)
 
 struct cmd_struct {
@@ -3060,6 +3219,7 @@ static struct cmd_struct commands[] = {
 	{"clone", module_clone, 0},
 	{"add-clone", add_clone, 0},
 	{"add-config", add_config, 0},
+	{"add", module_add, SUPPORT_SUPER_PREFIX},
 	{"update-module-mode", module_update_module_mode, 0},
 	{"update-clone", update_clone, 0},
 	{"ensure-core-worktree", ensure_core_worktree, 0},
diff --git a/git-submodule.sh b/git-submodule.sh
index 8c219ef382..1070540525 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -145,104 +145,12 @@ cmd_add()
 		shift
 	done
 
-	if ! git submodule--helper config --check-writeable >/dev/null 2>&1
+	if test -z "$1"
 	then
-		 die "fatal: $(eval_gettext "please make sure that the .gitmodules file is in the working tree")"
-	fi
-
-	if test -n "$reference_path"
-	then
-		is_absolute_path "$reference_path" ||
-		reference_path="$wt_prefix$reference_path"
-
-		reference="--reference=$reference_path"
-	fi
-
-	repo=$1
-	sm_path=$2
-
-	if test -z "$sm_path"; then
-		sm_path=$(printf '%s\n' "$repo" |
-			sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
-	fi
-
-	if test -z "$repo" || test -z "$sm_path"; then
 		usage
 	fi
 
-	is_absolute_path "$sm_path" || sm_path="$wt_prefix$sm_path"
-
-	# assure repo is absolute or relative to parent
-	case "$repo" in
-	./*|../*)
-		test -z "$wt_prefix" ||
-		die "fatal: $(gettext "Relative path can only be used from the toplevel of the working tree")"
-
-		# dereference source url relative to parent's url
-		realrepo=$(git submodule--helper resolve-relative-url "$repo") || exit
-		;;
-	*:*|/*)
-		# absolute url
-		realrepo=$repo
-		;;
-	*)
-		die "fatal: $(eval_gettext "repo URL: '\$repo' must be absolute or begin with ./|../")"
-	;;
-	esac
-
-	# normalize path:
-	# multiple //; leading ./; /./; /../; trailing /
-	sm_path=$(printf '%s/\n' "$sm_path" |
-		sed -e '
-			s|//*|/|g
-			s|^\(\./\)*||
-			s|/\(\./\)*|/|g
-			:start
-			s|\([^/]*\)/\.\./||
-			tstart
-			s|/*$||
-		')
-	if test -z "$force"
-	then
-		git ls-files --error-unmatch "$sm_path" > /dev/null 2>&1 &&
-		die "fatal: $(eval_gettext "'\$sm_path' already exists in the index")"
-	else
-		git ls-files -s "$sm_path" | sane_grep -v "^160000" > /dev/null 2>&1 &&
-		die "fatal: $(eval_gettext "'\$sm_path' already exists in the index and is not a submodule")"
-	fi
-
-	if test -d "$sm_path" &&
-		test -z $(git -C "$sm_path" rev-parse --show-cdup 2>/dev/null)
-	then
-	    git -C "$sm_path" rev-parse --verify -q HEAD >/dev/null ||
-	    die "fatal: $(eval_gettext "'\$sm_path' does not have a commit checked out")"
-	fi
-
-	if test -z "$force"
-	then
-	    dryerr=$(git add --dry-run --ignore-missing --no-warn-embedded-repo "$sm_path" 2>&1 >/dev/null)
-	    res=$?
-	    if test $res -ne 0
-	    then
-		 echo >&2 "$dryerr"
-		 exit $res
-	    fi
-	fi
-
-	if test -n "$custom_name"
-	then
-		sm_name="$custom_name"
-	else
-		sm_name="$sm_path"
-	fi
-
-	if ! git submodule--helper check-name "$sm_name"
-	then
-		die "fatal: $(eval_gettext "'$sm_name' is not a valid submodule name")"
-	fi
-
-	git submodule--helper add-clone ${GIT_QUIET:+--quiet} ${force:+"--force"} ${progress:+"--progress"} ${branch:+--branch "$branch"} --prefix "$wt_prefix" --path "$sm_path" --name "$sm_name" --url "$realrepo" ${reference:+"$reference"} ${dissociate:+"--dissociate"} ${depth:+"$depth"} || exit
-	git submodule--helper add-config ${force:+--force} ${branch:+--branch "$branch"} --url "$repo" --resolved-url "$realrepo" --path "$sm_path" --name "$sm_name"
+	git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper add ${GIT_QUIET:+--quiet} ${force:+--force} ${progress:+"--progress"} ${branch:+--branch "$branch"} ${reference_path:+--reference "$reference_path"} ${dissociate:+--dissociate} ${custom_name:+--name "$custom_name"} ${depth:+"$depth"} -- "$@"
 }
 
 #
-- 
2.32.0
Đoàn Trần Công Danh· Aug 6, 2021, 01:14 UTC · re: Atharva Raykar · lore

Re: [GSoC] [PATCH v2 6/9] submodule--helper: convert the bulk of cmd_add() to C

On 2021-08-05 13:10:51+0530, Atharva Raykar <raykar.ath@gmail.com> wrote:
Show 119 quoted lines
> Introduce the 'add' subcommand to `submodule--helper.c` that does all
> the work 'submodule add' past the parsing of flags.
> 
> As with the previous conversions, this is meant to be a faithful
> conversion with no modification to the behaviour of `submodule add`.
> 
> Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
> Mentored-by: Christian Couder <christian.couder@gmail.com>
> Helped-by: Kaartic Sivaraam <kaartic.sivaraam@gmail.com>
> Mentored-by: Shourya Shukla <periperidip@gmail.com>
> Based-on-patch-by: Shourya Shukla <periperidip@gmail.com>
> Based-on-patch-by: Prathamesh Chavan <pc44800@gmail.com>
> ---
>  builtin/submodule--helper.c | 160 ++++++++++++++++++++++++++++++++++++
>  git-submodule.sh            |  96 +---------------------
>  2 files changed, 162 insertions(+), 94 deletions(-)
> 
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index 99aabf1078..05ae9ebe50 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -3046,6 +3046,165 @@ static int add_config(int argc, const char **argv, const char *prefix)
>  	return 0;
>  }
>  
> +static void die_on_index_match(const char *path, int force)
> +{
> +	struct pathspec ps;
> +	const char *args[] = { path, NULL };
> +	parse_pathspec(&ps, 0, PATHSPEC_PREFER_CWD, NULL, args);
> +
> +	if (read_cache_preload(NULL) < 0)
> +		die(_("index file corrupt"));
> +
> +	if (ps.nr) {
> +		int i;
> +		char *ps_matched = xcalloc(ps.nr, 1);
> +
> +		/* TODO: audit for interaction with sparse-index. */
> +		ensure_full_index(&the_index);
> +
> +		/*
> +		 * Since there is only one pathspec, we just need
> +		 * need to check ps_matched[0] to know if a cache
> +		 * entry matched.
> +		 */
> +		for (i = 0; i < active_nr; i++) {
> +			ce_path_match(&the_index, active_cache[i], &ps,
> +				      ps_matched);
> +
> +			if (ps_matched[0]) {
> +				if (!force)
> +					die(_("'%s' already exists in the index"),
> +					    path);
> +				if (!S_ISGITLINK(active_cache[i]->ce_mode))
> +					die(_("'%s' already exists in the index "
> +					      "and is not a submodule"), path);
> +				break;
> +			}
> +		}
> +		free(ps_matched);
> +	}
> +}
> +
> +static void die_on_repo_without_commits(const char *path)
> +{
> +	struct strbuf sb = STRBUF_INIT;
> +	strbuf_addstr(&sb, path);
> +	if (is_nonbare_repository_dir(&sb)) {
> +		struct object_id oid;
> +		if (resolve_gitlink_ref(path, "HEAD", &oid) < 0)
> +			die(_("'%s' does not have a commit checked out"), path);
> +	}
> +}
> +
> +static int module_add(int argc, const char **argv, const char *prefix)
> +{
> +	int force = 0, quiet = 0, progress = 0, dissociate = 0;
> +	struct add_data add_data = ADD_DATA_INIT;
> +
> +	struct option options[] = {
> +		OPT_STRING('b', "branch", &add_data.branch, N_("branch"),
> +			   N_("branch of repository to add as submodule")),
> +		OPT__FORCE(&force, N_("allow adding an otherwise ignored submodule path"),
> +			   PARSE_OPT_NOCOMPLETE),
> +		OPT__QUIET(&quiet, N_("print only error messages")),
> +		OPT_BOOL(0, "progress", &progress, N_("force cloning progress")),
> +		OPT_STRING(0, "reference", &add_data.reference_path, N_("repository"),
> +			   N_("reference repository")),
> +		OPT_BOOL(0, "dissociate", &dissociate, N_("borrow the objects from reference repositories")),
> +		OPT_STRING(0, "name", &add_data.sm_name, N_("name"),
> +			   N_("sets the submodule’s name to the given string "
> +			      "instead of defaulting to its path")),
> +		OPT_INTEGER(0, "depth", &add_data.depth, N_("depth for shallow clones")),
> +		OPT_END()
> +	};
> +
> +	const char *const usage[] = {
> +		N_("git submodule--helper add [<options>] [--] <repository> [<path>]"),
> +		NULL
> +	};
> +
> +	argc = parse_options(argc, argv, prefix, options, usage, 0);
> +
> +	if (!is_writing_gitmodules_ok())
> +		die(_("please make sure that the .gitmodules file is in the working tree"));
> +
> +	if (prefix && *prefix &&
> +	    add_data.reference_path && !is_absolute_path(add_data.reference_path))
> +		add_data.reference_path = xstrfmt("%s%s", prefix, add_data.reference_path);
> +
> +	if (argc == 0 || argc > 2)
> +		usage_with_options(usage, options);
> +
> +	add_data.repo = argv[0];
> +	if (argc == 1)
> +		add_data.sm_path = guess_dir_name_from_git_url(add_data.repo, 0, 0);
> +	else
> +		add_data.sm_path = xstrdup(argv[1]);
add_data.sm_path is allocated in this block (regardless of legs).
Show 40 quoted lines
> +	if (prefix && *prefix && !is_absolute_path(add_data.sm_path))
> +		add_data.sm_path = xstrfmt("%s%s", prefix, add_data.sm_path);
> +
> +	if (starts_with_dot_dot_slash(add_data.repo) ||
> +	    starts_with_dot_slash(add_data.repo)) {
> +		if (prefix)
> +			die(_("Relative path can only be used from the toplevel "
> +			      "of the working tree"));
> +
> +		/* dereference source url relative to parent's url */
> +		add_data.realrepo = compute_submodule_clone_url(add_data.repo, NULL, 1);
> +	} else if (is_dir_sep(add_data.repo[0]) || strchr(add_data.repo, ':')) {
> +		add_data.realrepo = add_data.repo;
> +	} else {
> +		die(_("repo URL: '%s' must be absolute or begin with ./|../"),
> +		    add_data.repo);
> +	}
> +
> +	/*
> +	 * normalize path:
> +	 * multiple //; leading ./; /./; /../;
> +	 */
> +	normalize_path_copy(add_data.sm_path, add_data.sm_path);
> +	strip_dir_trailing_slashes(add_data.sm_path);
> +
> +	die_on_index_match(add_data.sm_path, force);
> +	die_on_repo_without_commits(add_data.sm_path);
> +
> +	if (!force) {
> +		int exit_code = -1;
> +		struct strbuf sb = STRBUF_INIT;
> +		struct child_process cp = CHILD_PROCESS_INIT;
> +		cp.git_cmd = 1;
> +		cp.no_stdout = 1;
> +		strvec_pushl(&cp.args, "add", "--dry-run", "--ignore-missing",
> +			     "--no-warn-embedded-repo", add_data.sm_path, NULL);
> +		if ((exit_code = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {
> +			strbuf_complete_line(&sb);
> +			fputs(sb.buf, stderr);
> +			return exit_code;
But, we don't free it when return from here.
Show 18 quoted lines
> +		}
> +		strbuf_release(&sb);
> +	}
> +
> +	if(!add_data.sm_name)
> +		add_data.sm_name = add_data.sm_path;
> +
> +	if (check_submodule_name(add_data.sm_name))
> +		die(_("'%s' is not a valid submodule name"), add_data.sm_name);
> +
> +	add_data.prefix = prefix;
> +	add_data.force = !!force;
> +	add_data.quiet = !!quiet;
> +	add_data.progress = !!progress;
> +	add_data.dissociate = !!dissociate;
> +
> +	if (add_submodule(&add_data))
> +		return 1;
And here.
> +	configure_added_submodule(&add_data);
> +	free(add_data.sm_path);
However, it will be free()-d here, is it intended?

I think we may use UNLEAK above (for now) because we will exit process after this function.

However, I anticipated we may need to do more stuffs after this function in the future.

Show 129 quoted lines
> +
> +	return 0;
> +}
> +
>  #define SUPPORT_SUPER_PREFIX (1<<0)
>  
>  struct cmd_struct {
> @@ -3060,6 +3219,7 @@ static struct cmd_struct commands[] = {
>  	{"clone", module_clone, 0},
>  	{"add-clone", add_clone, 0},
>  	{"add-config", add_config, 0},
> +	{"add", module_add, SUPPORT_SUPER_PREFIX},
>  	{"update-module-mode", module_update_module_mode, 0},
>  	{"update-clone", update_clone, 0},
>  	{"ensure-core-worktree", ensure_core_worktree, 0},
> diff --git a/git-submodule.sh b/git-submodule.sh
> index 8c219ef382..1070540525 100755
> --- a/git-submodule.sh
> +++ b/git-submodule.sh
> @@ -145,104 +145,12 @@ cmd_add()
>  		shift
>  	done
>  
> -	if ! git submodule--helper config --check-writeable >/dev/null 2>&1
> +	if test -z "$1"
>  	then
> -		 die "fatal: $(eval_gettext "please make sure that the .gitmodules file is in the working tree")"
> -	fi
> -
> -	if test -n "$reference_path"
> -	then
> -		is_absolute_path "$reference_path" ||
> -		reference_path="$wt_prefix$reference_path"
> -
> -		reference="--reference=$reference_path"
> -	fi
> -
> -	repo=$1
> -	sm_path=$2
> -
> -	if test -z "$sm_path"; then
> -		sm_path=$(printf '%s\n' "$repo" |
> -			sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
> -	fi
> -
> -	if test -z "$repo" || test -z "$sm_path"; then
>  		usage
>  	fi
>  
> -	is_absolute_path "$sm_path" || sm_path="$wt_prefix$sm_path"
> -
> -	# assure repo is absolute or relative to parent
> -	case "$repo" in
> -	./*|../*)
> -		test -z "$wt_prefix" ||
> -		die "fatal: $(gettext "Relative path can only be used from the toplevel of the working tree")"
> -
> -		# dereference source url relative to parent's url
> -		realrepo=$(git submodule--helper resolve-relative-url "$repo") || exit
> -		;;
> -	*:*|/*)
> -		# absolute url
> -		realrepo=$repo
> -		;;
> -	*)
> -		die "fatal: $(eval_gettext "repo URL: '\$repo' must be absolute or begin with ./|../")"
> -	;;
> -	esac
> -
> -	# normalize path:
> -	# multiple //; leading ./; /./; /../; trailing /
> -	sm_path=$(printf '%s/\n' "$sm_path" |
> -		sed -e '
> -			s|//*|/|g
> -			s|^\(\./\)*||
> -			s|/\(\./\)*|/|g
> -			:start
> -			s|\([^/]*\)/\.\./||
> -			tstart
> -			s|/*$||
> -		')
> -	if test -z "$force"
> -	then
> -		git ls-files --error-unmatch "$sm_path" > /dev/null 2>&1 &&
> -		die "fatal: $(eval_gettext "'\$sm_path' already exists in the index")"
> -	else
> -		git ls-files -s "$sm_path" | sane_grep -v "^160000" > /dev/null 2>&1 &&
> -		die "fatal: $(eval_gettext "'\$sm_path' already exists in the index and is not a submodule")"
> -	fi
> -
> -	if test -d "$sm_path" &&
> -		test -z $(git -C "$sm_path" rev-parse --show-cdup 2>/dev/null)
> -	then
> -	    git -C "$sm_path" rev-parse --verify -q HEAD >/dev/null ||
> -	    die "fatal: $(eval_gettext "'\$sm_path' does not have a commit checked out")"
> -	fi
> -
> -	if test -z "$force"
> -	then
> -	    dryerr=$(git add --dry-run --ignore-missing --no-warn-embedded-repo "$sm_path" 2>&1 >/dev/null)
> -	    res=$?
> -	    if test $res -ne 0
> -	    then
> -		 echo >&2 "$dryerr"
> -		 exit $res
> -	    fi
> -	fi
> -
> -	if test -n "$custom_name"
> -	then
> -		sm_name="$custom_name"
> -	else
> -		sm_name="$sm_path"
> -	fi
> -
> -	if ! git submodule--helper check-name "$sm_name"
> -	then
> -		die "fatal: $(eval_gettext "'$sm_name' is not a valid submodule name")"
> -	fi
> -
> -	git submodule--helper add-clone ${GIT_QUIET:+--quiet} ${force:+"--force"} ${progress:+"--progress"} ${branch:+--branch "$branch"} --prefix "$wt_prefix" --path "$sm_path" --name "$sm_name" --url "$realrepo" ${reference:+"$reference"} ${dissociate:+"--dissociate"} ${depth:+"$depth"} || exit
> -	git submodule--helper add-config ${force:+--force} ${branch:+--branch "$branch"} --url "$repo" --resolved-url "$realrepo" --path "$sm_path" --name "$sm_name"
> +	git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper add ${GIT_QUIET:+--quiet} ${force:+--force} ${progress:+"--progress"} ${branch:+--branch "$branch"} ${reference_path:+--reference "$reference_path"} ${dissociate:+--dissociate} ${custom_name:+--name "$custom_name"} ${depth:+"$depth"} -- "$@"
>  }
>  
>  #
> -- 
> 2.32.0
> 
-- 
Danh
Atharva Raykar· Aug 6, 2021, 11:33 UTC · re: Đoàn Trần Công Danh · lore

Re: [GSoC] [PATCH v2 6/9] submodule--helper: convert the bulk of cmd_add() to C

Đoàn Trần Công Danh <congdanhqx@gmail.com> writes:
Show 25 quoted lines
> On 2021-08-05 13:10:51+0530, Atharva Raykar <raykar.ath@gmail.com> wrote:
>> [...]
>> +		add_data.sm_path = xstrdup(argv[1]);
>
> add_data.sm_path is allocated in this block (regardless of legs).
>
>> [...]
>> +		if ((exit_code = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {
>> +			strbuf_complete_line(&sb);
>> +			fputs(sb.buf, stderr);
>> +			return exit_code;
>
> But, we don't free it when return from here.
>
>> [...]
>> +
>> +	if (add_submodule(&add_data))
>> +		return 1;
>
> And here.
>
>> +	configure_added_submodule(&add_data);
>> +	free(add_data.sm_path);
>
> However, it will be free()-d here, is it intended?

Yeah I meant to have it free()'d wherever possible, although I suppose it isn't strictly necessary since we exit.

Show 5 quoted lines
> I think we may use UNLEAK above (for now) because we will exit process
> after this function.
>
> However, I anticipated we may need to do more stuffs after this
> function in the future.
Right. So it's better I ensure that it's freed properly everywhere.
>> [...]
>> --
>> 2.32.0
>>
Atharva Raykar· Aug 5, 2021, 07:40 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v2 7/9] submodule--helper: remove add-clone subcommand

We no longer need this subcommand, as all of its functionality is being called by the newly-introduced `module_add()` directly within C.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 60 -------------------------------------
 1 file changed, 60 deletions(-)
Show changes to builtin/submodule--helper.c +0 −60
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 05ae9ebe50..700cc8bbeb 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2860,65 +2860,6 @@ static int add_submodule(const struct add_data *add_data)
 	return 0;
 }
 
-static int add_clone(int argc, const char **argv, const char *prefix)
-{
-	int force = 0, quiet = 0, dissociate = 0, progress = 0;
-	struct add_data add_data = ADD_DATA_INIT;
-
-	struct option options[] = {
-		OPT_STRING('b', "branch", &add_data.branch,
-			   N_("branch"),
-			   N_("branch of repository to checkout on cloning")),
-		OPT_STRING(0, "prefix", &prefix,
-			   N_("path"),
-			   N_("alternative anchor for relative paths")),
-		OPT_STRING(0, "path", &add_data.sm_path,
-			   N_("path"),
-			   N_("where the new submodule will be cloned to")),
-		OPT_STRING(0, "name", &add_data.sm_name,
-			   N_("string"),
-			   N_("name of the new submodule")),
-		OPT_STRING(0, "url", &add_data.realrepo,
-			   N_("string"),
-			   N_("url where to clone the submodule from")),
-		OPT_STRING(0, "reference", &add_data.reference_path,
-			   N_("repo"),
-			   N_("reference repository")),
-		OPT_BOOL(0, "dissociate", &dissociate,
-			 N_("use --reference only while cloning")),
-		OPT_INTEGER(0, "depth", &add_data.depth,
-			    N_("depth for shallow clones")),
-		OPT_BOOL(0, "progress", &progress,
-			 N_("force cloning progress")),
-		OPT__FORCE(&force, N_("allow adding an otherwise ignored submodule path"),
-			   PARSE_OPT_NOCOMPLETE),
-		OPT__QUIET(&quiet, "suppress output for cloning a submodule"),
-		OPT_END()
-	};
-
-	const char *const usage[] = {
-		N_("git submodule--helper add-clone [<options>...] "
-		   "--url <url> --path <path> --name <name>"),
-		NULL
-	};
-
-	argc = parse_options(argc, argv, prefix, options, usage, 0);
-
-	if (argc != 0)
-		usage_with_options(usage, options);
-
-	add_data.prefix = prefix;
-	add_data.progress = !!progress;
-	add_data.dissociate = !!dissociate;
-	add_data.force = !!force;
-	add_data.quiet = !!quiet;
-
-	if (add_submodule(&add_data))
-		return 1;
-
-	return 0;
-}
-
 static int config_submodule_in_gitmodules(const char *name, const char *var, const char *value)
 {
 	char *key;
@@ -3217,7 +3158,6 @@ static struct cmd_struct commands[] = {
 	{"list", module_list, 0},
 	{"name", module_name, 0},
 	{"clone", module_clone, 0},
-	{"add-clone", add_clone, 0},
 	{"add-config", add_config, 0},
 	{"add", module_add, SUPPORT_SUPER_PREFIX},
 	{"update-module-mode", module_update_module_mode, 0},
-- 
2.32.0
Atharva Raykar· Aug 5, 2021, 07:40 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v2 8/9] submodule--helper: remove add-config subcommand

Also no longer needed is this subcommand, as all of its functionality is being called by the newly-introduced `module_add()` directly within C.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 49 -------------------------------------
 1 file changed, 49 deletions(-)
Show changes to builtin/submodule--helper.c +0 −49
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 700cc8bbeb..da83e8e2a5 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2939,54 +2939,6 @@ static void configure_added_submodule(struct add_data *add_data)
 	}
 }
 
-static int add_config(int argc, const char **argv, const char *prefix)
-{
-	int force = 0;
-	struct add_data add_data = ADD_DATA_INIT;
-
-	struct option options[] = {
-		OPT_STRING('b', "branch", &add_data.branch,
-			   N_("branch"),
-			   N_("branch of repository to store in "
-			      "the submodule configuration")),
-		OPT_STRING(0, "url", &add_data.repo,
-			   N_("string"),
-			   N_("url to clone submodule from")),
-		OPT_STRING(0, "resolved-url", &add_data.realrepo,
-			   N_("string"),
-			   N_("url to clone the submodule from, after it has "
-			      "been dereferenced relative to parent's url, "
-			      "in the case where <url> is a relative url")),
-		OPT_STRING(0, "path", &add_data.sm_path,
-			   N_("path"),
-			   N_("where the new submodule will be cloned to")),
-		OPT_STRING(0, "name", &add_data.sm_name,
-			   N_("string"),
-			   N_("name of the new submodule")),
-		OPT__FORCE(&force, N_("allow adding an otherwise ignored submodule path"),
-			   PARSE_OPT_NOCOMPLETE),
-		OPT_END()
-	};
-
-	const char *const usage[] = {
-		N_("git submodule--helper add-config "
-		   "[--force|-f] [--branch|-b <branch>] "
-		   "--url <url> --resolved-url <resolved-url> "
-		   "--path <path> --name <name>"),
-		NULL
-	};
-
-	argc = parse_options(argc, argv, prefix, options, usage, 0);
-
-	if (argc)
-		usage_with_options(usage, options);
-
-	add_data.force = !!force;
-	configure_added_submodule(&add_data);
-
-	return 0;
-}
-
 static void die_on_index_match(const char *path, int force)
 {
 	struct pathspec ps;
@@ -3158,7 +3110,6 @@ static struct cmd_struct commands[] = {
 	{"list", module_list, 0},
 	{"name", module_name, 0},
 	{"clone", module_clone, 0},
-	{"add-config", add_config, 0},
 	{"add", module_add, SUPPORT_SUPER_PREFIX},
 	{"update-module-mode", module_update_module_mode, 0},
 	{"update-clone", update_clone, 0},
-- 
2.32.0
Atharva Raykar· Aug 5, 2021, 07:40 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v2 9/9] submodule--helper: remove resolve-relative-url subcommand

The shell subcommand `resolve-relative-url` is no longer required, as its last caller has been removed when it was converted to C.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 20 --------------------
 1 file changed, 20 deletions(-)
Show changes to builtin/submodule--helper.c +0 −20
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index da83e8e2a5..514891a22f 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -223,25 +223,6 @@ static char *compute_submodule_clone_url(const char *rel_url, const char *up_pat
 	return relurl;
 }
 
-static int resolve_relative_url(int argc, const char **argv, const char *prefix)
-{
-	const char *up_path = NULL;
-	char *res;
-	const char *url;
-
-	if (argc != 2 && argc != 3)
-		die("resolve-relative-url only accepts one or two arguments");
-
-	url = argv[1];
-	if (argc == 3)
-		up_path = argv[2];
-
-	res = compute_submodule_clone_url(url, up_path, 1);
-	puts(res);
-	free(res);
-	return 0;
-}
-
 static int resolve_relative_url_test(int argc, const char **argv, const char *prefix)
 {
 	char *remoteurl, *res;
@@ -3115,7 +3096,6 @@ static struct cmd_struct commands[] = {
 	{"update-clone", update_clone, 0},
 	{"ensure-core-worktree", ensure_core_worktree, 0},
 	{"relative-path", resolve_relative_path, 0},
-	{"resolve-relative-url", resolve_relative_url, 0},
 	{"resolve-relative-url-test", resolve_relative_url_test, 0},
 	{"foreach", module_foreach, SUPPORT_SUPER_PREFIX},
 	{"init", module_init, SUPPORT_SUPER_PREFIX},
-- 
2.32.0
Atharva Raykar· Aug 6, 2021, 12:01 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v3 0/8] submodule: convert the rest of 'add' to C

Changes since v2:
* Mention the small overhead introduced in [2/8] in the commit message.
* Attempt a more descriptive name for the libified dir helper. Add a docstring
  to explain it as well.
* Combine the "unconstification of sm_path" patch into the next one.
Atharva Raykar (8):
  submodule--helper: add options for compute_submodule_clone_url()
  submodule--helper: refactor resolve_relative_url() helper
  submodule--helper: remove repeated code in sync_submodule()
  dir: libify and export helper functions from clone.c
  submodule--helper: convert the bulk of cmd_add() to C
  submodule--helper: remove add-clone subcommand
  submodule--helper: remove add-config subcommand
  submodule--helper: remove resolve-relative-url subcommand
 builtin/clone.c             | 118 +-------------
 builtin/submodule--helper.c | 307 +++++++++++++++++++-----------------
 dir.c                       | 114 +++++++++++++
 dir.h                       |  11 ++
 git-submodule.sh            |  96 +----------
 5 files changed, 291 insertions(+), 355 deletions(-)
Range-diff against v2:
-:  ---------- > 1:  75edf24186 submodule--helper: add options for compute_submodule_clone_url()
-:  ---------- > 2:  8e7a3e727a submodule--helper: refactor resolve_relative_url() helper
1:  d8ee0bc450 ! 3:  82961ddd02 submodule--helper: remove repeated code in sync_submodule()
    @@ Commit message
         This part of `sync_submodule()` is doing the same thing that
         `compute_submodule_clone_url()` is doing. Let's reuse that helper here.
     
    +    Note that this change adds a small overhead where we allocate and free
    +    the 'remote' twice, but that is a small price to pay for the higher
    +    level of abstraction we get.
    +
         Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
         Mentored-by: Christian Couder <christian.couder@gmail.com>
         Mentored-by: Shourya Shukla <periperidip@gmail.com>
2:  6ac470d86f ! 4:  99d139375d dir: libify and export helper functions from clone.c
    @@ Commit message
     
         Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
         Mentored-by: Christian Couder <christian.couder@gmail.com>
    -    Mentored-by: Shourya Shukla <shouryashukla.oo@gmail.com>
    +    Mentored-by: Shourya Shukla <periperidip@gmail.com>
     
      ## builtin/clone.c ##
     @@ builtin/clone.c: static char *get_repo_path(const char *repo, int *is_bundle)
    @@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix)
      	else
     -		dir = guess_dir_name(repo_name, is_bundle, option_bare);
     -	strip_trailing_slashes(dir);
    -+		dir = guess_dir_name_from_git_url(repo_name, is_bundle, option_bare);
    ++		dir = guess_target_dir_from_git_url(repo_name, is_bundle, option_bare);
     +	strip_dir_trailing_slashes(dir);
      
      	dest_exists = path_exists(dir);
    @@ dir.c: int is_empty_dir(const char *path)
      	return ret;
      }
      
    -+char *guess_dir_name_from_git_url(const char *repo, int is_bundle, int is_bare)
    ++char *guess_target_dir_from_git_url(const char *repo, int is_bundle, int is_bare)
     +{
     +	const char *end = repo + strlen(repo), *start, *ptr;
     +	size_t len;
    @@ dir.h: static inline int is_dot_or_dotdot(const char *name)
      
      int is_empty_dir(const char *dir);
      
    -+char *guess_dir_name_from_git_url(const char *repo, int is_bundle, int is_bare);
    ++/*
    ++ * Retrieve a target directory name by reading "humanish" part of the
    ++ * given Git URL.
    ++ *
    ++ * For example:
    ++ * 	/path/to/repo.git => "repo"
    ++ * 	host.xz.foo/.git => "foo"
    ++ */
    ++char *guess_target_dir_from_git_url(const char *repo, int is_bundle, int is_bare);
     +void strip_dir_trailing_slashes(char *dir);
     +
      void setup_standard_excludes(struct dir_struct *dir);
3:  c0b264a4e8 < -:  ---------- submodule--helper: remove constness of sm_path
4:  f61124327f ! 5:  11eea777ba submodule--helper: convert the bulk of cmd_add() to C
    @@ Commit message
         Introduce the 'add' subcommand to `submodule--helper.c` that does all
         the work 'submodule add' past the parsing of flags.
     
    +    We also remove the constness of the sm_path field of the `add_data`
    +    struct. This is needed so that it can be modified by
    +    normalize_path_copy().
    +
         As with the previous conversions, this is meant to be a faithful
         conversion with no modification to the behaviour of `submodule add`.
     
    @@ Commit message
         Based-on-patch-by: Prathamesh Chavan <pc44800@gmail.com>
     
      ## builtin/submodule--helper.c ##
    +@@ builtin/submodule--helper.c: struct add_data {
    + 	const char *prefix;
    + 	const char *branch;
    + 	const char *reference_path;
    +-	const char *sm_path;
    ++	char *sm_path;
    + 	const char *sm_name;
    + 	const char *repo;
    + 	const char *realrepo;
     @@ builtin/submodule--helper.c: static int add_config(int argc, const char **argv, const char *prefix)
      	return 0;
      }
    @@ builtin/submodule--helper.c: static int add_config(int argc, const char **argv,
     +
     +	add_data.repo = argv[0];
     +	if (argc == 1)
    -+		add_data.sm_path = guess_dir_name_from_git_url(add_data.repo, 0, 0);
    ++		add_data.sm_path = guess_target_dir_from_git_url(add_data.repo, 0, 0);
     +	else
     +		add_data.sm_path = xstrdup(argv[1]);
     +
    @@ builtin/submodule--helper.c: static int add_config(int argc, const char **argv,
     +		if ((exit_code = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {
     +			strbuf_complete_line(&sb);
     +			fputs(sb.buf, stderr);
    ++			free(add_data.sm_path);
     +			return exit_code;
     +		}
     +		strbuf_release(&sb);
    @@ builtin/submodule--helper.c: static int add_config(int argc, const char **argv,
     +	add_data.progress = !!progress;
     +	add_data.dissociate = !!dissociate;
     +
    -+	if (add_submodule(&add_data))
    ++	if (add_submodule(&add_data)) {
    ++		free(add_data.sm_path);
     +		return 1;
    ++	}
     +	configure_added_submodule(&add_data);
     +	free(add_data.sm_path);
     +
5:  0c37bf84c3 = 6:  51393cd99b submodule--helper: remove add-clone subcommand
6:  d7e0db2565 = 7:  50cedcd8a8 submodule--helper: remove add-config subcommand
7:  6dd7cb1e18 = 8:  02558da532 submodule--helper: remove resolve-relative-url subcommand
-- 
2.32.0
Atharva Raykar· Aug 6, 2021, 12:01 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v3 1/8] submodule--helper: add options for compute_submodule_clone_url()

Let's modify the interface to `compute_submodule_clone_url()` function by adding two more arguments, so that we can reuse this in various parts of `submodule--helper.c` that follow a common pattern, which is--read the remote url configuration of the superproject and then call `relative_url()`.

This function is nearly identical to `resolve_relative_url()`, the only difference being the extra warning message. We can add a quiet flag to it, to suppress that warning when not needed, and then refactor `resolve_relative_url()` by using this function, something we will do in the next patch.

Having this functionality factored out will be useful for converting the rest of `submodule add` in subsequent patches.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)
Show changes to builtin/submodule--helper.c +9 −5
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 6891480013..2d2d8ac637 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -590,7 +590,7 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
 	return 0;
 }
 
-static char *compute_submodule_clone_url(const char *rel_url)
+static char *compute_submodule_clone_url(const char *rel_url, const char *up_path, int quiet)
 {
 	char *remoteurl, *relurl;
 	char *remote = get_default_remote();
@@ -598,10 +598,14 @@ static char *compute_submodule_clone_url(const char *rel_url)
 
 	strbuf_addf(&remotesb, "remote.%s.url", remote);
 	if (git_config_get_string(remotesb.buf, &remoteurl)) {
-		warning(_("could not look up configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf);
+		if (!quiet)
+			warning(_("could not look up configuration '%s'. "
+				  "Assuming this repository is its own "
+				  "authoritative upstream."),
+				remotesb.buf);
 		remoteurl = xgetcwd();
 	}
-	relurl = relative_url(remoteurl, rel_url, NULL);
+	relurl = relative_url(remoteurl, rel_url, up_path);
 
 	free(remote);
 	free(remoteurl);
@@ -660,7 +664,7 @@ static void init_submodule(const char *path, const char *prefix,
 		if (starts_with_dot_dot_slash(url) ||
 		    starts_with_dot_slash(url)) {
 			char *oldurl = url;
-			url = compute_submodule_clone_url(oldurl);
+			url = compute_submodule_clone_url(oldurl, NULL, 0);
 			free(oldurl);
 		}
 
@@ -2134,7 +2138,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
 	if (repo_config_get_string_tmp(the_repository, sb.buf, &url)) {
 		if (starts_with_dot_slash(sub->url) ||
 		    starts_with_dot_dot_slash(sub->url)) {
-			url = compute_submodule_clone_url(sub->url);
+			url = compute_submodule_clone_url(sub->url, NULL, 0);
 			need_free_url = 1;
 		} else
 			url = sub->url;
-- 
2.32.0
Atharva Raykar· Aug 6, 2021, 12:01 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v3 2/8] submodule--helper: refactor resolve_relative_url() helper

Refactor the helper function to resolve a relative url, by reusing the existing `compute_submodule_clone_url()` function.

`compute_submodule_clone_url()` performs the same work that `resolve_relative_url()` is doing, so we eliminate this code repetition by moving the former function's definition up, and calling it inside `resolve_relative_url()`.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 61 +++++++++++++++----------------------
 1 file changed, 25 insertions(+), 36 deletions(-)
Show changes to builtin/submodule--helper.c +25 −36
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 2d2d8ac637..f4b496bac6 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -199,33 +199,46 @@ static char *relative_url(const char *remote_url,
 	return strbuf_detach(&sb, NULL);
 }
 
+static char *compute_submodule_clone_url(const char *rel_url, const char *up_path, int quiet)
+{
+	char *remoteurl, *relurl;
+	char *remote = get_default_remote();
+	struct strbuf remotesb = STRBUF_INIT;
+
+	strbuf_addf(&remotesb, "remote.%s.url", remote);
+	if (git_config_get_string(remotesb.buf, &remoteurl)) {
+		if (!quiet)
+			warning(_("could not look up configuration '%s'. "
+				  "Assuming this repository is its own "
+				  "authoritative upstream."),
+				remotesb.buf);
+		remoteurl = xgetcwd();
+	}
+	relurl = relative_url(remoteurl, rel_url, up_path);
+
+	free(remote);
+	free(remoteurl);
+	strbuf_release(&remotesb);
+
+	return relurl;
+}
+
 static int resolve_relative_url(int argc, const char **argv, const char *prefix)
 {
-	char *remoteurl = NULL;
-	char *remote = get_default_remote();
 	const char *up_path = NULL;
 	char *res;
 	const char *url;
-	struct strbuf sb = STRBUF_INIT;
 
 	if (argc != 2 && argc != 3)
 		die("resolve-relative-url only accepts one or two arguments");
 
 	url = argv[1];
-	strbuf_addf(&sb, "remote.%s.url", remote);
-	free(remote);
-
-	if (git_config_get_string(sb.buf, &remoteurl))
-		/* the repository is its own authoritative upstream */
-		remoteurl = xgetcwd();
-
 	if (argc == 3)
 		up_path = argv[2];
 
-	res = relative_url(remoteurl, url, up_path);
+	res = compute_submodule_clone_url(url, up_path, 1);
 	puts(res);
 	free(res);
-	free(remoteurl);
 	return 0;
 }
 
@@ -590,30 +603,6 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
 	return 0;
 }
 
-static char *compute_submodule_clone_url(const char *rel_url, const char *up_path, int quiet)
-{
-	char *remoteurl, *relurl;
-	char *remote = get_default_remote();
-	struct strbuf remotesb = STRBUF_INIT;
-
-	strbuf_addf(&remotesb, "remote.%s.url", remote);
-	if (git_config_get_string(remotesb.buf, &remoteurl)) {
-		if (!quiet)
-			warning(_("could not look up configuration '%s'. "
-				  "Assuming this repository is its own "
-				  "authoritative upstream."),
-				remotesb.buf);
-		remoteurl = xgetcwd();
-	}
-	relurl = relative_url(remoteurl, rel_url, up_path);
-
-	free(remote);
-	free(remoteurl);
-	strbuf_release(&remotesb);
-
-	return relurl;
-}
-
 struct init_cb {
 	const char *prefix;
 	unsigned int flags;
-- 
2.32.0
Atharva Raykar· Aug 6, 2021, 12:01 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v3 3/8] submodule--helper: remove repeated code in sync_submodule()

This part of `sync_submodule()` is doing the same thing that `compute_submodule_clone_url()` is doing. Let's reuse that helper here.

Note that this change adds a small overhead where we allocate and free the 'remote' twice, but that is a small price to pay for the higher level of abstraction we get.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 16 +++-------------
 1 file changed, 3 insertions(+), 13 deletions(-)
Show changes to builtin/submodule--helper.c +3 −13
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index f4b496bac6..9b676c12f8 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1373,20 +1373,10 @@ static void sync_submodule(const char *path, const char *prefix,
 	if (sub && sub->url) {
 		if (starts_with_dot_dot_slash(sub->url) ||
 		    starts_with_dot_slash(sub->url)) {
-			char *remote_url, *up_path;
-			char *remote = get_default_remote();
-			strbuf_addf(&sb, "remote.%s.url", remote);
-
-			if (git_config_get_string(sb.buf, &remote_url))
-				remote_url = xgetcwd();
-
-			up_path = get_up_path(path);
-			sub_origin_url = relative_url(remote_url, sub->url, up_path);
-			super_config_url = relative_url(remote_url, sub->url, NULL);
-
-			free(remote);
+			char *up_path = get_up_path(path);
+			sub_origin_url = compute_submodule_clone_url(sub->url, up_path, 1);
+			super_config_url = compute_submodule_clone_url(sub->url, NULL, 1);
 			free(up_path);
-			free(remote_url);
 		} else {
 			sub_origin_url = xstrdup(sub->url);
 			super_config_url = xstrdup(sub->url);
-- 
2.32.0
Atharva Raykar· Aug 6, 2021, 12:01 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v3 4/8] dir: libify and export helper functions from clone.c

These functions can be useful to other parts of Git. Let's move them to dir.c, while renaming them to be make their functionality more explicit.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/clone.c | 118 +-----------------------------------------------
 dir.c           | 114 ++++++++++++++++++++++++++++++++++++++++++++++
 dir.h           |  11 +++++
 3 files changed, 127 insertions(+), 116 deletions(-)
Show changes to 3 files +127 −116

builtin/clone.c, dir.c, dir.h

diff --git a/builtin/clone.c b/builtin/clone.c
index 66fe66679c..fab0a3cef6 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -217,120 +217,6 @@ static char *get_repo_path(const char *repo, int *is_bundle)
 	return canon;
 }
 
-static char *guess_dir_name(const char *repo, int is_bundle, int is_bare)
-{
-	const char *end = repo + strlen(repo), *start, *ptr;
-	size_t len;
-	char *dir;
-
-	/*
-	 * Skip scheme.
-	 */
-	start = strstr(repo, "://");
-	if (start == NULL)
-		start = repo;
-	else
-		start += 3;
-
-	/*
-	 * Skip authentication data. The stripping does happen
-	 * greedily, such that we strip up to the last '@' inside
-	 * the host part.
-	 */
-	for (ptr = start; ptr < end && !is_dir_sep(*ptr); ptr++) {
-		if (*ptr == '@')
-			start = ptr + 1;
-	}
-
-	/*
-	 * Strip trailing spaces, slashes and /.git
-	 */
-	while (start < end && (is_dir_sep(end[-1]) || isspace(end[-1])))
-		end--;
-	if (end - start > 5 && is_dir_sep(end[-5]) &&
-	    !strncmp(end - 4, ".git", 4)) {
-		end -= 5;
-		while (start < end && is_dir_sep(end[-1]))
-			end--;
-	}
-
-	/*
-	 * Strip trailing port number if we've got only a
-	 * hostname (that is, there is no dir separator but a
-	 * colon). This check is required such that we do not
-	 * strip URI's like '/foo/bar:2222.git', which should
-	 * result in a dir '2222' being guessed due to backwards
-	 * compatibility.
-	 */
-	if (memchr(start, '/', end - start) == NULL
-	    && memchr(start, ':', end - start) != NULL) {
-		ptr = end;
-		while (start < ptr && isdigit(ptr[-1]) && ptr[-1] != ':')
-			ptr--;
-		if (start < ptr && ptr[-1] == ':')
-			end = ptr - 1;
-	}
-
-	/*
-	 * Find last component. To remain backwards compatible we
-	 * also regard colons as path separators, such that
-	 * cloning a repository 'foo:bar.git' would result in a
-	 * directory 'bar' being guessed.
-	 */
-	ptr = end;
-	while (start < ptr && !is_dir_sep(ptr[-1]) && ptr[-1] != ':')
-		ptr--;
-	start = ptr;
-
-	/*
-	 * Strip .{bundle,git}.
-	 */
-	len = end - start;
-	strip_suffix_mem(start, &len, is_bundle ? ".bundle" : ".git");
-
-	if (!len || (len == 1 && *start == '/'))
-		die(_("No directory name could be guessed.\n"
-		      "Please specify a directory on the command line"));
-
-	if (is_bare)
-		dir = xstrfmt("%.*s.git", (int)len, start);
-	else
-		dir = xstrndup(start, len);
-	/*
-	 * Replace sequences of 'control' characters and whitespace
-	 * with one ascii space, remove leading and trailing spaces.
-	 */
-	if (*dir) {
-		char *out = dir;
-		int prev_space = 1 /* strip leading whitespace */;
-		for (end = dir; *end; ++end) {
-			char ch = *end;
-			if ((unsigned char)ch < '\x20')
-				ch = '\x20';
-			if (isspace(ch)) {
-				if (prev_space)
-					continue;
-				prev_space = 1;
-			} else
-				prev_space = 0;
-			*out++ = ch;
-		}
-		*out = '\0';
-		if (out > dir && prev_space)
-			out[-1] = '\0';
-	}
-	return dir;
-}
-
-static void strip_trailing_slashes(char *dir)
-{
-	char *end = dir + strlen(dir);
-
-	while (dir < end - 1 && is_dir_sep(end[-1]))
-		end--;
-	*end = '\0';
-}
-
 static int add_one_reference(struct string_list_item *item, void *cb_data)
 {
 	struct strbuf err = STRBUF_INIT;
@@ -1041,8 +927,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 	if (argc == 2)
 		dir = xstrdup(argv[1]);
 	else
-		dir = guess_dir_name(repo_name, is_bundle, option_bare);
-	strip_trailing_slashes(dir);
+		dir = guess_target_dir_from_git_url(repo_name, is_bundle, option_bare);
+	strip_dir_trailing_slashes(dir);
 
 	dest_exists = path_exists(dir);
 	if (dest_exists && !is_empty_dir(dir))
diff --git a/dir.c b/dir.c
index 03c4d21267..9a9ab135c4 100644
--- a/dir.c
+++ b/dir.c
@@ -2970,6 +2970,120 @@ int is_empty_dir(const char *path)
 	return ret;
 }
 
+char *guess_target_dir_from_git_url(const char *repo, int is_bundle, int is_bare)
+{
+	const char *end = repo + strlen(repo), *start, *ptr;
+	size_t len;
+	char *dir;
+
+	/*
+	 * Skip scheme.
+	 */
+	start = strstr(repo, "://");
+	if (start == NULL)
+		start = repo;
+	else
+		start += 3;
+
+	/*
+	 * Skip authentication data. The stripping does happen
+	 * greedily, such that we strip up to the last '@' inside
+	 * the host part.
+	 */
+	for (ptr = start; ptr < end && !is_dir_sep(*ptr); ptr++) {
+		if (*ptr == '@')
+			start = ptr + 1;
+	}
+
+	/*
+	 * Strip trailing spaces, slashes and /.git
+	 */
+	while (start < end && (is_dir_sep(end[-1]) || isspace(end[-1])))
+		end--;
+	if (end - start > 5 && is_dir_sep(end[-5]) &&
+	    !strncmp(end - 4, ".git", 4)) {
+		end -= 5;
+		while (start < end && is_dir_sep(end[-1]))
+			end--;
+	}
+
+	/*
+	 * Strip trailing port number if we've got only a
+	 * hostname (that is, there is no dir separator but a
+	 * colon). This check is required such that we do not
+	 * strip URI's like '/foo/bar:2222.git', which should
+	 * result in a dir '2222' being guessed due to backwards
+	 * compatibility.
+	 */
+	if (memchr(start, '/', end - start) == NULL
+	    && memchr(start, ':', end - start) != NULL) {
+		ptr = end;
+		while (start < ptr && isdigit(ptr[-1]) && ptr[-1] != ':')
+			ptr--;
+		if (start < ptr && ptr[-1] == ':')
+			end = ptr - 1;
+	}
+
+	/*
+	 * Find last component. To remain backwards compatible we
+	 * also regard colons as path separators, such that
+	 * cloning a repository 'foo:bar.git' would result in a
+	 * directory 'bar' being guessed.
+	 */
+	ptr = end;
+	while (start < ptr && !is_dir_sep(ptr[-1]) && ptr[-1] != ':')
+		ptr--;
+	start = ptr;
+
+	/*
+	 * Strip .{bundle,git}.
+	 */
+	len = end - start;
+	strip_suffix_mem(start, &len, is_bundle ? ".bundle" : ".git");
+
+	if (!len || (len == 1 && *start == '/'))
+		die(_("No directory name could be guessed.\n"
+		      "Please specify a directory on the command line"));
+
+	if (is_bare)
+		dir = xstrfmt("%.*s.git", (int)len, start);
+	else
+		dir = xstrndup(start, len);
+	/*
+	 * Replace sequences of 'control' characters and whitespace
+	 * with one ascii space, remove leading and trailing spaces.
+	 */
+	if (*dir) {
+		char *out = dir;
+		int prev_space = 1 /* strip leading whitespace */;
+		for (end = dir; *end; ++end) {
+			char ch = *end;
+			if ((unsigned char)ch < '\x20')
+				ch = '\x20';
+			if (isspace(ch)) {
+				if (prev_space)
+					continue;
+				prev_space = 1;
+			} else
+				prev_space = 0;
+			*out++ = ch;
+		}
+		*out = '\0';
+		if (out > dir && prev_space)
+			out[-1] = '\0';
+	}
+	return dir;
+}
+
+void strip_dir_trailing_slashes(char *dir)
+{
+	char *end = dir + strlen(dir);
+
+	while (dir < end - 1 && is_dir_sep(end[-1]))
+		end--;
+	*end = '\0';
+}
+
 static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up)
 {
 	DIR *dir;
diff --git a/dir.h b/dir.h
index b3e1a54a97..6f0abdfe04 100644
--- a/dir.h
+++ b/dir.h
@@ -453,6 +453,17 @@ static inline int is_dot_or_dotdot(const char *name)
 
 int is_empty_dir(const char *dir);
 
+/*
+ * Retrieve a target directory name by reading "humanish" part of the
+ * given Git URL.
+ *
+ * For example:
+ * 	/path/to/repo.git => "repo"
+ * 	host.xz.foo/.git => "foo"
+ */
+char *guess_target_dir_from_git_url(const char *repo, int is_bundle, int is_bare);
+void strip_dir_trailing_slashes(char *dir);
+
 void setup_standard_excludes(struct dir_struct *dir);
 
 char *get_sparse_checkout_filename(void);
-- 
2.32.0
Atharva Raykar· Aug 6, 2021, 12:01 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v3 5/8] submodule--helper: convert the bulk of cmd_add() to C

Introduce the 'add' subcommand to `submodule--helper.c` that does all the work 'submodule add' past the parsing of flags.

We also remove the constness of the sm_path field of the `add_data` struct. This is needed so that it can be modified by normalize_path_copy().

As with the previous conversions, this is meant to be a faithful conversion with no modification to the behaviour of `submodule add`.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Helped-by: Kaartic Sivaraam <kaartic.sivaraam@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
Based-on-patch-by: Shourya Shukla <periperidip@gmail.com>
Based-on-patch-by: Prathamesh Chavan <pc44800@gmail.com>
---
 builtin/submodule--helper.c | 165 +++++++++++++++++++++++++++++++++++-
 git-submodule.sh            |  96 +--------------------
 2 files changed, 166 insertions(+), 95 deletions(-)
Show changes to 2 files +166 −95

builtin/submodule--helper.c, git-submodule.sh

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 9b676c12f8..f31e5a9bde 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2748,7 +2748,7 @@ struct add_data {
 	const char *prefix;
 	const char *branch;
 	const char *reference_path;
-	const char *sm_path;
+	char *sm_path;
 	const char *sm_name;
 	const char *repo;
 	const char *realrepo;
@@ -3046,6 +3046,168 @@ static int add_config(int argc, const char **argv, const char *prefix)
 	return 0;
 }
 
+static void die_on_index_match(const char *path, int force)
+{
+	struct pathspec ps;
+	const char *args[] = { path, NULL };
+	parse_pathspec(&ps, 0, PATHSPEC_PREFER_CWD, NULL, args);
+
+	if (read_cache_preload(NULL) < 0)
+		die(_("index file corrupt"));
+
+	if (ps.nr) {
+		int i;
+		char *ps_matched = xcalloc(ps.nr, 1);
+
+		/* TODO: audit for interaction with sparse-index. */
+		ensure_full_index(&the_index);
+
+		/*
+		 * Since there is only one pathspec, we just need
+		 * need to check ps_matched[0] to know if a cache
+		 * entry matched.
+		 */
+		for (i = 0; i < active_nr; i++) {
+			ce_path_match(&the_index, active_cache[i], &ps,
+				      ps_matched);
+
+			if (ps_matched[0]) {
+				if (!force)
+					die(_("'%s' already exists in the index"),
+					    path);
+				if (!S_ISGITLINK(active_cache[i]->ce_mode))
+					die(_("'%s' already exists in the index "
+					      "and is not a submodule"), path);
+				break;
+			}
+		}
+		free(ps_matched);
+	}
+}
+
+static void die_on_repo_without_commits(const char *path)
+{
+	struct strbuf sb = STRBUF_INIT;
+	strbuf_addstr(&sb, path);
+	if (is_nonbare_repository_dir(&sb)) {
+		struct object_id oid;
+		if (resolve_gitlink_ref(path, "HEAD", &oid) < 0)
+			die(_("'%s' does not have a commit checked out"), path);
+	}
+}
+
+static int module_add(int argc, const char **argv, const char *prefix)
+{
+	int force = 0, quiet = 0, progress = 0, dissociate = 0;
+	struct add_data add_data = ADD_DATA_INIT;
+
+	struct option options[] = {
+		OPT_STRING('b', "branch", &add_data.branch, N_("branch"),
+			   N_("branch of repository to add as submodule")),
+		OPT__FORCE(&force, N_("allow adding an otherwise ignored submodule path"),
+			   PARSE_OPT_NOCOMPLETE),
+		OPT__QUIET(&quiet, N_("print only error messages")),
+		OPT_BOOL(0, "progress", &progress, N_("force cloning progress")),
+		OPT_STRING(0, "reference", &add_data.reference_path, N_("repository"),
+			   N_("reference repository")),
+		OPT_BOOL(0, "dissociate", &dissociate, N_("borrow the objects from reference repositories")),
+		OPT_STRING(0, "name", &add_data.sm_name, N_("name"),
+			   N_("sets the submodule’s name to the given string "
+			      "instead of defaulting to its path")),
+		OPT_INTEGER(0, "depth", &add_data.depth, N_("depth for shallow clones")),
+		OPT_END()
+	};
+
+	const char *const usage[] = {
+		N_("git submodule--helper add [<options>] [--] <repository> [<path>]"),
+		NULL
+	};
+
+	argc = parse_options(argc, argv, prefix, options, usage, 0);
+
+	if (!is_writing_gitmodules_ok())
+		die(_("please make sure that the .gitmodules file is in the working tree"));
+
+	if (prefix && *prefix &&
+	    add_data.reference_path && !is_absolute_path(add_data.reference_path))
+		add_data.reference_path = xstrfmt("%s%s", prefix, add_data.reference_path);
+
+	if (argc == 0 || argc > 2)
+		usage_with_options(usage, options);
+
+	add_data.repo = argv[0];
+	if (argc == 1)
+		add_data.sm_path = guess_target_dir_from_git_url(add_data.repo, 0, 0);
+	else
+		add_data.sm_path = xstrdup(argv[1]);
+
+	if (prefix && *prefix && !is_absolute_path(add_data.sm_path))
+		add_data.sm_path = xstrfmt("%s%s", prefix, add_data.sm_path);
+
+	if (starts_with_dot_dot_slash(add_data.repo) ||
+	    starts_with_dot_slash(add_data.repo)) {
+		if (prefix)
+			die(_("Relative path can only be used from the toplevel "
+			      "of the working tree"));
+
+		/* dereference source url relative to parent's url */
+		add_data.realrepo = compute_submodule_clone_url(add_data.repo, NULL, 1);
+	} else if (is_dir_sep(add_data.repo[0]) || strchr(add_data.repo, ':')) {
+		add_data.realrepo = add_data.repo;
+	} else {
+		die(_("repo URL: '%s' must be absolute or begin with ./|../"),
+		    add_data.repo);
+	}
+
+	/*
+	 * normalize path:
+	 * multiple //; leading ./; /./; /../;
+	 */
+	normalize_path_copy(add_data.sm_path, add_data.sm_path);
+	strip_dir_trailing_slashes(add_data.sm_path);
+
+	die_on_index_match(add_data.sm_path, force);
+	die_on_repo_without_commits(add_data.sm_path);
+
+	if (!force) {
+		int exit_code = -1;
+		struct strbuf sb = STRBUF_INIT;
+		struct child_process cp = CHILD_PROCESS_INIT;
+		cp.git_cmd = 1;
+		cp.no_stdout = 1;
+		strvec_pushl(&cp.args, "add", "--dry-run", "--ignore-missing",
+			     "--no-warn-embedded-repo", add_data.sm_path, NULL);
+		if ((exit_code = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {
+			strbuf_complete_line(&sb);
+			fputs(sb.buf, stderr);
+			free(add_data.sm_path);
+			return exit_code;
+		}
+		strbuf_release(&sb);
+	}
+
+	if(!add_data.sm_name)
+		add_data.sm_name = add_data.sm_path;
+
+	if (check_submodule_name(add_data.sm_name))
+		die(_("'%s' is not a valid submodule name"), add_data.sm_name);
+
+	add_data.prefix = prefix;
+	add_data.force = !!force;
+	add_data.quiet = !!quiet;
+	add_data.progress = !!progress;
+	add_data.dissociate = !!dissociate;
+
+	if (add_submodule(&add_data)) {
+		free(add_data.sm_path);
+		return 1;
+	}
+	configure_added_submodule(&add_data);
+	free(add_data.sm_path);
+
+	return 0;
+}
+
 #define SUPPORT_SUPER_PREFIX (1<<0)
 
 struct cmd_struct {
@@ -3060,6 +3222,7 @@ static struct cmd_struct commands[] = {
 	{"clone", module_clone, 0},
 	{"add-clone", add_clone, 0},
 	{"add-config", add_config, 0},
+	{"add", module_add, SUPPORT_SUPER_PREFIX},
 	{"update-module-mode", module_update_module_mode, 0},
 	{"update-clone", update_clone, 0},
 	{"ensure-core-worktree", ensure_core_worktree, 0},
diff --git a/git-submodule.sh b/git-submodule.sh
index 8c219ef382..1070540525 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -145,104 +145,12 @@ cmd_add()
 		shift
 	done
 
-	if ! git submodule--helper config --check-writeable >/dev/null 2>&1
+	if test -z "$1"
 	then
-		 die "fatal: $(eval_gettext "please make sure that the .gitmodules file is in the working tree")"
-	fi
-
-	if test -n "$reference_path"
-	then
-		is_absolute_path "$reference_path" ||
-		reference_path="$wt_prefix$reference_path"
-
-		reference="--reference=$reference_path"
-	fi
-
-	repo=$1
-	sm_path=$2
-
-	if test -z "$sm_path"; then
-		sm_path=$(printf '%s\n' "$repo" |
-			sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
-	fi
-
-	if test -z "$repo" || test -z "$sm_path"; then
 		usage
 	fi
 
-	is_absolute_path "$sm_path" || sm_path="$wt_prefix$sm_path"
-
-	# assure repo is absolute or relative to parent
-	case "$repo" in
-	./*|../*)
-		test -z "$wt_prefix" ||
-		die "fatal: $(gettext "Relative path can only be used from the toplevel of the working tree")"
-
-		# dereference source url relative to parent's url
-		realrepo=$(git submodule--helper resolve-relative-url "$repo") || exit
-		;;
-	*:*|/*)
-		# absolute url
-		realrepo=$repo
-		;;
-	*)
-		die "fatal: $(eval_gettext "repo URL: '\$repo' must be absolute or begin with ./|../")"
-	;;
-	esac
-
-	# normalize path:
-	# multiple //; leading ./; /./; /../; trailing /
-	sm_path=$(printf '%s/\n' "$sm_path" |
-		sed -e '
-			s|//*|/|g
-			s|^\(\./\)*||
-			s|/\(\./\)*|/|g
-			:start
-			s|\([^/]*\)/\.\./||
-			tstart
-			s|/*$||
-		')
-	if test -z "$force"
-	then
-		git ls-files --error-unmatch "$sm_path" > /dev/null 2>&1 &&
-		die "fatal: $(eval_gettext "'\$sm_path' already exists in the index")"
-	else
-		git ls-files -s "$sm_path" | sane_grep -v "^160000" > /dev/null 2>&1 &&
-		die "fatal: $(eval_gettext "'\$sm_path' already exists in the index and is not a submodule")"
-	fi
-
-	if test -d "$sm_path" &&
-		test -z $(git -C "$sm_path" rev-parse --show-cdup 2>/dev/null)
-	then
-	    git -C "$sm_path" rev-parse --verify -q HEAD >/dev/null ||
-	    die "fatal: $(eval_gettext "'\$sm_path' does not have a commit checked out")"
-	fi
-
-	if test -z "$force"
-	then
-	    dryerr=$(git add --dry-run --ignore-missing --no-warn-embedded-repo "$sm_path" 2>&1 >/dev/null)
-	    res=$?
-	    if test $res -ne 0
-	    then
-		 echo >&2 "$dryerr"
-		 exit $res
-	    fi
-	fi
-
-	if test -n "$custom_name"
-	then
-		sm_name="$custom_name"
-	else
-		sm_name="$sm_path"
-	fi
-
-	if ! git submodule--helper check-name "$sm_name"
-	then
-		die "fatal: $(eval_gettext "'$sm_name' is not a valid submodule name")"
-	fi
-
-	git submodule--helper add-clone ${GIT_QUIET:+--quiet} ${force:+"--force"} ${progress:+"--progress"} ${branch:+--branch "$branch"} --prefix "$wt_prefix" --path "$sm_path" --name "$sm_name" --url "$realrepo" ${reference:+"$reference"} ${dissociate:+"--dissociate"} ${depth:+"$depth"} || exit
-	git submodule--helper add-config ${force:+--force} ${branch:+--branch "$branch"} --url "$repo" --resolved-url "$realrepo" --path "$sm_path" --name "$sm_name"
+	git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper add ${GIT_QUIET:+--quiet} ${force:+--force} ${progress:+"--progress"} ${branch:+--branch "$branch"} ${reference_path:+--reference "$reference_path"} ${dissociate:+--dissociate} ${custom_name:+--name "$custom_name"} ${depth:+"$depth"} -- "$@"
 }
 
 #
-- 
2.32.0
Atharva Raykar· Aug 6, 2021, 12:01 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v3 6/8] submodule--helper: remove add-clone subcommand

We no longer need this subcommand, as all of its functionality is being called by the newly-introduced `module_add()` directly within C.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 60 -------------------------------------
 1 file changed, 60 deletions(-)
Show changes to builtin/submodule--helper.c +0 −60
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index f31e5a9bde..1257066a0f 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2860,65 +2860,6 @@ static int add_submodule(const struct add_data *add_data)
 	return 0;
 }
 
-static int add_clone(int argc, const char **argv, const char *prefix)
-{
-	int force = 0, quiet = 0, dissociate = 0, progress = 0;
-	struct add_data add_data = ADD_DATA_INIT;
-
-	struct option options[] = {
-		OPT_STRING('b', "branch", &add_data.branch,
-			   N_("branch"),
-			   N_("branch of repository to checkout on cloning")),
-		OPT_STRING(0, "prefix", &prefix,
-			   N_("path"),
-			   N_("alternative anchor for relative paths")),
-		OPT_STRING(0, "path", &add_data.sm_path,
-			   N_("path"),
-			   N_("where the new submodule will be cloned to")),
-		OPT_STRING(0, "name", &add_data.sm_name,
-			   N_("string"),
-			   N_("name of the new submodule")),
-		OPT_STRING(0, "url", &add_data.realrepo,
-			   N_("string"),
-			   N_("url where to clone the submodule from")),
-		OPT_STRING(0, "reference", &add_data.reference_path,
-			   N_("repo"),
-			   N_("reference repository")),
-		OPT_BOOL(0, "dissociate", &dissociate,
-			 N_("use --reference only while cloning")),
-		OPT_INTEGER(0, "depth", &add_data.depth,
-			    N_("depth for shallow clones")),
-		OPT_BOOL(0, "progress", &progress,
-			 N_("force cloning progress")),
-		OPT__FORCE(&force, N_("allow adding an otherwise ignored submodule path"),
-			   PARSE_OPT_NOCOMPLETE),
-		OPT__QUIET(&quiet, "suppress output for cloning a submodule"),
-		OPT_END()
-	};
-
-	const char *const usage[] = {
-		N_("git submodule--helper add-clone [<options>...] "
-		   "--url <url> --path <path> --name <name>"),
-		NULL
-	};
-
-	argc = parse_options(argc, argv, prefix, options, usage, 0);
-
-	if (argc != 0)
-		usage_with_options(usage, options);
-
-	add_data.prefix = prefix;
-	add_data.progress = !!progress;
-	add_data.dissociate = !!dissociate;
-	add_data.force = !!force;
-	add_data.quiet = !!quiet;
-
-	if (add_submodule(&add_data))
-		return 1;
-
-	return 0;
-}
-
 static int config_submodule_in_gitmodules(const char *name, const char *var, const char *value)
 {
 	char *key;
@@ -3220,7 +3161,6 @@ static struct cmd_struct commands[] = {
 	{"list", module_list, 0},
 	{"name", module_name, 0},
 	{"clone", module_clone, 0},
-	{"add-clone", add_clone, 0},
 	{"add-config", add_config, 0},
 	{"add", module_add, SUPPORT_SUPER_PREFIX},
 	{"update-module-mode", module_update_module_mode, 0},
-- 
2.32.0
Atharva Raykar· Aug 6, 2021, 12:01 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v3 7/8] submodule--helper: remove add-config subcommand

Also no longer needed is this subcommand, as all of its functionality is being called by the newly-introduced `module_add()` directly within C.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 49 -------------------------------------
 1 file changed, 49 deletions(-)
Show changes to builtin/submodule--helper.c +0 −49
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 1257066a0f..a858c2be29 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2939,54 +2939,6 @@ static void configure_added_submodule(struct add_data *add_data)
 	}
 }
 
-static int add_config(int argc, const char **argv, const char *prefix)
-{
-	int force = 0;
-	struct add_data add_data = ADD_DATA_INIT;
-
-	struct option options[] = {
-		OPT_STRING('b', "branch", &add_data.branch,
-			   N_("branch"),
-			   N_("branch of repository to store in "
-			      "the submodule configuration")),
-		OPT_STRING(0, "url", &add_data.repo,
-			   N_("string"),
-			   N_("url to clone submodule from")),
-		OPT_STRING(0, "resolved-url", &add_data.realrepo,
-			   N_("string"),
-			   N_("url to clone the submodule from, after it has "
-			      "been dereferenced relative to parent's url, "
-			      "in the case where <url> is a relative url")),
-		OPT_STRING(0, "path", &add_data.sm_path,
-			   N_("path"),
-			   N_("where the new submodule will be cloned to")),
-		OPT_STRING(0, "name", &add_data.sm_name,
-			   N_("string"),
-			   N_("name of the new submodule")),
-		OPT__FORCE(&force, N_("allow adding an otherwise ignored submodule path"),
-			   PARSE_OPT_NOCOMPLETE),
-		OPT_END()
-	};
-
-	const char *const usage[] = {
-		N_("git submodule--helper add-config "
-		   "[--force|-f] [--branch|-b <branch>] "
-		   "--url <url> --resolved-url <resolved-url> "
-		   "--path <path> --name <name>"),
-		NULL
-	};
-
-	argc = parse_options(argc, argv, prefix, options, usage, 0);
-
-	if (argc)
-		usage_with_options(usage, options);
-
-	add_data.force = !!force;
-	configure_added_submodule(&add_data);
-
-	return 0;
-}
-
 static void die_on_index_match(const char *path, int force)
 {
 	struct pathspec ps;
@@ -3161,7 +3113,6 @@ static struct cmd_struct commands[] = {
 	{"list", module_list, 0},
 	{"name", module_name, 0},
 	{"clone", module_clone, 0},
-	{"add-config", add_config, 0},
 	{"add", module_add, SUPPORT_SUPER_PREFIX},
 	{"update-module-mode", module_update_module_mode, 0},
 	{"update-clone", update_clone, 0},
-- 
2.32.0
Atharva Raykar· Aug 6, 2021, 12:01 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v3 8/8] submodule--helper: remove resolve-relative-url subcommand

The shell subcommand `resolve-relative-url` is no longer required, as its last caller has been removed when it was converted to C.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 20 --------------------
 1 file changed, 20 deletions(-)
Show changes to builtin/submodule--helper.c +0 −20
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index a858c2be29..9772435959 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -223,25 +223,6 @@ static char *compute_submodule_clone_url(const char *rel_url, const char *up_pat
 	return relurl;
 }
 
-static int resolve_relative_url(int argc, const char **argv, const char *prefix)
-{
-	const char *up_path = NULL;
-	char *res;
-	const char *url;
-
-	if (argc != 2 && argc != 3)
-		die("resolve-relative-url only accepts one or two arguments");
-
-	url = argv[1];
-	if (argc == 3)
-		up_path = argv[2];
-
-	res = compute_submodule_clone_url(url, up_path, 1);
-	puts(res);
-	free(res);
-	return 0;
-}
-
 static int resolve_relative_url_test(int argc, const char **argv, const char *prefix)
 {
 	char *remoteurl, *res;
@@ -3118,7 +3099,6 @@ static struct cmd_struct commands[] = {
 	{"update-clone", update_clone, 0},
 	{"ensure-core-worktree", ensure_core_worktree, 0},
 	{"relative-path", resolve_relative_path, 0},
-	{"resolve-relative-url", resolve_relative_url, 0},
 	{"resolve-relative-url-test", resolve_relative_url_test, 0},
 	{"foreach", module_foreach, SUPPORT_SUPER_PREFIX},
 	{"init", module_init, SUPPORT_SUPER_PREFIX},
-- 
2.32.0
Atharva Raykar· Aug 7, 2021, 07:16 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v4 0/8] submodule: convert the rest of 'add' to C

Changes since v3:
* Rename the libified dir helper and update the docstring.
Atharva Raykar (8):
  submodule--helper: add options for compute_submodule_clone_url()
  submodule--helper: refactor resolve_relative_url() helper
  submodule--helper: remove repeated code in sync_submodule()
  dir: libify and export helper functions from clone.c
  submodule--helper: convert the bulk of cmd_add() to C
  submodule--helper: remove add-clone subcommand
  submodule--helper: remove add-config subcommand
  submodule--helper: remove resolve-relative-url subcommand
 builtin/clone.c             | 118 +-------------
 builtin/submodule--helper.c | 307 +++++++++++++++++++-----------------
 dir.c                       | 114 +++++++++++++
 dir.h                       |  10 ++
 git-submodule.sh            |  96 +----------
 5 files changed, 290 insertions(+), 355 deletions(-)
Range-diff against v3:
-:  ---------- > 1:  75edf24186 submodule--helper: add options for compute_submodule_clone_url()
-:  ---------- > 2:  8e7a3e727a submodule--helper: refactor resolve_relative_url() helper
-:  ---------- > 3:  82961ddd02 submodule--helper: remove repeated code in sync_submodule()
1:  99d139375d ! 4:  fa97d6801e dir: libify and export helper functions from clone.c
    @@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix)
      	else
     -		dir = guess_dir_name(repo_name, is_bundle, option_bare);
     -	strip_trailing_slashes(dir);
    -+		dir = guess_target_dir_from_git_url(repo_name, is_bundle, option_bare);
    ++		dir = git_url_basename(repo_name, is_bundle, option_bare);
     +	strip_dir_trailing_slashes(dir);
      
      	dest_exists = path_exists(dir);
    @@ dir.c: int is_empty_dir(const char *path)
      	return ret;
      }
      
    -+char *guess_target_dir_from_git_url(const char *repo, int is_bundle, int is_bare)
    ++char *git_url_basename(const char *repo, int is_bundle, int is_bare)
     +{
     +	const char *end = repo + strlen(repo), *start, *ptr;
     +	size_t len;
    @@ dir.h: static inline int is_dot_or_dotdot(const char *name)
      int is_empty_dir(const char *dir);
      
     +/*
    -+ * Retrieve a target directory name by reading "humanish" part of the
    -+ * given Git URL.
    ++ * Retrieve the "humanish" basename of the given Git URL.
     + *
     + * For example:
     + * 	/path/to/repo.git => "repo"
     + * 	host.xz.foo/.git => "foo"
     + */
    -+char *guess_target_dir_from_git_url(const char *repo, int is_bundle, int is_bare);
    ++char *git_url_basename(const char *repo, int is_bundle, int is_bare);
     +void strip_dir_trailing_slashes(char *dir);
     +
      void setup_standard_excludes(struct dir_struct *dir);
2:  11eea777ba ! 5:  a3aa25518d submodule--helper: convert the bulk of cmd_add() to C
    @@ builtin/submodule--helper.c: static int add_config(int argc, const char **argv,
     +
     +	add_data.repo = argv[0];
     +	if (argc == 1)
    -+		add_data.sm_path = guess_target_dir_from_git_url(add_data.repo, 0, 0);
    ++		add_data.sm_path = git_url_basename(add_data.repo, 0, 0);
     +	else
     +		add_data.sm_path = xstrdup(argv[1]);
     +
3:  51393cd99b = 6:  9667159d4b submodule--helper: remove add-clone subcommand
4:  50cedcd8a8 = 7:  dc87b5627a submodule--helper: remove add-config subcommand
5:  02558da532 = 8:  ea08e4fbad submodule--helper: remove resolve-relative-url subcommand
-- 
2.32.0
Atharva Raykar· Aug 7, 2021, 07:16 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v4 1/8] submodule--helper: add options for compute_submodule_clone_url()

Let's modify the interface to `compute_submodule_clone_url()` function by adding two more arguments, so that we can reuse this in various parts of `submodule--helper.c` that follow a common pattern, which is--read the remote url configuration of the superproject and then call `relative_url()`.

This function is nearly identical to `resolve_relative_url()`, the only difference being the extra warning message. We can add a quiet flag to it, to suppress that warning when not needed, and then refactor `resolve_relative_url()` by using this function, something we will do in the next patch.

Having this functionality factored out will be useful for converting the rest of `submodule add` in subsequent patches.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)
Show changes to builtin/submodule--helper.c +9 −5
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 6891480013..2d2d8ac637 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -590,7 +590,7 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
 	return 0;
 }
 
-static char *compute_submodule_clone_url(const char *rel_url)
+static char *compute_submodule_clone_url(const char *rel_url, const char *up_path, int quiet)
 {
 	char *remoteurl, *relurl;
 	char *remote = get_default_remote();
@@ -598,10 +598,14 @@ static char *compute_submodule_clone_url(const char *rel_url)
 
 	strbuf_addf(&remotesb, "remote.%s.url", remote);
 	if (git_config_get_string(remotesb.buf, &remoteurl)) {
-		warning(_("could not look up configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf);
+		if (!quiet)
+			warning(_("could not look up configuration '%s'. "
+				  "Assuming this repository is its own "
+				  "authoritative upstream."),
+				remotesb.buf);
 		remoteurl = xgetcwd();
 	}
-	relurl = relative_url(remoteurl, rel_url, NULL);
+	relurl = relative_url(remoteurl, rel_url, up_path);
 
 	free(remote);
 	free(remoteurl);
@@ -660,7 +664,7 @@ static void init_submodule(const char *path, const char *prefix,
 		if (starts_with_dot_dot_slash(url) ||
 		    starts_with_dot_slash(url)) {
 			char *oldurl = url;
-			url = compute_submodule_clone_url(oldurl);
+			url = compute_submodule_clone_url(oldurl, NULL, 0);
 			free(oldurl);
 		}
 
@@ -2134,7 +2138,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
 	if (repo_config_get_string_tmp(the_repository, sb.buf, &url)) {
 		if (starts_with_dot_slash(sub->url) ||
 		    starts_with_dot_dot_slash(sub->url)) {
-			url = compute_submodule_clone_url(sub->url);
+			url = compute_submodule_clone_url(sub->url, NULL, 0);
 			need_free_url = 1;
 		} else
 			url = sub->url;
-- 
2.32.0
Kaartic Sivaraam· Aug 8, 2021, 17:41 UTC · re: Atharva Raykar · lore

Re: [GSoC] [PATCH v4 1/8] submodule--helper: add options for compute_submodule_clone_url()

On 07/08/21 12:46 pm, Atharva Raykar wrote:
Show 9 quoted lines
> Let's modify the interface to `compute_submodule_clone_url()` function
> by adding two more arguments, so that we can reuse this in various parts
> of `submodule--helper.c` that follow a common pattern, which is--read
> the remote url configuration of the superproject and then call
> `relative_url()`.
> 
> This function is nearly identical to `resolve_relative_url()`, the only
> difference being the extra warning message. We can add a quiet flag to
> it, ...

It took me a while to figure what "it" meant in the above sentence. Does it refer to `compute_submodule_clone_url` or `resolve_relative_url`. After one sees the patch and takes a look at `resolve_relative_url`, it's clear the "it" indeed does refer to `resolve_relative_url`. But it might worth clarifying this in the commit message itself.

Certainly not worth a re-roll on its own. May be Junio could amend this while queing ?
Show 10 quoted lines
> ...  to suppress that warning when not needed, and then refactor
> `resolve_relative_url()` by using this function, something we will do in
> the next patch.
> 
> Having this functionality factored out will be useful for converting the
> rest of `submodule add` in subsequent patches.
> 
> Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
> Mentored-by: Christian Couder <christian.couder@gmail.com>
> Mentored-by: Shourya Shukla <periperidip@gmail.com>
Otherwise, this looks good.
-- 
Sivaraam
Kaartic Sivaraam· Aug 8, 2021, 18:26 UTC · re: Kaartic Sivaraam · lore

Re: [GSoC] [PATCH v4 1/8] submodule--helper: add options for compute_submodule_clone_url()

On 08/08/21 11:11 pm, Kaartic Sivaraam wrote:
Show 19 quoted lines
> On 07/08/21 12:46 pm, Atharva Raykar wrote:
>> Let's modify the interface to `compute_submodule_clone_url()` function
>> by adding two more arguments, so that we can reuse this in various parts
>> of `submodule--helper.c` that follow a common pattern, which is--read
>> the remote url configuration of the superproject and then call
>> `relative_url()`.
>>
>> This function is nearly identical to `resolve_relative_url()`, the only
>> difference being the extra warning message. We can add a quiet flag to
>> it, ...
> 
> It took me a while to figure what "it" meant in the above sentence. Does it
> refer to `compute_submodule_clone_url` or `resolve_relative_url`. After one
> sees the patch and takes a look at `resolve_relative_url`, it's clear the "it"
> indeed does refer to `resolve_relative_url`. But it might worth clarifying this
> in the commit message itself.
> 
> Certainly not worth a re-roll on its own. May be Junio could amend this while queing ?
> 
Actually, I just noticed two other things which might be re-roll worthy. Read on ...
> -static char *compute_submodule_clone_url(const char *rel_url)
> +static char *compute_submodule_clone_url(const char *rel_url, const char *up_path, int quiet)
>  {
>  	char *remoteurl, *relurl;

I know this isn't new code. But there's already an argument names 'rel_url'. So, a variable named 'relurl' in the same scope is making it hard to distinguish between these two. Could you also try distinguishing these better by renaming 'relurl' to 'res' or something else?

Show 15 quoted lines
>  	char *remote = get_default_remote();
> @@ -598,10 +598,14 @@ static char *compute_submodule_clone_url(const char *rel_url)
>  
>  	strbuf_addf(&remotesb, "remote.%s.url", remote);
>  	if (git_config_get_string(remotesb.buf, &remoteurl)) {
> -		warning(_("could not look up configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf);
> +		if (!quiet)
> +			warning(_("could not look up configuration '%s'. "
> +				  "Assuming this repository is its own "
> +				  "authoritative upstream."),
> +				remotesb.buf);
>  		remoteurl = xgetcwd();
>  	}
> -	relurl = relative_url(remoteurl, rel_url, NULL);
> +	relurl = relative_url(remoteurl, rel_url, up_path);

After reading 2/8 of the series, I just noticed that 'remoteurl' is always initialized in 'resolve_realtive_url'. It is either initialized to the return value of 'xgetcwd' or retains its assigned value of 'NULL'. But it looks like that's not the case here. 'remoteurl' could be used uninitialized when the above if block does not get executed which in turn could result in weird behaviour in case 'remoteurl' gets a value of anything other than 'NULL' at runtime.

This again has nothing to do with the change done in this patch. Regardless, it looks like something worth correcting. Thus, I thought of pointing it out.

Show 12 quoted lines
>  
>  	free(remote);
>  	free(remoteurl);
> @@ -660,7 +664,7 @@ static void init_submodule(const char *path, const char *prefix,
>  		if (starts_with_dot_dot_slash(url) ||
>  		    starts_with_dot_slash(url)) {
>  			char *oldurl = url;
> -			url = compute_submodule_clone_url(oldurl);
> +			url = compute_submodule_clone_url(oldurl, NULL, 0);
>  			free(oldurl);
>  		}
>
-- 
Sivaraam
Atharva Raykar· Aug 9, 2021, 07:29 UTC · re: Kaartic Sivaraam · lore

Re: [GSoC] [PATCH v4 1/8] submodule--helper: add options for compute_submodule_clone_url()

Kaartic Sivaraam <kaartic.sivaraam@gmail.com> writes:
Show 12 quoted lines
> On 08/08/21 11:11 pm, Kaartic Sivaraam wrote:
>> On 07/08/21 12:46 pm, Atharva Raykar wrote:
>>> [...]
>> It took me a while to figure what "it" meant in the above sentence. Does it
>> refer to `compute_submodule_clone_url` or `resolve_relative_url`. After one
>> sees the patch and takes a look at `resolve_relative_url`, it's clear the "it"
>> indeed does refer to `resolve_relative_url`. But it might worth clarifying this
>> in the commit message itself.
>> Certainly not worth a re-roll on its own. May be Junio could amend this while
>> queing ?
>>
> Actually, I just noticed two other things which might be re-roll worthy. Read on ...
I'll keep re-rolling till the code is good, it's never a problem ;-)
Show 9 quoted lines
>> -static char *compute_submodule_clone_url(const char *rel_url)
>> +static char *compute_submodule_clone_url(const char *rel_url, const char *up_path, int quiet)
>>  {
>>  	char *remoteurl, *relurl;
>
> I know this isn't new code. But there's already an argument names
> 'rel_url'. So, a variable named 'relurl' in the same scope is making it
> hard to distinguish between these two. Could you also try distinguishing
> these better by renaming 'relurl' to 'res' or something else?
Okay.
Show 26 quoted lines
>>  	char *remote = get_default_remote();
>> @@ -598,10 +598,14 @@ static char *compute_submodule_clone_url(const char *rel_url)
>>   	strbuf_addf(&remotesb, "remote.%s.url", remote);
>>  	if (git_config_get_string(remotesb.buf, &remoteurl)) {
>> -		warning(_("could not look up configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf);
>> +		if (!quiet)
>> +			warning(_("could not look up configuration '%s'. "
>> +				  "Assuming this repository is its own "
>> +				  "authoritative upstream."),
>> +				remotesb.buf);
>>  		remoteurl = xgetcwd();
>>  	}
>> -	relurl = relative_url(remoteurl, rel_url, NULL);
>> +	relurl = relative_url(remoteurl, rel_url, up_path);
>
> After reading 2/8 of the series, I just noticed that 'remoteurl' is always
> initialized in 'resolve_realtive_url'. It is either initialized to the return
> value of 'xgetcwd' or retains its assigned value of 'NULL'. But it looks
> like that's not the case here. 'remoteurl' could be used uninitialized
> when the above if block does not get executed which in turn could result in
> weird behaviour in case 'remoteurl' gets a value of anything other than 'NULL'
> at runtime.
>
> This again has nothing to do with the change done in this patch. Regardless, it
> looks like something worth correcting. Thus, I thought of pointing it out.
>
Right. I agree it should be corrected.
Show 11 quoted lines
>>   	free(remote);
>>  	free(remoteurl);
>> @@ -660,7 +664,7 @@ static void init_submodule(const char *path, const char *prefix,
>>  		if (starts_with_dot_dot_slash(url) ||
>>  		    starts_with_dot_slash(url)) {
>>  			char *oldurl = url;
>> -			url = compute_submodule_clone_url(oldurl);
>> +			url = compute_submodule_clone_url(oldurl, NULL, 0);
>>  			free(oldurl);
>>  		}
>>
Atharva Raykar· Aug 9, 2021, 08:47 UTC · re: Atharva Raykar · lore

Re: [GSoC] [PATCH v4 1/8] submodule--helper: add options for compute_submodule_clone_url()

Atharva Raykar <raykar.ath@gmail.com> writes:
Show 33 quoted lines
> Kaartic Sivaraam <kaartic.sivaraam@gmail.com> writes:
>
>> On 08/08/21 11:11 pm, Kaartic Sivaraam wrote:
>>> On 07/08/21 12:46 pm, Atharva Raykar wrote:
>>> [...]
>>>  	char *remote = get_default_remote();
>>> @@ -598,10 +598,14 @@ static char *compute_submodule_clone_url(const char *rel_url)
>>>   	strbuf_addf(&remotesb, "remote.%s.url", remote);
>>>  	if (git_config_get_string(remotesb.buf, &remoteurl)) {
>>> -		warning(_("could not look up configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf);
>>> +		if (!quiet)
>>> +			warning(_("could not look up configuration '%s'. "
>>> +				  "Assuming this repository is its own "
>>> +				  "authoritative upstream."),
>>> +				remotesb.buf);
>>>  		remoteurl = xgetcwd();
>>>  	}
>>> -	relurl = relative_url(remoteurl, rel_url, NULL);
>>> +	relurl = relative_url(remoteurl, rel_url, up_path);
>>
>> After reading 2/8 of the series, I just noticed that 'remoteurl' is always
>> initialized in 'resolve_realtive_url'. It is either initialized to the return
>> value of 'xgetcwd' or retains its assigned value of 'NULL'. But it looks
>> like that's not the case here. 'remoteurl' could be used uninitialized
>> when the above if block does not get executed which in turn could result in
>> weird behaviour in case 'remoteurl' gets a value of anything other than 'NULL'
>> at runtime.
>>
>> This again has nothing to do with the change done in this patch. Regardless, it
>> looks like something worth correcting. Thus, I thought of pointing it out.
>>
>
> Right. I agree it should be corrected.

Actually on having another look, I'm not sure if we need to assign NULL to 'remoteurl' at all.

The 'if (git_config_get_string(...))' on success will allocate 'remoteurl'. If it fails, it will be given the return value of 'xgetcwd()'. There is nothing in the config API docs that suggest a success mode for the git_config_get_*() functions that will assign nothing to the buffer we give it. Therefore, by the time we get to the variable's first use in the 'relative_url()' function, we are guaranteed to have a well-defined value.

It seems to me that the original 'resolve_relative_url()' had an unnecessary NULL initialization.

Kaartic Sivaraam· Aug 10, 2021, 17:36 UTC · re: Atharva Raykar · lore

Re: [GSoC] [PATCH v4 1/8] submodule--helper: add options for compute_submodule_clone_url()

On 09/08/21 2:17 pm, Atharva Raykar wrote:
Show 48 quoted lines
> 
> Atharva Raykar <raykar.ath@gmail.com> writes:
> 
>> Kaartic Sivaraam <kaartic.sivaraam@gmail.com> writes:
>>
>>> On 08/08/21 11:11 pm, Kaartic Sivaraam wrote:
>>>> On 07/08/21 12:46 pm, Atharva Raykar wrote:
>>>> [...]
>>>>   	char *remote = get_default_remote();
>>>> @@ -598,10 +598,14 @@ static char *compute_submodule_clone_url(const char *rel_url)
>>>>    	strbuf_addf(&remotesb, "remote.%s.url", remote);
>>>>   	if (git_config_get_string(remotesb.buf, &remoteurl)) {
>>>> -		warning(_("could not look up configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf);
>>>> +		if (!quiet)
>>>> +			warning(_("could not look up configuration '%s'. "
>>>> +				  "Assuming this repository is its own "
>>>> +				  "authoritative upstream."),
>>>> +				remotesb.buf);
>>>>   		remoteurl = xgetcwd();
>>>>   	}
>>>> -	relurl = relative_url(remoteurl, rel_url, NULL);
>>>> +	relurl = relative_url(remoteurl, rel_url, up_path);
>>>
>>> After reading 2/8 of the series, I just noticed that 'remoteurl' is always
>>> initialized in 'resolve_realtive_url'. It is either initialized to the return
>>> value of 'xgetcwd' or retains its assigned value of 'NULL'. But it looks
>>> like that's not the case here. 'remoteurl' could be used uninitialized
>>> when the above if block does not get executed which in turn could result in
>>> weird behaviour in case 'remoteurl' gets a value of anything other than 'NULL'
>>> at runtime.
>>>
>>> This again has nothing to do with the change done in this patch. Regardless, it
>>> looks like something worth correcting. Thus, I thought of pointing it out.
>>>
>>
>> Right. I agree it should be corrected.
> 
> Actually on having another look, I'm not sure if we need to assign NULL
> to 'remoteurl' at all.
> 
> The 'if (git_config_get_string(...))' on success will allocate
> 'remoteurl'. If it fails, it will be given the return value of
> 'xgetcwd()'. There is nothing in the config API docs that suggest a
> success mode for the git_config_get_*() functions that will assign
> nothing to the buffer we give it. Therefore, by the time we get to the
> variable's first use in the 'relative_url()' function, we are guaranteed
> to have a well-defined value.
> 

Ah ha! That explains why we haven't got any reports about weird behaviours when using the likes of `git submodule init` so far ;-)

Thanks for digging this and sorry about the false flag!
> It seems to me that the original 'resolve_relative_url()' had an
> unnecessary NULL initialization.
> 

Makes sense. I guess I feel into the trap of blindly trusting that the original code was written correctly x-<

-- 
Sivaraam
Atharva Raykar· Aug 7, 2021, 07:16 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v4 2/8] submodule--helper: refactor resolve_relative_url() helper

Refactor the helper function to resolve a relative url, by reusing the existing `compute_submodule_clone_url()` function.

`compute_submodule_clone_url()` performs the same work that `resolve_relative_url()` is doing, so we eliminate this code repetition by moving the former function's definition up, and calling it inside `resolve_relative_url()`.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 61 +++++++++++++++----------------------
 1 file changed, 25 insertions(+), 36 deletions(-)
Show changes to builtin/submodule--helper.c +25 −36
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 2d2d8ac637..f4b496bac6 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -199,33 +199,46 @@ static char *relative_url(const char *remote_url,
 	return strbuf_detach(&sb, NULL);
 }
 
+static char *compute_submodule_clone_url(const char *rel_url, const char *up_path, int quiet)
+{
+	char *remoteurl, *relurl;
+	char *remote = get_default_remote();
+	struct strbuf remotesb = STRBUF_INIT;
+
+	strbuf_addf(&remotesb, "remote.%s.url", remote);
+	if (git_config_get_string(remotesb.buf, &remoteurl)) {
+		if (!quiet)
+			warning(_("could not look up configuration '%s'. "
+				  "Assuming this repository is its own "
+				  "authoritative upstream."),
+				remotesb.buf);
+		remoteurl = xgetcwd();
+	}
+	relurl = relative_url(remoteurl, rel_url, up_path);
+
+	free(remote);
+	free(remoteurl);
+	strbuf_release(&remotesb);
+
+	return relurl;
+}
+
 static int resolve_relative_url(int argc, const char **argv, const char *prefix)
 {
-	char *remoteurl = NULL;
-	char *remote = get_default_remote();
 	const char *up_path = NULL;
 	char *res;
 	const char *url;
-	struct strbuf sb = STRBUF_INIT;
 
 	if (argc != 2 && argc != 3)
 		die("resolve-relative-url only accepts one or two arguments");
 
 	url = argv[1];
-	strbuf_addf(&sb, "remote.%s.url", remote);
-	free(remote);
-
-	if (git_config_get_string(sb.buf, &remoteurl))
-		/* the repository is its own authoritative upstream */
-		remoteurl = xgetcwd();
-
 	if (argc == 3)
 		up_path = argv[2];
 
-	res = relative_url(remoteurl, url, up_path);
+	res = compute_submodule_clone_url(url, up_path, 1);
 	puts(res);
 	free(res);
-	free(remoteurl);
 	return 0;
 }
 
@@ -590,30 +603,6 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
 	return 0;
 }
 
-static char *compute_submodule_clone_url(const char *rel_url, const char *up_path, int quiet)
-{
-	char *remoteurl, *relurl;
-	char *remote = get_default_remote();
-	struct strbuf remotesb = STRBUF_INIT;
-
-	strbuf_addf(&remotesb, "remote.%s.url", remote);
-	if (git_config_get_string(remotesb.buf, &remoteurl)) {
-		if (!quiet)
-			warning(_("could not look up configuration '%s'. "
-				  "Assuming this repository is its own "
-				  "authoritative upstream."),
-				remotesb.buf);
-		remoteurl = xgetcwd();
-	}
-	relurl = relative_url(remoteurl, rel_url, up_path);
-
-	free(remote);
-	free(remoteurl);
-	strbuf_release(&remotesb);
-
-	return relurl;
-}
-
 struct init_cb {
 	const char *prefix;
 	unsigned int flags;
-- 
2.32.0
Atharva Raykar· Aug 7, 2021, 07:16 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v4 3/8] submodule--helper: remove repeated code in sync_submodule()

This part of `sync_submodule()` is doing the same thing that `compute_submodule_clone_url()` is doing. Let's reuse that helper here.

Note that this change adds a small overhead where we allocate and free the 'remote' twice, but that is a small price to pay for the higher level of abstraction we get.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 16 +++-------------
 1 file changed, 3 insertions(+), 13 deletions(-)
Show changes to builtin/submodule--helper.c +3 −13
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index f4b496bac6..9b676c12f8 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1373,20 +1373,10 @@ static void sync_submodule(const char *path, const char *prefix,
 	if (sub && sub->url) {
 		if (starts_with_dot_dot_slash(sub->url) ||
 		    starts_with_dot_slash(sub->url)) {
-			char *remote_url, *up_path;
-			char *remote = get_default_remote();
-			strbuf_addf(&sb, "remote.%s.url", remote);
-
-			if (git_config_get_string(sb.buf, &remote_url))
-				remote_url = xgetcwd();
-
-			up_path = get_up_path(path);
-			sub_origin_url = relative_url(remote_url, sub->url, up_path);
-			super_config_url = relative_url(remote_url, sub->url, NULL);
-
-			free(remote);
+			char *up_path = get_up_path(path);
+			sub_origin_url = compute_submodule_clone_url(sub->url, up_path, 1);
+			super_config_url = compute_submodule_clone_url(sub->url, NULL, 1);
 			free(up_path);
-			free(remote_url);
 		} else {
 			sub_origin_url = xstrdup(sub->url);
 			super_config_url = xstrdup(sub->url);
-- 
2.32.0
Kaartic Sivaraam· Aug 8, 2021, 19:00 UTC · re: Atharva Raykar · lore

Re: [GSoC] [PATCH v4 3/8] submodule--helper: remove repeated code in sync_submodule()

On 07/08/21 12:46 pm, Atharva Raykar wrote:
> This part of `sync_submodule()` is doing the same thing that
> `compute_submodule_clone_url()` is doing. Let's reuse that helper here.
> 

Nice to see more code redundancy being removed. Now that we're using 'compute_submodule_clone_url' in multiple places, I'm starting to wonder if the name still suits the helper. Yeah, I just started yet another naming discussion ;-) I guess this one wouldn't be tough though. It feels to me like 'resolve_relative_url' is a good enough name that doesn't mislead readers by having 'clone_url' in its name. In case anyone else has better name suggestions, they are indeed very welcome to suggest those :-)

Once there's agreement on a particular name, I think the helper function could be renamed. Possibly in a new patch next to this one.

Show 40 quoted lines
> Note that this change adds a small overhead where we allocate and free
> the 'remote' twice, but that is a small price to pay for the higher
> level of abstraction we get.
> 
> Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
> Mentored-by: Christian Couder <christian.couder@gmail.com>
> Mentored-by: Shourya Shukla <periperidip@gmail.com>
> ---
>   builtin/submodule--helper.c | 16 +++-------------
>   1 file changed, 3 insertions(+), 13 deletions(-)
> 
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index f4b496bac6..9b676c12f8 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -1373,20 +1373,10 @@ static void sync_submodule(const char *path, const char *prefix,
>   	if (sub && sub->url) {
>   		if (starts_with_dot_dot_slash(sub->url) ||
>   		    starts_with_dot_slash(sub->url)) {
> -			char *remote_url, *up_path;
> -			char *remote = get_default_remote();
> -			strbuf_addf(&sb, "remote.%s.url", remote);
> -
> -			if (git_config_get_string(sb.buf, &remote_url))
> -				remote_url = xgetcwd();
> -
> -			up_path = get_up_path(path);
> -			sub_origin_url = relative_url(remote_url, sub->url, up_path);
> -			super_config_url = relative_url(remote_url, sub->url, NULL);
> -
> -			free(remote);
> +			char *up_path = get_up_path(path);
> +			sub_origin_url = compute_submodule_clone_url(sub->url, up_path, 1);
> +			super_config_url = compute_submodule_clone_url(sub->url, NULL, 1);
>   			free(up_path);
> -			free(remote_url);
>   		} else {
>   			sub_origin_url = xstrdup(sub->url);
>   			super_config_url = xstrdup(sub->url);
> 
-- 
Sivaraam
Atharva Raykar· Aug 9, 2021, 07:36 UTC · re: Kaartic Sivaraam · lore

Re: [GSoC] [PATCH v4 3/8] submodule--helper: remove repeated code in sync_submodule()

Kaartic Sivaraam <kaartic.sivaraam@gmail.com> writes:
Show 16 quoted lines
> On 07/08/21 12:46 pm, Atharva Raykar wrote:
>> This part of `sync_submodule()` is doing the same thing that
>> `compute_submodule_clone_url()` is doing. Let's reuse that helper here.
>>
>
> Nice to see more code redundancy being removed. Now that we're using
> 'compute_submodule_clone_url' in multiple places, I'm starting to
> wonder if the name still suits the helper. Yeah, I just started yet
> another naming discussion ;-) I guess this one wouldn't be tough though.
> It feels to me like 'resolve_relative_url' is a good enough name that
> doesn't mislead readers by having 'clone_url' in its name. In case anyone
> else has better name suggestions, they are indeed very welcome to suggest
> those :-)
>
> Once there's agreement on a particular name, I think the helper function
> could be renamed. Possibly in a new patch next to this one.

I don't mind the rename back to resolve_relative_url(), although it definitely has to come in a separate patch. I didn't want to create a situation where readers will be confused about what actually happened with that function. I felt a thing might happen if I repurpose it from subcommand to internal helper in the same patch.

Show 38 quoted lines
>> Note that this change adds a small overhead where we allocate and free
>> the 'remote' twice, but that is a small price to pay for the higher
>> level of abstraction we get.
>> Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
>> Mentored-by: Christian Couder <christian.couder@gmail.com>
>> Mentored-by: Shourya Shukla <periperidip@gmail.com>
>> ---
>>   builtin/submodule--helper.c | 16 +++-------------
>>   1 file changed, 3 insertions(+), 13 deletions(-)
>> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
>> index f4b496bac6..9b676c12f8 100644
>> --- a/builtin/submodule--helper.c
>> +++ b/builtin/submodule--helper.c
>> @@ -1373,20 +1373,10 @@ static void sync_submodule(const char *path, const char *prefix,
>>   	if (sub && sub->url) {
>>   		if (starts_with_dot_dot_slash(sub->url) ||
>>   		    starts_with_dot_slash(sub->url)) {
>> -			char *remote_url, *up_path;
>> -			char *remote = get_default_remote();
>> -			strbuf_addf(&sb, "remote.%s.url", remote);
>> -
>> -			if (git_config_get_string(sb.buf, &remote_url))
>> -				remote_url = xgetcwd();
>> -
>> -			up_path = get_up_path(path);
>> -			sub_origin_url = relative_url(remote_url, sub->url, up_path);
>> -			super_config_url = relative_url(remote_url, sub->url, NULL);
>> -
>> -			free(remote);
>> +			char *up_path = get_up_path(path);
>> +			sub_origin_url = compute_submodule_clone_url(sub->url, up_path, 1);
>> +			super_config_url = compute_submodule_clone_url(sub->url, NULL, 1);
>>   			free(up_path);
>> -			free(remote_url);
>>   		} else {
>>   			sub_origin_url = xstrdup(sub->url);
>>   			super_config_url = xstrdup(sub->url);
>>
Atharva Raykar· Aug 7, 2021, 07:16 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v4 4/8] dir: libify and export helper functions from clone.c

These functions can be useful to other parts of Git. Let's move them to dir.c, while renaming them to be make their functionality more explicit.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/clone.c | 118 +-----------------------------------------------
 dir.c           | 114 ++++++++++++++++++++++++++++++++++++++++++++++
 dir.h           |  10 ++++
 3 files changed, 126 insertions(+), 116 deletions(-)
Show changes to 3 files +126 −116

builtin/clone.c, dir.c, dir.h

diff --git a/builtin/clone.c b/builtin/clone.c
index 66fe66679c..5ba24b7ae7 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -217,120 +217,6 @@ static char *get_repo_path(const char *repo, int *is_bundle)
 	return canon;
 }
 
-static char *guess_dir_name(const char *repo, int is_bundle, int is_bare)
-{
-	const char *end = repo + strlen(repo), *start, *ptr;
-	size_t len;
-	char *dir;
-
-	/*
-	 * Skip scheme.
-	 */
-	start = strstr(repo, "://");
-	if (start == NULL)
-		start = repo;
-	else
-		start += 3;
-
-	/*
-	 * Skip authentication data. The stripping does happen
-	 * greedily, such that we strip up to the last '@' inside
-	 * the host part.
-	 */
-	for (ptr = start; ptr < end && !is_dir_sep(*ptr); ptr++) {
-		if (*ptr == '@')
-			start = ptr + 1;
-	}
-
-	/*
-	 * Strip trailing spaces, slashes and /.git
-	 */
-	while (start < end && (is_dir_sep(end[-1]) || isspace(end[-1])))
-		end--;
-	if (end - start > 5 && is_dir_sep(end[-5]) &&
-	    !strncmp(end - 4, ".git", 4)) {
-		end -= 5;
-		while (start < end && is_dir_sep(end[-1]))
-			end--;
-	}
-
-	/*
-	 * Strip trailing port number if we've got only a
-	 * hostname (that is, there is no dir separator but a
-	 * colon). This check is required such that we do not
-	 * strip URI's like '/foo/bar:2222.git', which should
-	 * result in a dir '2222' being guessed due to backwards
-	 * compatibility.
-	 */
-	if (memchr(start, '/', end - start) == NULL
-	    && memchr(start, ':', end - start) != NULL) {
-		ptr = end;
-		while (start < ptr && isdigit(ptr[-1]) && ptr[-1] != ':')
-			ptr--;
-		if (start < ptr && ptr[-1] == ':')
-			end = ptr - 1;
-	}
-
-	/*
-	 * Find last component. To remain backwards compatible we
-	 * also regard colons as path separators, such that
-	 * cloning a repository 'foo:bar.git' would result in a
-	 * directory 'bar' being guessed.
-	 */
-	ptr = end;
-	while (start < ptr && !is_dir_sep(ptr[-1]) && ptr[-1] != ':')
-		ptr--;
-	start = ptr;
-
-	/*
-	 * Strip .{bundle,git}.
-	 */
-	len = end - start;
-	strip_suffix_mem(start, &len, is_bundle ? ".bundle" : ".git");
-
-	if (!len || (len == 1 && *start == '/'))
-		die(_("No directory name could be guessed.\n"
-		      "Please specify a directory on the command line"));
-
-	if (is_bare)
-		dir = xstrfmt("%.*s.git", (int)len, start);
-	else
-		dir = xstrndup(start, len);
-	/*
-	 * Replace sequences of 'control' characters and whitespace
-	 * with one ascii space, remove leading and trailing spaces.
-	 */
-	if (*dir) {
-		char *out = dir;
-		int prev_space = 1 /* strip leading whitespace */;
-		for (end = dir; *end; ++end) {
-			char ch = *end;
-			if ((unsigned char)ch < '\x20')
-				ch = '\x20';
-			if (isspace(ch)) {
-				if (prev_space)
-					continue;
-				prev_space = 1;
-			} else
-				prev_space = 0;
-			*out++ = ch;
-		}
-		*out = '\0';
-		if (out > dir && prev_space)
-			out[-1] = '\0';
-	}
-	return dir;
-}
-
-static void strip_trailing_slashes(char *dir)
-{
-	char *end = dir + strlen(dir);
-
-	while (dir < end - 1 && is_dir_sep(end[-1]))
-		end--;
-	*end = '\0';
-}
-
 static int add_one_reference(struct string_list_item *item, void *cb_data)
 {
 	struct strbuf err = STRBUF_INIT;
@@ -1041,8 +927,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 	if (argc == 2)
 		dir = xstrdup(argv[1]);
 	else
-		dir = guess_dir_name(repo_name, is_bundle, option_bare);
-	strip_trailing_slashes(dir);
+		dir = git_url_basename(repo_name, is_bundle, option_bare);
+	strip_dir_trailing_slashes(dir);
 
 	dest_exists = path_exists(dir);
 	if (dest_exists && !is_empty_dir(dir))
diff --git a/dir.c b/dir.c
index 03c4d21267..6d26db3189 100644
--- a/dir.c
+++ b/dir.c
@@ -2970,6 +2970,120 @@ int is_empty_dir(const char *path)
 	return ret;
 }
 
+char *git_url_basename(const char *repo, int is_bundle, int is_bare)
+{
+	const char *end = repo + strlen(repo), *start, *ptr;
+	size_t len;
+	char *dir;
+
+	/*
+	 * Skip scheme.
+	 */
+	start = strstr(repo, "://");
+	if (start == NULL)
+		start = repo;
+	else
+		start += 3;
+
+	/*
+	 * Skip authentication data. The stripping does happen
+	 * greedily, such that we strip up to the last '@' inside
+	 * the host part.
+	 */
+	for (ptr = start; ptr < end && !is_dir_sep(*ptr); ptr++) {
+		if (*ptr == '@')
+			start = ptr + 1;
+	}
+
+	/*
+	 * Strip trailing spaces, slashes and /.git
+	 */
+	while (start < end && (is_dir_sep(end[-1]) || isspace(end[-1])))
+		end--;
+	if (end - start > 5 && is_dir_sep(end[-5]) &&
+	    !strncmp(end - 4, ".git", 4)) {
+		end -= 5;
+		while (start < end && is_dir_sep(end[-1]))
+			end--;
+	}
+
+	/*
+	 * Strip trailing port number if we've got only a
+	 * hostname (that is, there is no dir separator but a
+	 * colon). This check is required such that we do not
+	 * strip URI's like '/foo/bar:2222.git', which should
+	 * result in a dir '2222' being guessed due to backwards
+	 * compatibility.
+	 */
+	if (memchr(start, '/', end - start) == NULL
+	    && memchr(start, ':', end - start) != NULL) {
+		ptr = end;
+		while (start < ptr && isdigit(ptr[-1]) && ptr[-1] != ':')
+			ptr--;
+		if (start < ptr && ptr[-1] == ':')
+			end = ptr - 1;
+	}
+
+	/*
+	 * Find last component. To remain backwards compatible we
+	 * also regard colons as path separators, such that
+	 * cloning a repository 'foo:bar.git' would result in a
+	 * directory 'bar' being guessed.
+	 */
+	ptr = end;
+	while (start < ptr && !is_dir_sep(ptr[-1]) && ptr[-1] != ':')
+		ptr--;
+	start = ptr;
+
+	/*
+	 * Strip .{bundle,git}.
+	 */
+	len = end - start;
+	strip_suffix_mem(start, &len, is_bundle ? ".bundle" : ".git");
+
+	if (!len || (len == 1 && *start == '/'))
+		die(_("No directory name could be guessed.\n"
+		      "Please specify a directory on the command line"));
+
+	if (is_bare)
+		dir = xstrfmt("%.*s.git", (int)len, start);
+	else
+		dir = xstrndup(start, len);
+	/*
+	 * Replace sequences of 'control' characters and whitespace
+	 * with one ascii space, remove leading and trailing spaces.
+	 */
+	if (*dir) {
+		char *out = dir;
+		int prev_space = 1 /* strip leading whitespace */;
+		for (end = dir; *end; ++end) {
+			char ch = *end;
+			if ((unsigned char)ch < '\x20')
+				ch = '\x20';
+			if (isspace(ch)) {
+				if (prev_space)
+					continue;
+				prev_space = 1;
+			} else
+				prev_space = 0;
+			*out++ = ch;
+		}
+		*out = '\0';
+		if (out > dir && prev_space)
+			out[-1] = '\0';
+	}
+	return dir;
+}
+
+void strip_dir_trailing_slashes(char *dir)
+{
+	char *end = dir + strlen(dir);
+
+	while (dir < end - 1 && is_dir_sep(end[-1]))
+		end--;
+	*end = '\0';
+}
+
 static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up)
 {
 	DIR *dir;
diff --git a/dir.h b/dir.h
index b3e1a54a97..a4a6fd7371 100644
--- a/dir.h
+++ b/dir.h
@@ -453,6 +453,16 @@ static inline int is_dot_or_dotdot(const char *name)
 
 int is_empty_dir(const char *dir);
 
+/*
+ * Retrieve the "humanish" basename of the given Git URL.
+ *
+ * For example:
+ * 	/path/to/repo.git => "repo"
+ * 	host.xz.foo/.git => "foo"
+ */
+char *git_url_basename(const char *repo, int is_bundle, int is_bare);
+void strip_dir_trailing_slashes(char *dir);
+
 void setup_standard_excludes(struct dir_struct *dir);
 
 char *get_sparse_checkout_filename(void);
-- 
2.32.0
Kaartic Sivaraam· Aug 8, 2021, 19:23 UTC · re: Atharva Raykar · lore

Re: [GSoC] [PATCH v4 4/8] dir: libify and export helper functions from clone.c

On 07/08/21 12:46 pm, Atharva Raykar wrote:
Show 17 quoted lines
>> [ ... ]
>
> diff --git a/dir.h b/dir.h
> index b3e1a54a97..a4a6fd7371 100644
> --- a/dir.h
> +++ b/dir.h
> @@ -453,6 +453,16 @@ static inline int is_dot_or_dotdot(const char *name)
>   
>   int is_empty_dir(const char *dir);
>   
> +/*
> + * Retrieve the "humanish" basename of the given Git URL.
> + *
> + * For example:
> + * 	/path/to/repo.git => "repo"
> + * 	host.xz.foo/.git => "foo"
> + */
Are you sure about the examples here? I just tried and ...
   - '/path/to/repo.git' gave me 'repo' like you said
.. but ..
   - 'host.xz.foo/.git' gives me 'host.xz.foo' instead of 'foo'.
     I think you meant to have 'host.xz/foo.git' in the example.
Also, here's another example that might be useful to mention in the docstring:
   - 'http://example.com/user/bar.baz' => 'bar.baz'
Show 7 quoted lines
> +char *git_url_basename(const char *repo, int is_bundle, int is_bare);
> +void strip_dir_trailing_slashes(char *dir);
> +
>   void setup_standard_excludes(struct dir_struct *dir);
>   
>   char *get_sparse_checkout_filename(void);
> 
-- 
Sivaraam
Atharva Raykar· Aug 9, 2021, 08:02 UTC · re: Kaartic Sivaraam · lore

Re: [GSoC] [PATCH v4 4/8] dir: libify and export helper functions from clone.c

Kaartic Sivaraam <kaartic.sivaraam@gmail.com> writes:
Show 25 quoted lines
> On 07/08/21 12:46 pm, Atharva Raykar wrote:
>>> [ ... ]
>>
>> diff --git a/dir.h b/dir.h
>> index b3e1a54a97..a4a6fd7371 100644
>> --- a/dir.h
>> +++ b/dir.h
>> @@ -453,6 +453,16 @@ static inline int is_dot_or_dotdot(const char *name)
>>     int is_empty_dir(const char *dir);
>>   +/*
>> + * Retrieve the "humanish" basename of the given Git URL.
>> + *
>> + * For example:
>> + * 	/path/to/repo.git => "repo"
>> + * 	host.xz.foo/.git => "foo"
>> + */
>
> Are you sure about the examples here? I just tried and ...
>
>   - '/path/to/repo.git' gave me 'repo' like you said
>
> .. but ..
>
>   - 'host.xz.foo/.git' gives me 'host.xz.foo' instead of 'foo'.
>     I think you meant to have 'host.xz/foo.git' in the example.

Yikes! I meant 'host.xz:foo/.git'. That should give us 'foo'. Thanks for the correction.

> Also, here's another example that might be useful to mention in the docstring:
>
>   - 'http://example.com/user/bar.baz' => 'bar.baz'

Yeah, especially since that's probably the most familiar example for most end users.

Show 6 quoted lines
>> +char *git_url_basename(const char *repo, int is_bundle, int is_bare);
>> +void strip_dir_trailing_slashes(char *dir);
>> +
>>   void setup_standard_excludes(struct dir_struct *dir);
>>     char *get_sparse_checkout_filename(void);
>>
Kaartic Sivaraam· Aug 10, 2021, 17:53 UTC · re: Atharva Raykar · lore

Re: [GSoC] [PATCH v4 4/8] dir: libify and export helper functions from clone.c

On 09/08/21 1:32 pm, Atharva Raykar wrote:
Show 32 quoted lines
> 
> Kaartic Sivaraam <kaartic.sivaraam@gmail.com> writes:
> 
>> On 07/08/21 12:46 pm, Atharva Raykar wrote:
>>>> [ ... ]
>>>
>>> diff --git a/dir.h b/dir.h
>>> index b3e1a54a97..a4a6fd7371 100644
>>> --- a/dir.h
>>> +++ b/dir.h
>>> @@ -453,6 +453,16 @@ static inline int is_dot_or_dotdot(const char *name)
>>>      int is_empty_dir(const char *dir);
>>>    +/*
>>> + * Retrieve the "humanish" basename of the given Git URL.
>>> + *
>>> + * For example:
>>> + * 	/path/to/repo.git => "repo"
>>> + * 	host.xz.foo/.git => "foo"
>>> + */
>>
>> Are you sure about the examples here? I just tried and ...
>>
>>    - '/path/to/repo.git' gave me 'repo' like you said
>>
>> .. but ..
>>
>>    - 'host.xz.foo/.git' gives me 'host.xz.foo' instead of 'foo'.
>>      I think you meant to have 'host.xz/foo.git' in the example.
> 
> Yikes! I meant 'host.xz:foo/.git'. That should give us 'foo'. Thanks for
> the correction.
> 
Interesting. I've usually seen host.xz:foo like syntax in HTTP URLs. For instance,
     http://host.xz:4000/bar.baz.git
`git_url_basename` returns `bar.baz` for the above.

I wonder what real-world URL has a syntax like 'host.xz:foo/.git' for which 'foo' would be an appropriate basename to return. Does a real-world URL of this form exist? Or is this just cooked up to demonstrate the basename that would be returned for a hypothetical URL like this?

-- 
Sivaraam
Junio C Hamano· Aug 10, 2021, 21:27 UTC · re: Kaartic Sivaraam · lore

Re: [GSoC] [PATCH v4 4/8] dir: libify and export helper functions from clone.c

Kaartic Sivaraam <kaartic.sivaraam@gmail.com> writes:
> I wonder what real-world URL has a syntax like 'host.xz:foo/.git' for which
> 'foo' would be an appropriate basename to return. Does a real-world URL of
> this form exist? Or is this just cooked up to demonstrate the basename that
> would be returned for a hypothetical URL like this?

It is not really a URL, but historically we've called them "scp-style URL". scp of course copied the syntax from "rcp" where <hostname> and <pathname> are separated by a colon ':' named a file or a directory at the named path on the named host.

Atharva Raykar· Aug 11, 2021, 10:25 UTC · re: Kaartic Sivaraam · lore

Re: [GSoC] [PATCH v4 4/8] dir: libify and export helper functions from clone.c

Kaartic Sivaraam <kaartic.sivaraam@gmail.com> writes:
Show 19 quoted lines
> On 09/08/21 1:32 pm, Atharva Raykar wrote:
>> Kaartic Sivaraam <kaartic.sivaraam@gmail.com> writes:
>>
>>> On 07/08/21 12:46 pm, Atharva Raykar wrote:
>>>>> [ ... ]
>> Yikes! I meant 'host.xz:foo/.git'. That should give us 'foo'. Thanks for
>> the correction.
>>
>
> Interesting. I've usually seen host.xz:foo like syntax in HTTP URLs. For instance,
>
>     http://host.xz:4000/bar.baz.git
>
> `git_url_basename` returns `bar.baz` for the above.
>
> I wonder what real-world URL has a syntax like 'host.xz:foo/.git' for which
> 'foo' would be an appropriate basename to return. Does a real-world URL of
> this form exist? Or is this just cooked up to demonstrate the basename that
> would be returned for a hypothetical URL like this?

Junio already answered all of your questions, I'll just add that I lifted that example from the git-clone documentation [1] that uses the exact same basename extraction function.

[1] https://git-scm.com/docs/git-clone#Documentation/git-clone.txt-ltdirectorygt
Atharva Raykar· Aug 7, 2021, 07:16 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v4 5/8] submodule--helper: convert the bulk of cmd_add() to C

Introduce the 'add' subcommand to `submodule--helper.c` that does all the work 'submodule add' past the parsing of flags.

We also remove the constness of the sm_path field of the `add_data` struct. This is needed so that it can be modified by normalize_path_copy().

As with the previous conversions, this is meant to be a faithful conversion with no modification to the behaviour of `submodule add`.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Helped-by: Kaartic Sivaraam <kaartic.sivaraam@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
Based-on-patch-by: Shourya Shukla <periperidip@gmail.com>
Based-on-patch-by: Prathamesh Chavan <pc44800@gmail.com>
---
 builtin/submodule--helper.c | 165 +++++++++++++++++++++++++++++++++++-
 git-submodule.sh            |  96 +--------------------
 2 files changed, 166 insertions(+), 95 deletions(-)
Show changes to 2 files +166 −95

builtin/submodule--helper.c, git-submodule.sh

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 9b676c12f8..3aa3ae8b61 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2748,7 +2748,7 @@ struct add_data {
 	const char *prefix;
 	const char *branch;
 	const char *reference_path;
-	const char *sm_path;
+	char *sm_path;
 	const char *sm_name;
 	const char *repo;
 	const char *realrepo;
@@ -3046,6 +3046,168 @@ static int add_config(int argc, const char **argv, const char *prefix)
 	return 0;
 }
 
+static void die_on_index_match(const char *path, int force)
+{
+	struct pathspec ps;
+	const char *args[] = { path, NULL };
+	parse_pathspec(&ps, 0, PATHSPEC_PREFER_CWD, NULL, args);
+
+	if (read_cache_preload(NULL) < 0)
+		die(_("index file corrupt"));
+
+	if (ps.nr) {
+		int i;
+		char *ps_matched = xcalloc(ps.nr, 1);
+
+		/* TODO: audit for interaction with sparse-index. */
+		ensure_full_index(&the_index);
+
+		/*
+		 * Since there is only one pathspec, we just need
+		 * need to check ps_matched[0] to know if a cache
+		 * entry matched.
+		 */
+		for (i = 0; i < active_nr; i++) {
+			ce_path_match(&the_index, active_cache[i], &ps,
+				      ps_matched);
+
+			if (ps_matched[0]) {
+				if (!force)
+					die(_("'%s' already exists in the index"),
+					    path);
+				if (!S_ISGITLINK(active_cache[i]->ce_mode))
+					die(_("'%s' already exists in the index "
+					      "and is not a submodule"), path);
+				break;
+			}
+		}
+		free(ps_matched);
+	}
+}
+
+static void die_on_repo_without_commits(const char *path)
+{
+	struct strbuf sb = STRBUF_INIT;
+	strbuf_addstr(&sb, path);
+	if (is_nonbare_repository_dir(&sb)) {
+		struct object_id oid;
+		if (resolve_gitlink_ref(path, "HEAD", &oid) < 0)
+			die(_("'%s' does not have a commit checked out"), path);
+	}
+}
+
+static int module_add(int argc, const char **argv, const char *prefix)
+{
+	int force = 0, quiet = 0, progress = 0, dissociate = 0;
+	struct add_data add_data = ADD_DATA_INIT;
+
+	struct option options[] = {
+		OPT_STRING('b', "branch", &add_data.branch, N_("branch"),
+			   N_("branch of repository to add as submodule")),
+		OPT__FORCE(&force, N_("allow adding an otherwise ignored submodule path"),
+			   PARSE_OPT_NOCOMPLETE),
+		OPT__QUIET(&quiet, N_("print only error messages")),
+		OPT_BOOL(0, "progress", &progress, N_("force cloning progress")),
+		OPT_STRING(0, "reference", &add_data.reference_path, N_("repository"),
+			   N_("reference repository")),
+		OPT_BOOL(0, "dissociate", &dissociate, N_("borrow the objects from reference repositories")),
+		OPT_STRING(0, "name", &add_data.sm_name, N_("name"),
+			   N_("sets the submodule’s name to the given string "
+			      "instead of defaulting to its path")),
+		OPT_INTEGER(0, "depth", &add_data.depth, N_("depth for shallow clones")),
+		OPT_END()
+	};
+
+	const char *const usage[] = {
+		N_("git submodule--helper add [<options>] [--] <repository> [<path>]"),
+		NULL
+	};
+
+	argc = parse_options(argc, argv, prefix, options, usage, 0);
+
+	if (!is_writing_gitmodules_ok())
+		die(_("please make sure that the .gitmodules file is in the working tree"));
+
+	if (prefix && *prefix &&
+	    add_data.reference_path && !is_absolute_path(add_data.reference_path))
+		add_data.reference_path = xstrfmt("%s%s", prefix, add_data.reference_path);
+
+	if (argc == 0 || argc > 2)
+		usage_with_options(usage, options);
+
+	add_data.repo = argv[0];
+	if (argc == 1)
+		add_data.sm_path = git_url_basename(add_data.repo, 0, 0);
+	else
+		add_data.sm_path = xstrdup(argv[1]);
+
+	if (prefix && *prefix && !is_absolute_path(add_data.sm_path))
+		add_data.sm_path = xstrfmt("%s%s", prefix, add_data.sm_path);
+
+	if (starts_with_dot_dot_slash(add_data.repo) ||
+	    starts_with_dot_slash(add_data.repo)) {
+		if (prefix)
+			die(_("Relative path can only be used from the toplevel "
+			      "of the working tree"));
+
+		/* dereference source url relative to parent's url */
+		add_data.realrepo = compute_submodule_clone_url(add_data.repo, NULL, 1);
+	} else if (is_dir_sep(add_data.repo[0]) || strchr(add_data.repo, ':')) {
+		add_data.realrepo = add_data.repo;
+	} else {
+		die(_("repo URL: '%s' must be absolute or begin with ./|../"),
+		    add_data.repo);
+	}
+
+	/*
+	 * normalize path:
+	 * multiple //; leading ./; /./; /../;
+	 */
+	normalize_path_copy(add_data.sm_path, add_data.sm_path);
+	strip_dir_trailing_slashes(add_data.sm_path);
+
+	die_on_index_match(add_data.sm_path, force);
+	die_on_repo_without_commits(add_data.sm_path);
+
+	if (!force) {
+		int exit_code = -1;
+		struct strbuf sb = STRBUF_INIT;
+		struct child_process cp = CHILD_PROCESS_INIT;
+		cp.git_cmd = 1;
+		cp.no_stdout = 1;
+		strvec_pushl(&cp.args, "add", "--dry-run", "--ignore-missing",
+			     "--no-warn-embedded-repo", add_data.sm_path, NULL);
+		if ((exit_code = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {
+			strbuf_complete_line(&sb);
+			fputs(sb.buf, stderr);
+			free(add_data.sm_path);
+			return exit_code;
+		}
+		strbuf_release(&sb);
+	}
+
+	if(!add_data.sm_name)
+		add_data.sm_name = add_data.sm_path;
+
+	if (check_submodule_name(add_data.sm_name))
+		die(_("'%s' is not a valid submodule name"), add_data.sm_name);
+
+	add_data.prefix = prefix;
+	add_data.force = !!force;
+	add_data.quiet = !!quiet;
+	add_data.progress = !!progress;
+	add_data.dissociate = !!dissociate;
+
+	if (add_submodule(&add_data)) {
+		free(add_data.sm_path);
+		return 1;
+	}
+	configure_added_submodule(&add_data);
+	free(add_data.sm_path);
+
+	return 0;
+}
+
 #define SUPPORT_SUPER_PREFIX (1<<0)
 
 struct cmd_struct {
@@ -3060,6 +3222,7 @@ static struct cmd_struct commands[] = {
 	{"clone", module_clone, 0},
 	{"add-clone", add_clone, 0},
 	{"add-config", add_config, 0},
+	{"add", module_add, SUPPORT_SUPER_PREFIX},
 	{"update-module-mode", module_update_module_mode, 0},
 	{"update-clone", update_clone, 0},
 	{"ensure-core-worktree", ensure_core_worktree, 0},
diff --git a/git-submodule.sh b/git-submodule.sh
index 8c219ef382..1070540525 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -145,104 +145,12 @@ cmd_add()
 		shift
 	done
 
-	if ! git submodule--helper config --check-writeable >/dev/null 2>&1
+	if test -z "$1"
 	then
-		 die "fatal: $(eval_gettext "please make sure that the .gitmodules file is in the working tree")"
-	fi
-
-	if test -n "$reference_path"
-	then
-		is_absolute_path "$reference_path" ||
-		reference_path="$wt_prefix$reference_path"
-
-		reference="--reference=$reference_path"
-	fi
-
-	repo=$1
-	sm_path=$2
-
-	if test -z "$sm_path"; then
-		sm_path=$(printf '%s\n' "$repo" |
-			sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
-	fi
-
-	if test -z "$repo" || test -z "$sm_path"; then
 		usage
 	fi
 
-	is_absolute_path "$sm_path" || sm_path="$wt_prefix$sm_path"
-
-	# assure repo is absolute or relative to parent
-	case "$repo" in
-	./*|../*)
-		test -z "$wt_prefix" ||
-		die "fatal: $(gettext "Relative path can only be used from the toplevel of the working tree")"
-
-		# dereference source url relative to parent's url
-		realrepo=$(git submodule--helper resolve-relative-url "$repo") || exit
-		;;
-	*:*|/*)
-		# absolute url
-		realrepo=$repo
-		;;
-	*)
-		die "fatal: $(eval_gettext "repo URL: '\$repo' must be absolute or begin with ./|../")"
-	;;
-	esac
-
-	# normalize path:
-	# multiple //; leading ./; /./; /../; trailing /
-	sm_path=$(printf '%s/\n' "$sm_path" |
-		sed -e '
-			s|//*|/|g
-			s|^\(\./\)*||
-			s|/\(\./\)*|/|g
-			:start
-			s|\([^/]*\)/\.\./||
-			tstart
-			s|/*$||
-		')
-	if test -z "$force"
-	then
-		git ls-files --error-unmatch "$sm_path" > /dev/null 2>&1 &&
-		die "fatal: $(eval_gettext "'\$sm_path' already exists in the index")"
-	else
-		git ls-files -s "$sm_path" | sane_grep -v "^160000" > /dev/null 2>&1 &&
-		die "fatal: $(eval_gettext "'\$sm_path' already exists in the index and is not a submodule")"
-	fi
-
-	if test -d "$sm_path" &&
-		test -z $(git -C "$sm_path" rev-parse --show-cdup 2>/dev/null)
-	then
-	    git -C "$sm_path" rev-parse --verify -q HEAD >/dev/null ||
-	    die "fatal: $(eval_gettext "'\$sm_path' does not have a commit checked out")"
-	fi
-
-	if test -z "$force"
-	then
-	    dryerr=$(git add --dry-run --ignore-missing --no-warn-embedded-repo "$sm_path" 2>&1 >/dev/null)
-	    res=$?
-	    if test $res -ne 0
-	    then
-		 echo >&2 "$dryerr"
-		 exit $res
-	    fi
-	fi
-
-	if test -n "$custom_name"
-	then
-		sm_name="$custom_name"
-	else
-		sm_name="$sm_path"
-	fi
-
-	if ! git submodule--helper check-name "$sm_name"
-	then
-		die "fatal: $(eval_gettext "'$sm_name' is not a valid submodule name")"
-	fi
-
-	git submodule--helper add-clone ${GIT_QUIET:+--quiet} ${force:+"--force"} ${progress:+"--progress"} ${branch:+--branch "$branch"} --prefix "$wt_prefix" --path "$sm_path" --name "$sm_name" --url "$realrepo" ${reference:+"$reference"} ${dissociate:+"--dissociate"} ${depth:+"$depth"} || exit
-	git submodule--helper add-config ${force:+--force} ${branch:+--branch "$branch"} --url "$repo" --resolved-url "$realrepo" --path "$sm_path" --name "$sm_name"
+	git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper add ${GIT_QUIET:+--quiet} ${force:+--force} ${progress:+"--progress"} ${branch:+--branch "$branch"} ${reference_path:+--reference "$reference_path"} ${dissociate:+--dissociate} ${custom_name:+--name "$custom_name"} ${depth:+"$depth"} -- "$@"
 }
 
 #
-- 
2.32.0
Atharva Raykar· Aug 7, 2021, 07:16 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v4 6/8] submodule--helper: remove add-clone subcommand

We no longer need this subcommand, as all of its functionality is being called by the newly-introduced `module_add()` directly within C.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 60 -------------------------------------
 1 file changed, 60 deletions(-)
Show changes to builtin/submodule--helper.c +0 −60
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 3aa3ae8b61..5932438d65 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2860,65 +2860,6 @@ static int add_submodule(const struct add_data *add_data)
 	return 0;
 }
 
-static int add_clone(int argc, const char **argv, const char *prefix)
-{
-	int force = 0, quiet = 0, dissociate = 0, progress = 0;
-	struct add_data add_data = ADD_DATA_INIT;
-
-	struct option options[] = {
-		OPT_STRING('b', "branch", &add_data.branch,
-			   N_("branch"),
-			   N_("branch of repository to checkout on cloning")),
-		OPT_STRING(0, "prefix", &prefix,
-			   N_("path"),
-			   N_("alternative anchor for relative paths")),
-		OPT_STRING(0, "path", &add_data.sm_path,
-			   N_("path"),
-			   N_("where the new submodule will be cloned to")),
-		OPT_STRING(0, "name", &add_data.sm_name,
-			   N_("string"),
-			   N_("name of the new submodule")),
-		OPT_STRING(0, "url", &add_data.realrepo,
-			   N_("string"),
-			   N_("url where to clone the submodule from")),
-		OPT_STRING(0, "reference", &add_data.reference_path,
-			   N_("repo"),
-			   N_("reference repository")),
-		OPT_BOOL(0, "dissociate", &dissociate,
-			 N_("use --reference only while cloning")),
-		OPT_INTEGER(0, "depth", &add_data.depth,
-			    N_("depth for shallow clones")),
-		OPT_BOOL(0, "progress", &progress,
-			 N_("force cloning progress")),
-		OPT__FORCE(&force, N_("allow adding an otherwise ignored submodule path"),
-			   PARSE_OPT_NOCOMPLETE),
-		OPT__QUIET(&quiet, "suppress output for cloning a submodule"),
-		OPT_END()
-	};
-
-	const char *const usage[] = {
-		N_("git submodule--helper add-clone [<options>...] "
-		   "--url <url> --path <path> --name <name>"),
-		NULL
-	};
-
-	argc = parse_options(argc, argv, prefix, options, usage, 0);
-
-	if (argc != 0)
-		usage_with_options(usage, options);
-
-	add_data.prefix = prefix;
-	add_data.progress = !!progress;
-	add_data.dissociate = !!dissociate;
-	add_data.force = !!force;
-	add_data.quiet = !!quiet;
-
-	if (add_submodule(&add_data))
-		return 1;
-
-	return 0;
-}
-
 static int config_submodule_in_gitmodules(const char *name, const char *var, const char *value)
 {
 	char *key;
@@ -3220,7 +3161,6 @@ static struct cmd_struct commands[] = {
 	{"list", module_list, 0},
 	{"name", module_name, 0},
 	{"clone", module_clone, 0},
-	{"add-clone", add_clone, 0},
 	{"add-config", add_config, 0},
 	{"add", module_add, SUPPORT_SUPER_PREFIX},
 	{"update-module-mode", module_update_module_mode, 0},
-- 
2.32.0
Atharva Raykar· Aug 7, 2021, 07:16 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v4 7/8] submodule--helper: remove add-config subcommand

Also no longer needed is this subcommand, as all of its functionality is being called by the newly-introduced `module_add()` directly within C.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 49 -------------------------------------
 1 file changed, 49 deletions(-)
Show changes to builtin/submodule--helper.c +0 −49
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 5932438d65..d526423a0c 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2939,54 +2939,6 @@ static void configure_added_submodule(struct add_data *add_data)
 	}
 }
 
-static int add_config(int argc, const char **argv, const char *prefix)
-{
-	int force = 0;
-	struct add_data add_data = ADD_DATA_INIT;
-
-	struct option options[] = {
-		OPT_STRING('b', "branch", &add_data.branch,
-			   N_("branch"),
-			   N_("branch of repository to store in "
-			      "the submodule configuration")),
-		OPT_STRING(0, "url", &add_data.repo,
-			   N_("string"),
-			   N_("url to clone submodule from")),
-		OPT_STRING(0, "resolved-url", &add_data.realrepo,
-			   N_("string"),
-			   N_("url to clone the submodule from, after it has "
-			      "been dereferenced relative to parent's url, "
-			      "in the case where <url> is a relative url")),
-		OPT_STRING(0, "path", &add_data.sm_path,
-			   N_("path"),
-			   N_("where the new submodule will be cloned to")),
-		OPT_STRING(0, "name", &add_data.sm_name,
-			   N_("string"),
-			   N_("name of the new submodule")),
-		OPT__FORCE(&force, N_("allow adding an otherwise ignored submodule path"),
-			   PARSE_OPT_NOCOMPLETE),
-		OPT_END()
-	};
-
-	const char *const usage[] = {
-		N_("git submodule--helper add-config "
-		   "[--force|-f] [--branch|-b <branch>] "
-		   "--url <url> --resolved-url <resolved-url> "
-		   "--path <path> --name <name>"),
-		NULL
-	};
-
-	argc = parse_options(argc, argv, prefix, options, usage, 0);
-
-	if (argc)
-		usage_with_options(usage, options);
-
-	add_data.force = !!force;
-	configure_added_submodule(&add_data);
-
-	return 0;
-}
-
 static void die_on_index_match(const char *path, int force)
 {
 	struct pathspec ps;
@@ -3161,7 +3113,6 @@ static struct cmd_struct commands[] = {
 	{"list", module_list, 0},
 	{"name", module_name, 0},
 	{"clone", module_clone, 0},
-	{"add-config", add_config, 0},
 	{"add", module_add, SUPPORT_SUPER_PREFIX},
 	{"update-module-mode", module_update_module_mode, 0},
 	{"update-clone", update_clone, 0},
-- 
2.32.0
Atharva Raykar· Aug 7, 2021, 07:16 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v4 8/8] submodule--helper: remove resolve-relative-url subcommand

The shell subcommand `resolve-relative-url` is no longer required, as its last caller has been removed when it was converted to C.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 20 --------------------
 1 file changed, 20 deletions(-)
Show changes to builtin/submodule--helper.c +0 −20
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index d526423a0c..bc123d7aaf 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -223,25 +223,6 @@ static char *compute_submodule_clone_url(const char *rel_url, const char *up_pat
 	return relurl;
 }
 
-static int resolve_relative_url(int argc, const char **argv, const char *prefix)
-{
-	const char *up_path = NULL;
-	char *res;
-	const char *url;
-
-	if (argc != 2 && argc != 3)
-		die("resolve-relative-url only accepts one or two arguments");
-
-	url = argv[1];
-	if (argc == 3)
-		up_path = argv[2];
-
-	res = compute_submodule_clone_url(url, up_path, 1);
-	puts(res);
-	free(res);
-	return 0;
-}
-
 static int resolve_relative_url_test(int argc, const char **argv, const char *prefix)
 {
 	char *remoteurl, *res;
@@ -3118,7 +3099,6 @@ static struct cmd_struct commands[] = {
 	{"update-clone", update_clone, 0},
 	{"ensure-core-worktree", ensure_core_worktree, 0},
 	{"relative-path", resolve_relative_path, 0},
-	{"resolve-relative-url", resolve_relative_url, 0},
 	{"resolve-relative-url-test", resolve_relative_url_test, 0},
 	{"foreach", module_foreach, SUPPORT_SUPER_PREFIX},
 	{"init", module_init, SUPPORT_SUPER_PREFIX},
-- 
2.32.0
Kaartic Sivaraam· Aug 8, 2021, 18:01 UTC · re: Atharva Raykar · lore

Re: [GSoC] [PATCH v4 0/8] submodule: convert the rest of 'add' to C

On 07/08/21 12:46 pm, Atharva Raykar wrote:
> Changes since v3:
> 
> * Rename the libified dir helper and update the docstring.
>

Just a note. I'm not really sure about this yet, the fact that this series depends on the change introduced by 'ar/submodule-add-config'[1] might be worth mentioning in re-rolls too. This could help the maintainer to easily identify the topic dependency :-)

[1]: https://lore.kernel.org/git/20210801063352.50813-1-raykar.ath@gmail.com/
And ...
Show 18 quoted lines
> Atharva Raykar (8):
>    submodule--helper: add options for compute_submodule_clone_url()
>    submodule--helper: refactor resolve_relative_url() helper
>    submodule--helper: remove repeated code in sync_submodule()
>    dir: libify and export helper functions from clone.c
>    submodule--helper: convert the bulk of cmd_add() to C
>    submodule--helper: remove add-clone subcommand
>    submodule--helper: remove add-config subcommand
>    submodule--helper: remove resolve-relative-url subcommand
> 
>   builtin/clone.c             | 118 +-------------
>   builtin/submodule--helper.c | 307 +++++++++++++++++++-----------------
>   dir.c                       | 114 +++++++++++++
>   dir.h                       |  10 ++
>   git-submodule.sh            |  96 +----------
>   5 files changed, 290 insertions(+), 355 deletions(-)
> 
> Range-diff against v3:
Thanks for consistently including a useful range-diff!
-- 
Sivaraam

> -:  ---------- > 1:  75edf24186 submodule--helper: add options for compute_submodule_clone_url()
> -:  ---------- > 2:  8e7a3e727a submodule--helper: refactor resolve_relative_url() helper
> -:  ---------- > 3:  82961ddd02 submodule--helper: remove repeated code in sync_submodule()
> 1:  99d139375d ! 4:  fa97d6801e dir: libify and export helper functions from clone.c
>      @@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix)
>        	else
>       -		dir = guess_dir_name(repo_name, is_bundle, option_bare);
>       -	strip_trailing_slashes(dir);
>      -+		dir = guess_target_dir_from_git_url(repo_name, is_bundle, option_bare);
>      ++		dir = git_url_basename(repo_name, is_bundle, option_bare);
>       +	strip_dir_trailing_slashes(dir);
>        
>        	dest_exists = path_exists(dir);
>      @@ dir.c: int is_empty_dir(const char *path)
>        	return ret;
>        }
>        
>      -+char *guess_target_dir_from_git_url(const char *repo, int is_bundle, int is_bare)
>      ++char *git_url_basename(const char *repo, int is_bundle, int is_bare)
>       +{
>       +	const char *end = repo + strlen(repo), *start, *ptr;
>       +	size_t len;
>      @@ dir.h: static inline int is_dot_or_dotdot(const char *name)
>        int is_empty_dir(const char *dir);
>        
>       +/*
>      -+ * Retrieve a target directory name by reading "humanish" part of the
>      -+ * given Git URL.
>      ++ * Retrieve the "humanish" basename of the given Git URL.
>       + *
>       + * For example:
>       + * 	/path/to/repo.git => "repo"
>       + * 	host.xz.foo/.git => "foo"
>       + */
>      -+char *guess_target_dir_from_git_url(const char *repo, int is_bundle, int is_bare);
>      ++char *git_url_basename(const char *repo, int is_bundle, int is_bare);
>       +void strip_dir_trailing_slashes(char *dir);
>       +
>        void setup_standard_excludes(struct dir_struct *dir);
> 2:  11eea777ba ! 5:  a3aa25518d submodule--helper: convert the bulk of cmd_add() to C
>      @@ builtin/submodule--helper.c: static int add_config(int argc, const char **argv,
>       +
>       +	add_data.repo = argv[0];
>       +	if (argc == 1)
>      -+		add_data.sm_path = guess_target_dir_from_git_url(add_data.repo, 0, 0);
>      ++		add_data.sm_path = git_url_basename(add_data.repo, 0, 0);
>       +	else
>       +		add_data.sm_path = xstrdup(argv[1]);
>       +
> 3:  51393cd99b = 6:  9667159d4b submodule--helper: remove add-clone subcommand
> 4:  50cedcd8a8 = 7:  dc87b5627a submodule--helper: remove add-config subcommand
> 5:  02558da532 = 8:  ea08e4fbad submodule--helper: remove resolve-relative-url subcommand
> 
Atharva Raykar· Aug 10, 2021, 11:46 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v5 0/9] submodule: convert the rest of 'add' to C

NOTE: This series uses (ar/submodule-add-config):
https://lore.kernel.org/git/20210806140431.92018-1-raykar.ath@gmail.com/
Since v4:
* Rename a local variable in 'compute_submodule_clone_url()' to reduce ambiguity.
* Add a new patch that renames 'compute_submodule_clone_url()' to
  'resolve_relative_url()' to better convey its use. We add it at the end after
  the 'resolve-relative-url' subcommand is removed so that there is no confusion
  as to what that function name binds to.
* Fix incorrect example in the docstring for 'git_url_basename()'. Also add
  another example for more clarity.

Also since this query of mine got buried in the revisions, I shall include it here again:

Questions about the cache API used in [PATCH 5/9]:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1. What is the difference between 'read_cache()' and 'read_cache_preload()'?
     From what I can tell, the latter optimistically preloads the index stat
     data. Is there any reason to use 'read_cache()' over
     'read_cache_preload()'?
  2. Which one is more appropriate for use in 'die_on_index_match()'?

Fetch-it-Via: git fetch https://github.com/tfidfwastaken/git submodule-helper-add-list-5

Atharva Raykar (9):
  submodule--helper: add options for compute_submodule_clone_url()
  submodule--helper: refactor resolve_relative_url() helper
  submodule--helper: remove repeated code in sync_submodule()
  dir: libify and export helper functions from clone.c
  submodule--helper: convert the bulk of cmd_add() to C
  submodule--helper: remove add-clone subcommand
  submodule--helper: remove add-config subcommand
  submodule--helper: remove resolve-relative-url subcommand
  submodule--helper: rename compute_submodule_clone_url()
 builtin/clone.c             | 118 +-------------
 builtin/submodule--helper.c | 307 +++++++++++++++++++-----------------
 dir.c                       | 114 +++++++++++++
 dir.h                       |  11 ++
 git-submodule.sh            |  96 +----------
 5 files changed, 291 insertions(+), 355 deletions(-)
Range-diff against v4:
 1:  75edf24186 !  1:  78e19c4b01 submodule--helper: add options for compute_submodule_clone_url()
    @@ Commit message
         `resolve_relative_url()` by using this function, something we will do in
         the next patch.
     
    +    We also rename the local variable 'relurl' to avoid potential confusion
    +    with the 'rel_url' parameter while we are at it.
    +
         Having this functionality factored out will be useful for converting the
         rest of `submodule add` in subsequent patches.
     
    @@ builtin/submodule--helper.c: static int module_foreach(int argc, const char **ar
     -static char *compute_submodule_clone_url(const char *rel_url)
     +static char *compute_submodule_clone_url(const char *rel_url, const char *up_path, int quiet)
      {
    - 	char *remoteurl, *relurl;
    +-	char *remoteurl, *relurl;
    ++	char *remoteurl, *resolved_url;
      	char *remote = get_default_remote();
    -@@ builtin/submodule--helper.c: static char *compute_submodule_clone_url(const char *rel_url)
    + 	struct strbuf remotesb = STRBUF_INIT;
      
      	strbuf_addf(&remotesb, "remote.%s.url", remote);
      	if (git_config_get_string(remotesb.buf, &remoteurl)) {
    @@ builtin/submodule--helper.c: static char *compute_submodule_clone_url(const char
      		remoteurl = xgetcwd();
      	}
     -	relurl = relative_url(remoteurl, rel_url, NULL);
    -+	relurl = relative_url(remoteurl, rel_url, up_path);
    ++	resolved_url = relative_url(remoteurl, rel_url, up_path);
      
      	free(remote);
      	free(remoteurl);
    + 	strbuf_release(&remotesb);
    + 
    +-	return relurl;
    ++	return resolved_url;
    + }
    + 
    + struct init_cb {
     @@ builtin/submodule--helper.c: static void init_submodule(const char *path, const char *prefix,
      		if (starts_with_dot_dot_slash(url) ||
      		    starts_with_dot_slash(url)) {
 2:  8e7a3e727a !  2:  faadf74ec1 submodule--helper: refactor resolve_relative_url() helper
    @@ builtin/submodule--helper.c: static char *relative_url(const char *remote_url,
     +static char *compute_submodule_clone_url(const char *rel_url, const char *up_path, int quiet)
      {
     -	char *remoteurl = NULL;
    -+	char *remoteurl, *relurl;
    ++	char *remoteurl, *resolved_url;
      	char *remote = get_default_remote();
     +	struct strbuf remotesb = STRBUF_INIT;
     +
    @@ builtin/submodule--helper.c: static char *relative_url(const char *remote_url,
     +				remotesb.buf);
     +		remoteurl = xgetcwd();
     +	}
    -+	relurl = relative_url(remoteurl, rel_url, up_path);
    ++	resolved_url = relative_url(remoteurl, rel_url, up_path);
     +
     +	free(remote);
     +	free(remoteurl);
     +	strbuf_release(&remotesb);
     +
    -+	return relurl;
    ++	return resolved_url;
     +}
     +
     +static int resolve_relative_url(int argc, const char **argv, const char *prefix)
    @@ builtin/submodule--helper.c: static int module_foreach(int argc, const char **ar
      
     -static char *compute_submodule_clone_url(const char *rel_url, const char *up_path, int quiet)
     -{
    --	char *remoteurl, *relurl;
    +-	char *remoteurl, *resolved_url;
     -	char *remote = get_default_remote();
     -	struct strbuf remotesb = STRBUF_INIT;
     -
    @@ builtin/submodule--helper.c: static int module_foreach(int argc, const char **ar
     -				remotesb.buf);
     -		remoteurl = xgetcwd();
     -	}
    --	relurl = relative_url(remoteurl, rel_url, up_path);
    +-	resolved_url = relative_url(remoteurl, rel_url, up_path);
     -
     -	free(remote);
     -	free(remoteurl);
     -	strbuf_release(&remotesb);
     -
    --	return relurl;
    +-	return resolved_url;
     -}
     -
      struct init_cb {
 3:  82961ddd02 =  3:  d2127bd09a submodule--helper: remove repeated code in sync_submodule()
 4:  fa97d6801e !  4:  4b05f93def dir: libify and export helper functions from clone.c
    @@ dir.h: static inline int is_dot_or_dotdot(const char *name)
     + *
     + * For example:
     + * 	/path/to/repo.git => "repo"
    -+ * 	host.xz.foo/.git => "foo"
    ++ * 	host.xz:foo/.git => "foo"
    ++ * 	http://example.com/user/bar.baz => "bar.baz"
     + */
     +char *git_url_basename(const char *repo, int is_bundle, int is_bare);
     +void strip_dir_trailing_slashes(char *dir);
 5:  a3aa25518d =  5:  1491a011b0 submodule--helper: convert the bulk of cmd_add() to C
 6:  9667159d4b =  6:  5d5d07a6ba submodule--helper: remove add-clone subcommand
 7:  dc87b5627a =  7:  0ea27c2a15 submodule--helper: remove add-config subcommand
 8:  ea08e4fbad !  8:  6bd2571f73 submodule--helper: remove resolve-relative-url subcommand
    @@ Commit message
     
      ## builtin/submodule--helper.c ##
     @@ builtin/submodule--helper.c: static char *compute_submodule_clone_url(const char *rel_url, const char *up_pat
    - 	return relurl;
    + 	return resolved_url;
      }
      
     -static int resolve_relative_url(int argc, const char **argv, const char *prefix)
 -:  ---------- >  9:  14a05a0c1d submodule--helper: rename compute_submodule_clone_url()
-- 
2.32.0
Atharva Raykar· Aug 10, 2021, 11:46 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v5 1/9] submodule--helper: add options for compute_submodule_clone_url()

Let's modify the interface to `compute_submodule_clone_url()` function by adding two more arguments, so that we can reuse this in various parts of `submodule--helper.c` that follow a common pattern, which is--read the remote url configuration of the superproject and then call `relative_url()`.

This function is nearly identical to `resolve_relative_url()`, the only difference being the extra warning message. We can add a quiet flag to it, to suppress that warning when not needed, and then refactor `resolve_relative_url()` by using this function, something we will do in the next patch.

We also rename the local variable 'relurl' to avoid potential confusion with the 'rel_url' parameter while we are at it.

Having this functionality factored out will be useful for converting the rest of `submodule add` in subsequent patches.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)
Show changes to builtin/submodule--helper.c +11 −7
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 6891480013..d850b30fce 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -590,24 +590,28 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
 	return 0;
 }
 
-static char *compute_submodule_clone_url(const char *rel_url)
+static char *compute_submodule_clone_url(const char *rel_url, const char *up_path, int quiet)
 {
-	char *remoteurl, *relurl;
+	char *remoteurl, *resolved_url;
 	char *remote = get_default_remote();
 	struct strbuf remotesb = STRBUF_INIT;
 
 	strbuf_addf(&remotesb, "remote.%s.url", remote);
 	if (git_config_get_string(remotesb.buf, &remoteurl)) {
-		warning(_("could not look up configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf);
+		if (!quiet)
+			warning(_("could not look up configuration '%s'. "
+				  "Assuming this repository is its own "
+				  "authoritative upstream."),
+				remotesb.buf);
 		remoteurl = xgetcwd();
 	}
-	relurl = relative_url(remoteurl, rel_url, NULL);
+	resolved_url = relative_url(remoteurl, rel_url, up_path);
 
 	free(remote);
 	free(remoteurl);
 	strbuf_release(&remotesb);
 
-	return relurl;
+	return resolved_url;
 }
 
 struct init_cb {
@@ -660,7 +664,7 @@ static void init_submodule(const char *path, const char *prefix,
 		if (starts_with_dot_dot_slash(url) ||
 		    starts_with_dot_slash(url)) {
 			char *oldurl = url;
-			url = compute_submodule_clone_url(oldurl);
+			url = compute_submodule_clone_url(oldurl, NULL, 0);
 			free(oldurl);
 		}
 
@@ -2134,7 +2138,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
 	if (repo_config_get_string_tmp(the_repository, sb.buf, &url)) {
 		if (starts_with_dot_slash(sub->url) ||
 		    starts_with_dot_dot_slash(sub->url)) {
-			url = compute_submodule_clone_url(sub->url);
+			url = compute_submodule_clone_url(sub->url, NULL, 0);
 			need_free_url = 1;
 		} else
 			url = sub->url;
-- 
2.32.0
Bagas Sanjaya· Aug 11, 2021, 06:44 UTC · re: Atharva Raykar · lore

Re: [GSoC] [PATCH v5 1/9] submodule--helper: add options for compute_submodule_clone_url()

On 10/08/21 18.46, Atharva Raykar wrote:
Show 9 quoted lines
>   	if (git_config_get_string(remotesb.buf, &remoteurl)) {
> -		warning(_("could not look up configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf);
> +		if (!quiet)
> +			warning(_("could not look up configuration '%s'. "
> +				  "Assuming this repository is its own "
> +				  "authoritative upstream."),
> +				remotesb.buf);
>   		remoteurl = xgetcwd();
>   	}
Why did you split warning message? We could keep that in one line.
-- 
An old man doll... just what I always wanted! - Clara
Atharva Raykar· Aug 11, 2021, 10:30 UTC · re: Bagas Sanjaya · lore

Re: [GSoC] [PATCH v5 1/9] submodule--helper: add options for compute_submodule_clone_url()

Bagas Sanjaya <bagasdotme@gmail.com> writes:
Show 12 quoted lines
> On 10/08/21 18.46, Atharva Raykar wrote:
>>   	if (git_config_get_string(remotesb.buf, &remoteurl)) {
>> -		warning(_("could not look up configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf);
>> +		if (!quiet)
>> +			warning(_("could not look up configuration '%s'. "
>> +				  "Assuming this repository is its own "
>> +				  "authoritative upstream."),
>> +				remotesb.buf);
>>   		remoteurl = xgetcwd();
>>   	}
>
> Why did you split warning message? We could keep that in one line.

That line was too long, and given that I was moving the function and changing it a little bit, I decided to make it adhere more closely to the CodingGuidelines [1] and local convention.

[1] https://github.com/git/git/blob/6c85aac65fb455af85745130ce35ddae4678db84/Documentation/CodingGuidelines#L190
Atharva Raykar· Aug 10, 2021, 11:46 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v5 3/9] submodule--helper: remove repeated code in sync_submodule()

This part of `sync_submodule()` is doing the same thing that `compute_submodule_clone_url()` is doing. Let's reuse that helper here.

Note that this change adds a small overhead where we allocate and free the 'remote' twice, but that is a small price to pay for the higher level of abstraction we get.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 16 +++-------------
 1 file changed, 3 insertions(+), 13 deletions(-)
Show changes to builtin/submodule--helper.c +3 −13
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index fe126cdee9..da77248948 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1373,20 +1373,10 @@ static void sync_submodule(const char *path, const char *prefix,
 	if (sub && sub->url) {
 		if (starts_with_dot_dot_slash(sub->url) ||
 		    starts_with_dot_slash(sub->url)) {
-			char *remote_url, *up_path;
-			char *remote = get_default_remote();
-			strbuf_addf(&sb, "remote.%s.url", remote);
-
-			if (git_config_get_string(sb.buf, &remote_url))
-				remote_url = xgetcwd();
-
-			up_path = get_up_path(path);
-			sub_origin_url = relative_url(remote_url, sub->url, up_path);
-			super_config_url = relative_url(remote_url, sub->url, NULL);
-
-			free(remote);
+			char *up_path = get_up_path(path);
+			sub_origin_url = compute_submodule_clone_url(sub->url, up_path, 1);
+			super_config_url = compute_submodule_clone_url(sub->url, NULL, 1);
 			free(up_path);
-			free(remote_url);
 		} else {
 			sub_origin_url = xstrdup(sub->url);
 			super_config_url = xstrdup(sub->url);
-- 
2.32.0
Atharva Raykar· Aug 10, 2021, 11:46 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v5 2/9] submodule--helper: refactor resolve_relative_url() helper

Refactor the helper function to resolve a relative url, by reusing the existing `compute_submodule_clone_url()` function.

`compute_submodule_clone_url()` performs the same work that `resolve_relative_url()` is doing, so we eliminate this code repetition by moving the former function's definition up, and calling it inside `resolve_relative_url()`.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 61 +++++++++++++++----------------------
 1 file changed, 25 insertions(+), 36 deletions(-)
Show changes to builtin/submodule--helper.c +25 −36
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index d850b30fce..fe126cdee9 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -199,33 +199,46 @@ static char *relative_url(const char *remote_url,
 	return strbuf_detach(&sb, NULL);
 }
 
+static char *compute_submodule_clone_url(const char *rel_url, const char *up_path, int quiet)
+{
+	char *remoteurl, *resolved_url;
+	char *remote = get_default_remote();
+	struct strbuf remotesb = STRBUF_INIT;
+
+	strbuf_addf(&remotesb, "remote.%s.url", remote);
+	if (git_config_get_string(remotesb.buf, &remoteurl)) {
+		if (!quiet)
+			warning(_("could not look up configuration '%s'. "
+				  "Assuming this repository is its own "
+				  "authoritative upstream."),
+				remotesb.buf);
+		remoteurl = xgetcwd();
+	}
+	resolved_url = relative_url(remoteurl, rel_url, up_path);
+
+	free(remote);
+	free(remoteurl);
+	strbuf_release(&remotesb);
+
+	return resolved_url;
+}
+
 static int resolve_relative_url(int argc, const char **argv, const char *prefix)
 {
-	char *remoteurl = NULL;
-	char *remote = get_default_remote();
 	const char *up_path = NULL;
 	char *res;
 	const char *url;
-	struct strbuf sb = STRBUF_INIT;
 
 	if (argc != 2 && argc != 3)
 		die("resolve-relative-url only accepts one or two arguments");
 
 	url = argv[1];
-	strbuf_addf(&sb, "remote.%s.url", remote);
-	free(remote);
-
-	if (git_config_get_string(sb.buf, &remoteurl))
-		/* the repository is its own authoritative upstream */
-		remoteurl = xgetcwd();
-
 	if (argc == 3)
 		up_path = argv[2];
 
-	res = relative_url(remoteurl, url, up_path);
+	res = compute_submodule_clone_url(url, up_path, 1);
 	puts(res);
 	free(res);
-	free(remoteurl);
 	return 0;
 }
 
@@ -590,30 +603,6 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
 	return 0;
 }
 
-static char *compute_submodule_clone_url(const char *rel_url, const char *up_path, int quiet)
-{
-	char *remoteurl, *resolved_url;
-	char *remote = get_default_remote();
-	struct strbuf remotesb = STRBUF_INIT;
-
-	strbuf_addf(&remotesb, "remote.%s.url", remote);
-	if (git_config_get_string(remotesb.buf, &remoteurl)) {
-		if (!quiet)
-			warning(_("could not look up configuration '%s'. "
-				  "Assuming this repository is its own "
-				  "authoritative upstream."),
-				remotesb.buf);
-		remoteurl = xgetcwd();
-	}
-	resolved_url = relative_url(remoteurl, rel_url, up_path);
-
-	free(remote);
-	free(remoteurl);
-	strbuf_release(&remotesb);
-
-	return resolved_url;
-}
-
 struct init_cb {
 	const char *prefix;
 	unsigned int flags;
-- 
2.32.0
Atharva Raykar· Aug 10, 2021, 11:46 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v5 4/9] dir: libify and export helper functions from clone.c

These functions can be useful to other parts of Git. Let's move them to dir.c, while renaming them to be make their functionality more explicit.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/clone.c | 118 +-----------------------------------------------
 dir.c           | 114 ++++++++++++++++++++++++++++++++++++++++++++++
 dir.h           |  11 +++++
 3 files changed, 127 insertions(+), 116 deletions(-)
Show changes to 3 files +127 −116

builtin/clone.c, dir.c, dir.h

diff --git a/builtin/clone.c b/builtin/clone.c
index 66fe66679c..5ba24b7ae7 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -217,120 +217,6 @@ static char *get_repo_path(const char *repo, int *is_bundle)
 	return canon;
 }
 
-static char *guess_dir_name(const char *repo, int is_bundle, int is_bare)
-{
-	const char *end = repo + strlen(repo), *start, *ptr;
-	size_t len;
-	char *dir;
-
-	/*
-	 * Skip scheme.
-	 */
-	start = strstr(repo, "://");
-	if (start == NULL)
-		start = repo;
-	else
-		start += 3;
-
-	/*
-	 * Skip authentication data. The stripping does happen
-	 * greedily, such that we strip up to the last '@' inside
-	 * the host part.
-	 */
-	for (ptr = start; ptr < end && !is_dir_sep(*ptr); ptr++) {
-		if (*ptr == '@')
-			start = ptr + 1;
-	}
-
-	/*
-	 * Strip trailing spaces, slashes and /.git
-	 */
-	while (start < end && (is_dir_sep(end[-1]) || isspace(end[-1])))
-		end--;
-	if (end - start > 5 && is_dir_sep(end[-5]) &&
-	    !strncmp(end - 4, ".git", 4)) {
-		end -= 5;
-		while (start < end && is_dir_sep(end[-1]))
-			end--;
-	}
-
-	/*
-	 * Strip trailing port number if we've got only a
-	 * hostname (that is, there is no dir separator but a
-	 * colon). This check is required such that we do not
-	 * strip URI's like '/foo/bar:2222.git', which should
-	 * result in a dir '2222' being guessed due to backwards
-	 * compatibility.
-	 */
-	if (memchr(start, '/', end - start) == NULL
-	    && memchr(start, ':', end - start) != NULL) {
-		ptr = end;
-		while (start < ptr && isdigit(ptr[-1]) && ptr[-1] != ':')
-			ptr--;
-		if (start < ptr && ptr[-1] == ':')
-			end = ptr - 1;
-	}
-
-	/*
-	 * Find last component. To remain backwards compatible we
-	 * also regard colons as path separators, such that
-	 * cloning a repository 'foo:bar.git' would result in a
-	 * directory 'bar' being guessed.
-	 */
-	ptr = end;
-	while (start < ptr && !is_dir_sep(ptr[-1]) && ptr[-1] != ':')
-		ptr--;
-	start = ptr;
-
-	/*
-	 * Strip .{bundle,git}.
-	 */
-	len = end - start;
-	strip_suffix_mem(start, &len, is_bundle ? ".bundle" : ".git");
-
-	if (!len || (len == 1 && *start == '/'))
-		die(_("No directory name could be guessed.\n"
-		      "Please specify a directory on the command line"));
-
-	if (is_bare)
-		dir = xstrfmt("%.*s.git", (int)len, start);
-	else
-		dir = xstrndup(start, len);
-	/*
-	 * Replace sequences of 'control' characters and whitespace
-	 * with one ascii space, remove leading and trailing spaces.
-	 */
-	if (*dir) {
-		char *out = dir;
-		int prev_space = 1 /* strip leading whitespace */;
-		for (end = dir; *end; ++end) {
-			char ch = *end;
-			if ((unsigned char)ch < '\x20')
-				ch = '\x20';
-			if (isspace(ch)) {
-				if (prev_space)
-					continue;
-				prev_space = 1;
-			} else
-				prev_space = 0;
-			*out++ = ch;
-		}
-		*out = '\0';
-		if (out > dir && prev_space)
-			out[-1] = '\0';
-	}
-	return dir;
-}
-
-static void strip_trailing_slashes(char *dir)
-{
-	char *end = dir + strlen(dir);
-
-	while (dir < end - 1 && is_dir_sep(end[-1]))
-		end--;
-	*end = '\0';
-}
-
 static int add_one_reference(struct string_list_item *item, void *cb_data)
 {
 	struct strbuf err = STRBUF_INIT;
@@ -1041,8 +927,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 	if (argc == 2)
 		dir = xstrdup(argv[1]);
 	else
-		dir = guess_dir_name(repo_name, is_bundle, option_bare);
-	strip_trailing_slashes(dir);
+		dir = git_url_basename(repo_name, is_bundle, option_bare);
+	strip_dir_trailing_slashes(dir);
 
 	dest_exists = path_exists(dir);
 	if (dest_exists && !is_empty_dir(dir))
diff --git a/dir.c b/dir.c
index 03c4d21267..6d26db3189 100644
--- a/dir.c
+++ b/dir.c
@@ -2970,6 +2970,120 @@ int is_empty_dir(const char *path)
 	return ret;
 }
 
+char *git_url_basename(const char *repo, int is_bundle, int is_bare)
+{
+	const char *end = repo + strlen(repo), *start, *ptr;
+	size_t len;
+	char *dir;
+
+	/*
+	 * Skip scheme.
+	 */
+	start = strstr(repo, "://");
+	if (start == NULL)
+		start = repo;
+	else
+		start += 3;
+
+	/*
+	 * Skip authentication data. The stripping does happen
+	 * greedily, such that we strip up to the last '@' inside
+	 * the host part.
+	 */
+	for (ptr = start; ptr < end && !is_dir_sep(*ptr); ptr++) {
+		if (*ptr == '@')
+			start = ptr + 1;
+	}
+
+	/*
+	 * Strip trailing spaces, slashes and /.git
+	 */
+	while (start < end && (is_dir_sep(end[-1]) || isspace(end[-1])))
+		end--;
+	if (end - start > 5 && is_dir_sep(end[-5]) &&
+	    !strncmp(end - 4, ".git", 4)) {
+		end -= 5;
+		while (start < end && is_dir_sep(end[-1]))
+			end--;
+	}
+
+	/*
+	 * Strip trailing port number if we've got only a
+	 * hostname (that is, there is no dir separator but a
+	 * colon). This check is required such that we do not
+	 * strip URI's like '/foo/bar:2222.git', which should
+	 * result in a dir '2222' being guessed due to backwards
+	 * compatibility.
+	 */
+	if (memchr(start, '/', end - start) == NULL
+	    && memchr(start, ':', end - start) != NULL) {
+		ptr = end;
+		while (start < ptr && isdigit(ptr[-1]) && ptr[-1] != ':')
+			ptr--;
+		if (start < ptr && ptr[-1] == ':')
+			end = ptr - 1;
+	}
+
+	/*
+	 * Find last component. To remain backwards compatible we
+	 * also regard colons as path separators, such that
+	 * cloning a repository 'foo:bar.git' would result in a
+	 * directory 'bar' being guessed.
+	 */
+	ptr = end;
+	while (start < ptr && !is_dir_sep(ptr[-1]) && ptr[-1] != ':')
+		ptr--;
+	start = ptr;
+
+	/*
+	 * Strip .{bundle,git}.
+	 */
+	len = end - start;
+	strip_suffix_mem(start, &len, is_bundle ? ".bundle" : ".git");
+
+	if (!len || (len == 1 && *start == '/'))
+		die(_("No directory name could be guessed.\n"
+		      "Please specify a directory on the command line"));
+
+	if (is_bare)
+		dir = xstrfmt("%.*s.git", (int)len, start);
+	else
+		dir = xstrndup(start, len);
+	/*
+	 * Replace sequences of 'control' characters and whitespace
+	 * with one ascii space, remove leading and trailing spaces.
+	 */
+	if (*dir) {
+		char *out = dir;
+		int prev_space = 1 /* strip leading whitespace */;
+		for (end = dir; *end; ++end) {
+			char ch = *end;
+			if ((unsigned char)ch < '\x20')
+				ch = '\x20';
+			if (isspace(ch)) {
+				if (prev_space)
+					continue;
+				prev_space = 1;
+			} else
+				prev_space = 0;
+			*out++ = ch;
+		}
+		*out = '\0';
+		if (out > dir && prev_space)
+			out[-1] = '\0';
+	}
+	return dir;
+}
+
+void strip_dir_trailing_slashes(char *dir)
+{
+	char *end = dir + strlen(dir);
+
+	while (dir < end - 1 && is_dir_sep(end[-1]))
+		end--;
+	*end = '\0';
+}
+
 static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up)
 {
 	DIR *dir;
diff --git a/dir.h b/dir.h
index b3e1a54a97..63ff04f5ac 100644
--- a/dir.h
+++ b/dir.h
@@ -453,6 +453,17 @@ static inline int is_dot_or_dotdot(const char *name)
 
 int is_empty_dir(const char *dir);
 
+/*
+ * Retrieve the "humanish" basename of the given Git URL.
+ *
+ * For example:
+ * 	/path/to/repo.git => "repo"
+ * 	host.xz:foo/.git => "foo"
+ * 	http://example.com/user/bar.baz => "bar.baz"
+ */
+char *git_url_basename(const char *repo, int is_bundle, int is_bare);
+void strip_dir_trailing_slashes(char *dir);
+
 void setup_standard_excludes(struct dir_struct *dir);
 
 char *get_sparse_checkout_filename(void);
-- 
2.32.0
Atharva Raykar· Aug 10, 2021, 11:46 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v5 5/9] submodule--helper: convert the bulk of cmd_add() to C

Introduce the 'add' subcommand to `submodule--helper.c` that does all the work 'submodule add' past the parsing of flags.

We also remove the constness of the sm_path field of the `add_data` struct. This is needed so that it can be modified by normalize_path_copy().

As with the previous conversions, this is meant to be a faithful conversion with no modification to the behaviour of `submodule add`.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Helped-by: Kaartic Sivaraam <kaartic.sivaraam@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
Based-on-patch-by: Shourya Shukla <periperidip@gmail.com>
Based-on-patch-by: Prathamesh Chavan <pc44800@gmail.com>
---
 builtin/submodule--helper.c | 165 +++++++++++++++++++++++++++++++++++-
 git-submodule.sh            |  96 +--------------------
 2 files changed, 166 insertions(+), 95 deletions(-)
Show changes to 2 files +166 −95

builtin/submodule--helper.c, git-submodule.sh

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index da77248948..bbc5b4d9fd 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2748,7 +2748,7 @@ struct add_data {
 	const char *prefix;
 	const char *branch;
 	const char *reference_path;
-	const char *sm_path;
+	char *sm_path;
 	const char *sm_name;
 	const char *repo;
 	const char *realrepo;
@@ -3046,6 +3046,168 @@ static int add_config(int argc, const char **argv, const char *prefix)
 	return 0;
 }
 
+static void die_on_index_match(const char *path, int force)
+{
+	struct pathspec ps;
+	const char *args[] = { path, NULL };
+	parse_pathspec(&ps, 0, PATHSPEC_PREFER_CWD, NULL, args);
+
+	if (read_cache_preload(NULL) < 0)
+		die(_("index file corrupt"));
+
+	if (ps.nr) {
+		int i;
+		char *ps_matched = xcalloc(ps.nr, 1);
+
+		/* TODO: audit for interaction with sparse-index. */
+		ensure_full_index(&the_index);
+
+		/*
+		 * Since there is only one pathspec, we just need
+		 * need to check ps_matched[0] to know if a cache
+		 * entry matched.
+		 */
+		for (i = 0; i < active_nr; i++) {
+			ce_path_match(&the_index, active_cache[i], &ps,
+				      ps_matched);
+
+			if (ps_matched[0]) {
+				if (!force)
+					die(_("'%s' already exists in the index"),
+					    path);
+				if (!S_ISGITLINK(active_cache[i]->ce_mode))
+					die(_("'%s' already exists in the index "
+					      "and is not a submodule"), path);
+				break;
+			}
+		}
+		free(ps_matched);
+	}
+}
+
+static void die_on_repo_without_commits(const char *path)
+{
+	struct strbuf sb = STRBUF_INIT;
+	strbuf_addstr(&sb, path);
+	if (is_nonbare_repository_dir(&sb)) {
+		struct object_id oid;
+		if (resolve_gitlink_ref(path, "HEAD", &oid) < 0)
+			die(_("'%s' does not have a commit checked out"), path);
+	}
+}
+
+static int module_add(int argc, const char **argv, const char *prefix)
+{
+	int force = 0, quiet = 0, progress = 0, dissociate = 0;
+	struct add_data add_data = ADD_DATA_INIT;
+
+	struct option options[] = {
+		OPT_STRING('b', "branch", &add_data.branch, N_("branch"),
+			   N_("branch of repository to add as submodule")),
+		OPT__FORCE(&force, N_("allow adding an otherwise ignored submodule path"),
+			   PARSE_OPT_NOCOMPLETE),
+		OPT__QUIET(&quiet, N_("print only error messages")),
+		OPT_BOOL(0, "progress", &progress, N_("force cloning progress")),
+		OPT_STRING(0, "reference", &add_data.reference_path, N_("repository"),
+			   N_("reference repository")),
+		OPT_BOOL(0, "dissociate", &dissociate, N_("borrow the objects from reference repositories")),
+		OPT_STRING(0, "name", &add_data.sm_name, N_("name"),
+			   N_("sets the submodule’s name to the given string "
+			      "instead of defaulting to its path")),
+		OPT_INTEGER(0, "depth", &add_data.depth, N_("depth for shallow clones")),
+		OPT_END()
+	};
+
+	const char *const usage[] = {
+		N_("git submodule--helper add [<options>] [--] <repository> [<path>]"),
+		NULL
+	};
+
+	argc = parse_options(argc, argv, prefix, options, usage, 0);
+
+	if (!is_writing_gitmodules_ok())
+		die(_("please make sure that the .gitmodules file is in the working tree"));
+
+	if (prefix && *prefix &&
+	    add_data.reference_path && !is_absolute_path(add_data.reference_path))
+		add_data.reference_path = xstrfmt("%s%s", prefix, add_data.reference_path);
+
+	if (argc == 0 || argc > 2)
+		usage_with_options(usage, options);
+
+	add_data.repo = argv[0];
+	if (argc == 1)
+		add_data.sm_path = git_url_basename(add_data.repo, 0, 0);
+	else
+		add_data.sm_path = xstrdup(argv[1]);
+
+	if (prefix && *prefix && !is_absolute_path(add_data.sm_path))
+		add_data.sm_path = xstrfmt("%s%s", prefix, add_data.sm_path);
+
+	if (starts_with_dot_dot_slash(add_data.repo) ||
+	    starts_with_dot_slash(add_data.repo)) {
+		if (prefix)
+			die(_("Relative path can only be used from the toplevel "
+			      "of the working tree"));
+
+		/* dereference source url relative to parent's url */
+		add_data.realrepo = compute_submodule_clone_url(add_data.repo, NULL, 1);
+	} else if (is_dir_sep(add_data.repo[0]) || strchr(add_data.repo, ':')) {
+		add_data.realrepo = add_data.repo;
+	} else {
+		die(_("repo URL: '%s' must be absolute or begin with ./|../"),
+		    add_data.repo);
+	}
+
+	/*
+	 * normalize path:
+	 * multiple //; leading ./; /./; /../;
+	 */
+	normalize_path_copy(add_data.sm_path, add_data.sm_path);
+	strip_dir_trailing_slashes(add_data.sm_path);
+
+	die_on_index_match(add_data.sm_path, force);
+	die_on_repo_without_commits(add_data.sm_path);
+
+	if (!force) {
+		int exit_code = -1;
+		struct strbuf sb = STRBUF_INIT;
+		struct child_process cp = CHILD_PROCESS_INIT;
+		cp.git_cmd = 1;
+		cp.no_stdout = 1;
+		strvec_pushl(&cp.args, "add", "--dry-run", "--ignore-missing",
+			     "--no-warn-embedded-repo", add_data.sm_path, NULL);
+		if ((exit_code = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {
+			strbuf_complete_line(&sb);
+			fputs(sb.buf, stderr);
+			free(add_data.sm_path);
+			return exit_code;
+		}
+		strbuf_release(&sb);
+	}
+
+	if(!add_data.sm_name)
+		add_data.sm_name = add_data.sm_path;
+
+	if (check_submodule_name(add_data.sm_name))
+		die(_("'%s' is not a valid submodule name"), add_data.sm_name);
+
+	add_data.prefix = prefix;
+	add_data.force = !!force;
+	add_data.quiet = !!quiet;
+	add_data.progress = !!progress;
+	add_data.dissociate = !!dissociate;
+
+	if (add_submodule(&add_data)) {
+		free(add_data.sm_path);
+		return 1;
+	}
+	configure_added_submodule(&add_data);
+	free(add_data.sm_path);
+
+	return 0;
+}
+
 #define SUPPORT_SUPER_PREFIX (1<<0)
 
 struct cmd_struct {
@@ -3060,6 +3222,7 @@ static struct cmd_struct commands[] = {
 	{"clone", module_clone, 0},
 	{"add-clone", add_clone, 0},
 	{"add-config", add_config, 0},
+	{"add", module_add, SUPPORT_SUPER_PREFIX},
 	{"update-module-mode", module_update_module_mode, 0},
 	{"update-clone", update_clone, 0},
 	{"ensure-core-worktree", ensure_core_worktree, 0},
diff --git a/git-submodule.sh b/git-submodule.sh
index 8c219ef382..1070540525 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -145,104 +145,12 @@ cmd_add()
 		shift
 	done
 
-	if ! git submodule--helper config --check-writeable >/dev/null 2>&1
+	if test -z "$1"
 	then
-		 die "fatal: $(eval_gettext "please make sure that the .gitmodules file is in the working tree")"
-	fi
-
-	if test -n "$reference_path"
-	then
-		is_absolute_path "$reference_path" ||
-		reference_path="$wt_prefix$reference_path"
-
-		reference="--reference=$reference_path"
-	fi
-
-	repo=$1
-	sm_path=$2
-
-	if test -z "$sm_path"; then
-		sm_path=$(printf '%s\n' "$repo" |
-			sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
-	fi
-
-	if test -z "$repo" || test -z "$sm_path"; then
 		usage
 	fi
 
-	is_absolute_path "$sm_path" || sm_path="$wt_prefix$sm_path"
-
-	# assure repo is absolute or relative to parent
-	case "$repo" in
-	./*|../*)
-		test -z "$wt_prefix" ||
-		die "fatal: $(gettext "Relative path can only be used from the toplevel of the working tree")"
-
-		# dereference source url relative to parent's url
-		realrepo=$(git submodule--helper resolve-relative-url "$repo") || exit
-		;;
-	*:*|/*)
-		# absolute url
-		realrepo=$repo
-		;;
-	*)
-		die "fatal: $(eval_gettext "repo URL: '\$repo' must be absolute or begin with ./|../")"
-	;;
-	esac
-
-	# normalize path:
-	# multiple //; leading ./; /./; /../; trailing /
-	sm_path=$(printf '%s/\n' "$sm_path" |
-		sed -e '
-			s|//*|/|g
-			s|^\(\./\)*||
-			s|/\(\./\)*|/|g
-			:start
-			s|\([^/]*\)/\.\./||
-			tstart
-			s|/*$||
-		')
-	if test -z "$force"
-	then
-		git ls-files --error-unmatch "$sm_path" > /dev/null 2>&1 &&
-		die "fatal: $(eval_gettext "'\$sm_path' already exists in the index")"
-	else
-		git ls-files -s "$sm_path" | sane_grep -v "^160000" > /dev/null 2>&1 &&
-		die "fatal: $(eval_gettext "'\$sm_path' already exists in the index and is not a submodule")"
-	fi
-
-	if test -d "$sm_path" &&
-		test -z $(git -C "$sm_path" rev-parse --show-cdup 2>/dev/null)
-	then
-	    git -C "$sm_path" rev-parse --verify -q HEAD >/dev/null ||
-	    die "fatal: $(eval_gettext "'\$sm_path' does not have a commit checked out")"
-	fi
-
-	if test -z "$force"
-	then
-	    dryerr=$(git add --dry-run --ignore-missing --no-warn-embedded-repo "$sm_path" 2>&1 >/dev/null)
-	    res=$?
-	    if test $res -ne 0
-	    then
-		 echo >&2 "$dryerr"
-		 exit $res
-	    fi
-	fi
-
-	if test -n "$custom_name"
-	then
-		sm_name="$custom_name"
-	else
-		sm_name="$sm_path"
-	fi
-
-	if ! git submodule--helper check-name "$sm_name"
-	then
-		die "fatal: $(eval_gettext "'$sm_name' is not a valid submodule name")"
-	fi
-
-	git submodule--helper add-clone ${GIT_QUIET:+--quiet} ${force:+"--force"} ${progress:+"--progress"} ${branch:+--branch "$branch"} --prefix "$wt_prefix" --path "$sm_path" --name "$sm_name" --url "$realrepo" ${reference:+"$reference"} ${dissociate:+"--dissociate"} ${depth:+"$depth"} || exit
-	git submodule--helper add-config ${force:+--force} ${branch:+--branch "$branch"} --url "$repo" --resolved-url "$realrepo" --path "$sm_path" --name "$sm_name"
+	git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper add ${GIT_QUIET:+--quiet} ${force:+--force} ${progress:+"--progress"} ${branch:+--branch "$branch"} ${reference_path:+--reference "$reference_path"} ${dissociate:+--dissociate} ${custom_name:+--name "$custom_name"} ${depth:+"$depth"} -- "$@"
 }
 
 #
-- 
2.32.0
Atharva Raykar· Aug 10, 2021, 11:46 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v5 6/9] submodule--helper: remove add-clone subcommand

We no longer need this subcommand, as all of its functionality is being called by the newly-introduced `module_add()` directly within C.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 60 -------------------------------------
 1 file changed, 60 deletions(-)
Show changes to builtin/submodule--helper.c +0 −60
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index bbc5b4d9fd..6ab7de7741 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2860,65 +2860,6 @@ static int add_submodule(const struct add_data *add_data)
 	return 0;
 }
 
-static int add_clone(int argc, const char **argv, const char *prefix)
-{
-	int force = 0, quiet = 0, dissociate = 0, progress = 0;
-	struct add_data add_data = ADD_DATA_INIT;
-
-	struct option options[] = {
-		OPT_STRING('b', "branch", &add_data.branch,
-			   N_("branch"),
-			   N_("branch of repository to checkout on cloning")),
-		OPT_STRING(0, "prefix", &prefix,
-			   N_("path"),
-			   N_("alternative anchor for relative paths")),
-		OPT_STRING(0, "path", &add_data.sm_path,
-			   N_("path"),
-			   N_("where the new submodule will be cloned to")),
-		OPT_STRING(0, "name", &add_data.sm_name,
-			   N_("string"),
-			   N_("name of the new submodule")),
-		OPT_STRING(0, "url", &add_data.realrepo,
-			   N_("string"),
-			   N_("url where to clone the submodule from")),
-		OPT_STRING(0, "reference", &add_data.reference_path,
-			   N_("repo"),
-			   N_("reference repository")),
-		OPT_BOOL(0, "dissociate", &dissociate,
-			 N_("use --reference only while cloning")),
-		OPT_INTEGER(0, "depth", &add_data.depth,
-			    N_("depth for shallow clones")),
-		OPT_BOOL(0, "progress", &progress,
-			 N_("force cloning progress")),
-		OPT__FORCE(&force, N_("allow adding an otherwise ignored submodule path"),
-			   PARSE_OPT_NOCOMPLETE),
-		OPT__QUIET(&quiet, "suppress output for cloning a submodule"),
-		OPT_END()
-	};
-
-	const char *const usage[] = {
-		N_("git submodule--helper add-clone [<options>...] "
-		   "--url <url> --path <path> --name <name>"),
-		NULL
-	};
-
-	argc = parse_options(argc, argv, prefix, options, usage, 0);
-
-	if (argc != 0)
-		usage_with_options(usage, options);
-
-	add_data.prefix = prefix;
-	add_data.progress = !!progress;
-	add_data.dissociate = !!dissociate;
-	add_data.force = !!force;
-	add_data.quiet = !!quiet;
-
-	if (add_submodule(&add_data))
-		return 1;
-
-	return 0;
-}
-
 static int config_submodule_in_gitmodules(const char *name, const char *var, const char *value)
 {
 	char *key;
@@ -3220,7 +3161,6 @@ static struct cmd_struct commands[] = {
 	{"list", module_list, 0},
 	{"name", module_name, 0},
 	{"clone", module_clone, 0},
-	{"add-clone", add_clone, 0},
 	{"add-config", add_config, 0},
 	{"add", module_add, SUPPORT_SUPER_PREFIX},
 	{"update-module-mode", module_update_module_mode, 0},
-- 
2.32.0
Atharva Raykar· Aug 10, 2021, 11:46 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v5 9/9] submodule--helper: rename compute_submodule_clone_url()

Let's rename 'compute_submodule_clone_url()' to 'resolve_relative_url()' to make it clear that this internal helper need not be used exclusively for computing submodule clone URLs.

Since the original 'resolve-relative-url' subcommand and its C entry point has been removed in c461095ae3 (submodule--helper: remove resolve-relative-url subcommand, 2021-07-02), this rename can be done without causing any confusion about which function it actually binds to.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)
Show changes to builtin/submodule--helper.c +6 −6
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 65591e47eb..5ab4e17e75 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -199,7 +199,7 @@ static char *relative_url(const char *remote_url,
 	return strbuf_detach(&sb, NULL);
 }
 
-static char *compute_submodule_clone_url(const char *rel_url, const char *up_path, int quiet)
+static char *resolve_relative_url(const char *rel_url, const char *up_path, int quiet)
 {
 	char *remoteurl, *resolved_url;
 	char *remote = get_default_remote();
@@ -634,7 +634,7 @@ static void init_submodule(const char *path, const char *prefix,
 		if (starts_with_dot_dot_slash(url) ||
 		    starts_with_dot_slash(url)) {
 			char *oldurl = url;
-			url = compute_submodule_clone_url(oldurl, NULL, 0);
+			url = resolve_relative_url(oldurl, NULL, 0);
 			free(oldurl);
 		}
 
@@ -1355,8 +1355,8 @@ static void sync_submodule(const char *path, const char *prefix,
 		if (starts_with_dot_dot_slash(sub->url) ||
 		    starts_with_dot_slash(sub->url)) {
 			char *up_path = get_up_path(path);
-			sub_origin_url = compute_submodule_clone_url(sub->url, up_path, 1);
-			super_config_url = compute_submodule_clone_url(sub->url, NULL, 1);
+			sub_origin_url = resolve_relative_url(sub->url, up_path, 1);
+			super_config_url = resolve_relative_url(sub->url, NULL, 1);
 			free(up_path);
 		} else {
 			sub_origin_url = xstrdup(sub->url);
@@ -2098,7 +2098,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
 	if (repo_config_get_string_tmp(the_repository, sb.buf, &url)) {
 		if (starts_with_dot_slash(sub->url) ||
 		    starts_with_dot_dot_slash(sub->url)) {
-			url = compute_submodule_clone_url(sub->url, NULL, 0);
+			url = resolve_relative_url(sub->url, NULL, 0);
 			need_free_url = 1;
 		} else
 			url = sub->url;
@@ -3025,7 +3025,7 @@ static int module_add(int argc, const char **argv, const char *prefix)
 			      "of the working tree"));
 
 		/* dereference source url relative to parent's url */
-		add_data.realrepo = compute_submodule_clone_url(add_data.repo, NULL, 1);
+		add_data.realrepo = resolve_relative_url(add_data.repo, NULL, 1);
 	} else if (is_dir_sep(add_data.repo[0]) || strchr(add_data.repo, ':')) {
 		add_data.realrepo = add_data.repo;
 	} else {
-- 
2.32.0
Atharva Raykar· Aug 10, 2021, 11:46 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v5 8/9] submodule--helper: remove resolve-relative-url subcommand

The shell subcommand `resolve-relative-url` is no longer required, as its last caller has been removed when it was converted to C.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 20 --------------------
 1 file changed, 20 deletions(-)
Show changes to builtin/submodule--helper.c +0 −20
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 0baff16686..65591e47eb 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -223,25 +223,6 @@ static char *compute_submodule_clone_url(const char *rel_url, const char *up_pat
 	return resolved_url;
 }
 
-static int resolve_relative_url(int argc, const char **argv, const char *prefix)
-{
-	const char *up_path = NULL;
-	char *res;
-	const char *url;
-
-	if (argc != 2 && argc != 3)
-		die("resolve-relative-url only accepts one or two arguments");
-
-	url = argv[1];
-	if (argc == 3)
-		up_path = argv[2];
-
-	res = compute_submodule_clone_url(url, up_path, 1);
-	puts(res);
-	free(res);
-	return 0;
-}
-
 static int resolve_relative_url_test(int argc, const char **argv, const char *prefix)
 {
 	char *remoteurl, *res;
@@ -3118,7 +3099,6 @@ static struct cmd_struct commands[] = {
 	{"update-clone", update_clone, 0},
 	{"ensure-core-worktree", ensure_core_worktree, 0},
 	{"relative-path", resolve_relative_path, 0},
-	{"resolve-relative-url", resolve_relative_url, 0},
 	{"resolve-relative-url-test", resolve_relative_url_test, 0},
 	{"foreach", module_foreach, SUPPORT_SUPER_PREFIX},
 	{"init", module_init, SUPPORT_SUPER_PREFIX},
-- 
2.32.0
Atharva Raykar· Aug 10, 2021, 11:46 UTC · re: Atharva Raykar · lore

[GSoC] [PATCH v5 7/9] submodule--helper: remove add-config subcommand

Also no longer needed is this subcommand, as all of its functionality is being called by the newly-introduced `module_add()` directly within C.

Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Shourya Shukla <periperidip@gmail.com>
---
 builtin/submodule--helper.c | 49 -------------------------------------
 1 file changed, 49 deletions(-)
Show changes to builtin/submodule--helper.c +0 −49
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 6ab7de7741..0baff16686 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2939,54 +2939,6 @@ static void configure_added_submodule(struct add_data *add_data)
 	}
 }
 
-static int add_config(int argc, const char **argv, const char *prefix)
-{
-	int force = 0;
-	struct add_data add_data = ADD_DATA_INIT;
-
-	struct option options[] = {
-		OPT_STRING('b', "branch", &add_data.branch,
-			   N_("branch"),
-			   N_("branch of repository to store in "
-			      "the submodule configuration")),
-		OPT_STRING(0, "url", &add_data.repo,
-			   N_("string"),
-			   N_("url to clone submodule from")),
-		OPT_STRING(0, "resolved-url", &add_data.realrepo,
-			   N_("string"),
-			   N_("url to clone the submodule from, after it has "
-			      "been dereferenced relative to parent's url, "
-			      "in the case where <url> is a relative url")),
-		OPT_STRING(0, "path", &add_data.sm_path,
-			   N_("path"),
-			   N_("where the new submodule will be cloned to")),
-		OPT_STRING(0, "name", &add_data.sm_name,
-			   N_("string"),
-			   N_("name of the new submodule")),
-		OPT__FORCE(&force, N_("allow adding an otherwise ignored submodule path"),
-			   PARSE_OPT_NOCOMPLETE),
-		OPT_END()
-	};
-
-	const char *const usage[] = {
-		N_("git submodule--helper add-config "
-		   "[--force|-f] [--branch|-b <branch>] "
-		   "--url <url> --resolved-url <resolved-url> "
-		   "--path <path> --name <name>"),
-		NULL
-	};
-
-	argc = parse_options(argc, argv, prefix, options, usage, 0);
-
-	if (argc)
-		usage_with_options(usage, options);
-
-	add_data.force = !!force;
-	configure_added_submodule(&add_data);
-
-	return 0;
-}
-
 static void die_on_index_match(const char *path, int force)
 {
 	struct pathspec ps;
@@ -3161,7 +3113,6 @@ static struct cmd_struct commands[] = {
 	{"list", module_list, 0},
 	{"name", module_name, 0},
 	{"clone", module_clone, 0},
-	{"add-config", add_config, 0},
 	{"add", module_add, SUPPORT_SUPER_PREFIX},
 	{"update-module-mode", module_update_module_mode, 0},
 	{"update-clone", update_clone, 0},
-- 
2.32.0
Junio C Hamano· Sep 8, 2021, 00:31 UTC · re: Atharva Raykar · lore

Re: [GSoC] [PATCH v5 0/9] submodule: convert the rest of 'add' to C

Atharva Raykar <raykar.ath@gmail.com> writes:
> NOTE: This series uses (ar/submodule-add-config):
> https://lore.kernel.org/git/20210806140431.92018-1-raykar.ath@gmail.com/

I didn't see anybody comment on this round (and do not think I saw anything glaringly wrong).

Is everybody happy with this version? I am about to mark it for 'next' in the next issue of "What's cooking" report, so please holler if I should wait.

Thanks.

← back to recent threads