git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
* [PATCH] submodule: Port resolve_relative_url from shell to C
@ 2015-12-10  1:07 Stefan Beller
  2015-12-10  6:48 ` Johannes Sixt
                   ` (2 more replies)
  0 siblings, 3 replies; 12+ messages in thread
From: Stefan Beller @ 2015-12-10  1:07 UTC (permalink / raw)
  To: gitster; +Cc: git, jens.lehmann, Stefan Beller

This reimplements the helper function `resolve_relative_url` in shell
in C. This functionality is needed in C for introducing the groups
feature later on. When using groups, the user should not need to run
`git submodule init`, but it should be implicit at all appropriate places,
which are all in C code. As the we would not just call out to `git
submodule init`, but do a more fine grained structure there, we actually
need all the init functionality in C before attempting the groups
feature. To get the init functionality in C, rewriting the
resolve_relative_url subfunction is a major step.

This also improves the performance:
(Best out of 3) time ./t7400-submodule-basic.sh
Before:
real	0m9.575s
user	0m2.683s
sys	0m6.773s
After:
real	0m9.293s
user	0m2.691s
sys	0m6.549s

Signed-off-by: Stefan Beller <sbeller@google.com>
---

 This applies on origin/master, and I'd carry as its own feature branch
 as I am nowhere near done with the groups feature after reading Jens feedback.
 (It took me a while to identify this as a next best step.)
 
 Thanks,
 Stefan

 builtin/submodule--helper.c | 120 ++++++++++++++++++++++++++++++++++++++++++++
 git-submodule.sh            |  81 ++----------------------------
 2 files changed, 124 insertions(+), 77 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index f4c3eff..f48b5b5 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -9,6 +9,125 @@
 #include "submodule-config.h"
 #include "string-list.h"
 #include "run-command.h"
+#include "remote.h"
+#include "refs.h"
+
+static const char *get_default_remote(void)
+{
+	char *dest = NULL;
+	unsigned char sha1[20];
+	int flag;
+	struct strbuf sb = STRBUF_INIT;
+	const char *refname = resolve_ref_unsafe("HEAD", 0, sha1, &flag);
+
+	if (!refname)
+		die("No such ref: HEAD");
+
+	refname = shorten_unambiguous_ref(refname, 0);
+	strbuf_addf(&sb, "branch.%s.remote", refname);
+	if (git_config_get_string(sb.buf, &dest))
+		return "origin";
+	else
+		return xstrdup(dest);
+}
+
+/*
+ * The function takes at most 2 arguments. The first argument is the
+ * URL that navigates to the submodule origin repo. When relative, this URL
+ * is relative to the superproject origin URL repo. The second up_path
+ * argument, if specified, is the relative path that navigates
+ * from the submodule working tree to the superproject working tree.
+ *
+ * The output of the function is the origin URL of the submodule.
+ *
+ * The output will either be an absolute URL or filesystem path (if the
+ * superproject origin URL is an absolute URL or filesystem path,
+ * respectively) or a relative file system path (if the superproject
+ * origin URL is a relative file system path).
+ *
+ * When the output is a relative file system path, the path is either
+ * relative to the submodule working tree, if up_path is specified, or to
+ * the superproject working tree otherwise.
+ */
+static const char *relative_url(const char *url, const char *up_path)
+{
+	int is_relative = 0;
+	size_t len;
+	char *remoteurl = NULL;
+	char *sep = "/";
+	const char *out;
+	struct strbuf sb = STRBUF_INIT;
+	const char *remote = get_default_remote();
+	strbuf_addf(&sb, "remote.%s.url", remote);
+
+	if (git_config_get_string(sb.buf, &remoteurl))
+		/* the repository is its own authoritative upstream */
+		remoteurl = xgetcwd();
+
+	if (strip_suffix(remoteurl, "/", &len))
+		remoteurl[len] = '\0';
+
+	if (strchr(remoteurl, ':') || skip_prefix(remoteurl, "/", &out))
+		is_relative = 0;
+	else if (skip_prefix(remoteurl, "./", &out) ||
+		    skip_prefix(remoteurl, "../", &out))
+		is_relative = 1;
+	else {
+		is_relative = 1;
+		strbuf_reset(&sb);
+		strbuf_addf(&sb, "./%s", remoteurl);
+		remoteurl = strbuf_detach(&sb, NULL);
+	}
+
+	while (url) {
+		if (skip_prefix(url, "../", &out)) {
+			char *rfind;
+			url = out;
+
+			rfind = strrchr(remoteurl, '/');
+			if (rfind)
+				*rfind = '\0';
+			else {
+				rfind = strrchr(remoteurl, ':');
+				if (rfind) {
+					*rfind = '\0';
+					sep = ":";
+				} else {
+					if (is_relative || !strcmp(".", remoteurl))
+						die(N_("cannot strip one component off url '%s'"), remoteurl);
+					else
+						remoteurl = ".";
+				}
+			}
+		} else if (skip_prefix(url, "./", &out))
+			url = out;
+		else
+			break;
+	}
+	strbuf_reset(&sb);
+	strbuf_addf(&sb, "%s%s%s", remoteurl, sep, url);
+
+	if (!skip_prefix(sb.buf, "./", &out))
+		out = sb.buf;
+	out = xstrdup(out);
+
+	strbuf_reset(&sb);
+	strbuf_addf(&sb, "%s%s", is_relative && up_path ? up_path : "", out);
+
+	free((char*)out);
+	return strbuf_detach(&sb, NULL);
+}
+
+static int resolve_relative_url(int argc, const char **argv, const char *prefix)
+{
+	if (argc == 2)
+		printf("%s\n", relative_url(argv[1], NULL));
+	else if (argc == 3)
+		printf("%s\n", relative_url(argv[1], argv[2]));
+	else
+		die("BUG: resolve_relative_url only accepts one or two arguments");
+	return 0;
+}
 
 struct module_list {
 	const struct cache_entry **entries;
@@ -264,6 +383,7 @@ static struct cmd_struct commands[] = {
 	{"list", module_list},
 	{"name", module_name},
 	{"clone", module_clone},
+	{"resolve_relative_url", resolve_relative_url},
 };
 
 int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
diff --git a/git-submodule.sh b/git-submodule.sh
index 9bc5c5f..6a7a3e4 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -46,79 +46,6 @@ prefix=
 custom_name=
 depth=
 
-# The function takes at most 2 arguments. The first argument is the
-# URL that navigates to the submodule origin repo. When relative, this URL
-# is relative to the superproject origin URL repo. The second up_path
-# argument, if specified, is the relative path that navigates
-# from the submodule working tree to the superproject working tree.
-#
-# The output of the function is the origin URL of the submodule.
-#
-# The output will either be an absolute URL or filesystem path (if the
-# superproject origin URL is an absolute URL or filesystem path,
-# respectively) or a relative file system path (if the superproject
-# origin URL is a relative file system path).
-#
-# When the output is a relative file system path, the path is either
-# relative to the submodule working tree, if up_path is specified, or to
-# the superproject working tree otherwise.
-resolve_relative_url ()
-{
-	remote=$(get_default_remote)
-	remoteurl=$(git config "remote.$remote.url") ||
-		remoteurl=$(pwd) # the repository is its own authoritative upstream
-	url="$1"
-	remoteurl=${remoteurl%/}
-	sep=/
-	up_path="$2"
-
-	case "$remoteurl" in
-	*:*|/*)
-		is_relative=
-		;;
-	./*|../*)
-		is_relative=t
-		;;
-	*)
-		is_relative=t
-		remoteurl="./$remoteurl"
-		;;
-	esac
-
-	while test -n "$url"
-	do
-		case "$url" in
-		../*)
-			url="${url#../}"
-			case "$remoteurl" in
-			*/*)
-				remoteurl="${remoteurl%/*}"
-				;;
-			*:*)
-				remoteurl="${remoteurl%:*}"
-				sep=:
-				;;
-			*)
-				if test -z "$is_relative" || test "." = "$remoteurl"
-				then
-					die "$(eval_gettext "cannot strip one component off url '\$remoteurl'")"
-				else
-					remoteurl=.
-				fi
-				;;
-			esac
-			;;
-		./*)
-			url="${url#./}"
-			;;
-		*)
-			break;;
-		esac
-	done
-	remoteurl="$remoteurl$sep${url%/}"
-	echo "${is_relative:+${up_path}}${remoteurl#./}"
-}
-
 # Resolve a path to be relative to another path.  This is intended for
 # converting submodule paths when git-submodule is run in a subdirectory
 # and only handles paths where the directory separator is '/'.
@@ -281,7 +208,7 @@ cmd_add()
 		die "$(gettext "Relative path can only be used from the toplevel of the working tree")"
 
 		# dereference source url relative to parent's url
-		realrepo=$(resolve_relative_url "$repo") || exit
+		realrepo=$(git submodule--helper resolve_relative_url "$repo") || exit
 		;;
 	*:*|/*)
 		# absolute url
@@ -485,7 +412,7 @@ cmd_init()
 			# Possibly a url relative to parent
 			case "$url" in
 			./*|../*)
-				url=$(resolve_relative_url "$url") || exit
+				url=$(git submodule--helper resolve_relative_url "$url") || exit
 				;;
 			esac
 			git config submodule."$name".url "$url" ||
@@ -1190,9 +1117,9 @@ cmd_sync()
 			# guarantee a trailing /
 			up_path=${up_path%/}/ &&
 			# path from submodule work tree to submodule origin repo
-			sub_origin_url=$(resolve_relative_url "$url" "$up_path") &&
+			sub_origin_url=$(git submodule--helper resolve_relative_url "$url" "$up_path") &&
 			# path from superproject work tree to submodule origin repo
-			super_config_url=$(resolve_relative_url "$url") || exit
+			super_config_url=$(git submodule--helper resolve_relative_url "$url") || exit
 			;;
 		*)
 			sub_origin_url="$url"
-- 
2.6.3.470.g4b82c23.dirty

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH] submodule: Port resolve_relative_url from shell to C
  2015-12-10  1:07 [PATCH] submodule: Port resolve_relative_url from shell to C Stefan Beller
@ 2015-12-10  6:48 ` Johannes Sixt
  2015-12-16 22:36   ` Stefan Beller
  2015-12-17  0:26 ` [PATCHv2] Porting " Stefan Beller
  2015-12-17  0:26 ` [PATCHv2] submodule: Port " Stefan Beller
  2 siblings, 1 reply; 12+ messages in thread
From: Johannes Sixt @ 2015-12-10  6:48 UTC (permalink / raw)
  To: Stefan Beller; +Cc: gitster, git, jens.lehmann

Am 10.12.2015 um 02:07 schrieb Stefan Beller:
> This reimplements the helper function `resolve_relative_url` in shell
> in C. This functionality is needed in C for introducing the groups
> feature later on. When using groups, the user should not need to run
> `git submodule init`, but it should be implicit at all appropriate places,
> which are all in C code. As the we would not just call out to `git
> submodule init`, but do a more fine grained structure there, we actually
> need all the init functionality in C before attempting the groups
> feature. To get the init functionality in C, rewriting the
> resolve_relative_url subfunction is a major step.

I see lots of '/', but no is_dir_sep() in the C version. Did you 
consider that local URLs can use a backslash as path separator on 
Windows? In the shell version, this did not matter because bash converts 
the backslashes to forward slashes for us. But when rewritten in C, this 
does not happen.

Valid URLs are

   D:\foo\bar.git
   \\server\share\foo\bar
   ..\..\foo\bar

and all of them with some or all backslashes replaced by forward slashes.

See also connect.c:url_is_local_not_ssh, which ensures that the first 
example above is considered a local path with a drive letter, not a 
remote ssh path.

>
> This also improves the performance:
> (Best out of 3) time ./t7400-submodule-basic.sh
> Before:
> real	0m9.575s
> user	0m2.683s
> sys	0m6.773s
> After:
> real	0m9.293s
> user	0m2.691s
> sys	0m6.549s
>
> Signed-off-by: Stefan Beller <sbeller@google.com>
> ---
>
>   This applies on origin/master, and I'd carry as its own feature branch
>   as I am nowhere near done with the groups feature after reading Jens feedback.
>   (It took me a while to identify this as a next best step.)
>
>   Thanks,
>   Stefan
>
>   builtin/submodule--helper.c | 120 ++++++++++++++++++++++++++++++++++++++++++++
>   git-submodule.sh            |  81 ++----------------------------
>   2 files changed, 124 insertions(+), 77 deletions(-)
>
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index f4c3eff..f48b5b5 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -9,6 +9,125 @@
>   #include "submodule-config.h"
>   #include "string-list.h"
>   #include "run-command.h"
> +#include "remote.h"
> +#include "refs.h"
> +
> +static const char *get_default_remote(void)
> +{
> +	char *dest = NULL;
> +	unsigned char sha1[20];
> +	int flag;
> +	struct strbuf sb = STRBUF_INIT;
> +	const char *refname = resolve_ref_unsafe("HEAD", 0, sha1, &flag);
> +
> +	if (!refname)
> +		die("No such ref: HEAD");
> +
> +	refname = shorten_unambiguous_ref(refname, 0);
> +	strbuf_addf(&sb, "branch.%s.remote", refname);
> +	if (git_config_get_string(sb.buf, &dest))
> +		return "origin";
> +	else
> +		return xstrdup(dest);
> +}
> +
> +/*
> + * The function takes at most 2 arguments. The first argument is the
> + * URL that navigates to the submodule origin repo. When relative, this URL
> + * is relative to the superproject origin URL repo. The second up_path
> + * argument, if specified, is the relative path that navigates
> + * from the submodule working tree to the superproject working tree.
> + *
> + * The output of the function is the origin URL of the submodule.
> + *
> + * The output will either be an absolute URL or filesystem path (if the
> + * superproject origin URL is an absolute URL or filesystem path,
> + * respectively) or a relative file system path (if the superproject
> + * origin URL is a relative file system path).
> + *
> + * When the output is a relative file system path, the path is either
> + * relative to the submodule working tree, if up_path is specified, or to
> + * the superproject working tree otherwise.
> + */
> +static const char *relative_url(const char *url, const char *up_path)
> +{
> +	int is_relative = 0;
> +	size_t len;
> +	char *remoteurl = NULL;
> +	char *sep = "/";
> +	const char *out;
> +	struct strbuf sb = STRBUF_INIT;
> +	const char *remote = get_default_remote();
> +	strbuf_addf(&sb, "remote.%s.url", remote);
> +
> +	if (git_config_get_string(sb.buf, &remoteurl))
> +		/* the repository is its own authoritative upstream */
> +		remoteurl = xgetcwd();
> +
> +	if (strip_suffix(remoteurl, "/", &len))
> +		remoteurl[len] = '\0';
> +
> +	if (strchr(remoteurl, ':') || skip_prefix(remoteurl, "/", &out))
> +		is_relative = 0;
> +	else if (skip_prefix(remoteurl, "./", &out) ||
> +		    skip_prefix(remoteurl, "../", &out))
> +		is_relative = 1;
> +	else {
> +		is_relative = 1;
> +		strbuf_reset(&sb);
> +		strbuf_addf(&sb, "./%s", remoteurl);
> +		remoteurl = strbuf_detach(&sb, NULL);
> +	}
> +
> +	while (url) {
> +		if (skip_prefix(url, "../", &out)) {
> +			char *rfind;
> +			url = out;
> +
> +			rfind = strrchr(remoteurl, '/');
> +			if (rfind)
> +				*rfind = '\0';
> +			else {
> +				rfind = strrchr(remoteurl, ':');
> +				if (rfind) {
> +					*rfind = '\0';
> +					sep = ":";
> +				} else {
> +					if (is_relative || !strcmp(".", remoteurl))
> +						die(N_("cannot strip one component off url '%s'"), remoteurl);
> +					else
> +						remoteurl = ".";
> +				}
> +			}
> +		} else if (skip_prefix(url, "./", &out))
> +			url = out;
> +		else
> +			break;
> +	}
> +	strbuf_reset(&sb);
> +	strbuf_addf(&sb, "%s%s%s", remoteurl, sep, url);
> +
> +	if (!skip_prefix(sb.buf, "./", &out))
> +		out = sb.buf;
> +	out = xstrdup(out);
> +
> +	strbuf_reset(&sb);
> +	strbuf_addf(&sb, "%s%s", is_relative && up_path ? up_path : "", out);
> +
> +	free((char*)out);
> +	return strbuf_detach(&sb, NULL);
> +}
> +
> +static int resolve_relative_url(int argc, const char **argv, const char *prefix)
> +{
> +	if (argc == 2)
> +		printf("%s\n", relative_url(argv[1], NULL));
> +	else if (argc == 3)
> +		printf("%s\n", relative_url(argv[1], argv[2]));
> +	else
> +		die("BUG: resolve_relative_url only accepts one or two arguments");
> +	return 0;
> +}
>
>   struct module_list {
>   	const struct cache_entry **entries;
> @@ -264,6 +383,7 @@ static struct cmd_struct commands[] = {
>   	{"list", module_list},
>   	{"name", module_name},
>   	{"clone", module_clone},
> +	{"resolve_relative_url", resolve_relative_url},
>   };
>
>   int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
> diff --git a/git-submodule.sh b/git-submodule.sh
> index 9bc5c5f..6a7a3e4 100755
> --- a/git-submodule.sh
> +++ b/git-submodule.sh
> @@ -46,79 +46,6 @@ prefix=
>   custom_name=
>   depth=
>
> -# The function takes at most 2 arguments. The first argument is the
> -# URL that navigates to the submodule origin repo. When relative, this URL
> -# is relative to the superproject origin URL repo. The second up_path
> -# argument, if specified, is the relative path that navigates
> -# from the submodule working tree to the superproject working tree.
> -#
> -# The output of the function is the origin URL of the submodule.
> -#
> -# The output will either be an absolute URL or filesystem path (if the
> -# superproject origin URL is an absolute URL or filesystem path,
> -# respectively) or a relative file system path (if the superproject
> -# origin URL is a relative file system path).
> -#
> -# When the output is a relative file system path, the path is either
> -# relative to the submodule working tree, if up_path is specified, or to
> -# the superproject working tree otherwise.
> -resolve_relative_url ()
> -{
> -	remote=$(get_default_remote)
> -	remoteurl=$(git config "remote.$remote.url") ||
> -		remoteurl=$(pwd) # the repository is its own authoritative upstream
> -	url="$1"
> -	remoteurl=${remoteurl%/}
> -	sep=/
> -	up_path="$2"
> -
> -	case "$remoteurl" in
> -	*:*|/*)
> -		is_relative=
> -		;;
> -	./*|../*)
> -		is_relative=t
> -		;;
> -	*)
> -		is_relative=t
> -		remoteurl="./$remoteurl"
> -		;;
> -	esac
> -
> -	while test -n "$url"
> -	do
> -		case "$url" in
> -		../*)
> -			url="${url#../}"
> -			case "$remoteurl" in
> -			*/*)
> -				remoteurl="${remoteurl%/*}"
> -				;;
> -			*:*)
> -				remoteurl="${remoteurl%:*}"
> -				sep=:
> -				;;
> -			*)
> -				if test -z "$is_relative" || test "." = "$remoteurl"
> -				then
> -					die "$(eval_gettext "cannot strip one component off url '\$remoteurl'")"
> -				else
> -					remoteurl=.
> -				fi
> -				;;
> -			esac
> -			;;
> -		./*)
> -			url="${url#./}"
> -			;;
> -		*)
> -			break;;
> -		esac
> -	done
> -	remoteurl="$remoteurl$sep${url%/}"
> -	echo "${is_relative:+${up_path}}${remoteurl#./}"
> -}
> -
>   # Resolve a path to be relative to another path.  This is intended for
>   # converting submodule paths when git-submodule is run in a subdirectory
>   # and only handles paths where the directory separator is '/'.
> @@ -281,7 +208,7 @@ cmd_add()
>   		die "$(gettext "Relative path can only be used from the toplevel of the working tree")"
>
>   		# dereference source url relative to parent's url
> -		realrepo=$(resolve_relative_url "$repo") || exit
> +		realrepo=$(git submodule--helper resolve_relative_url "$repo") || exit
>   		;;
>   	*:*|/*)
>   		# absolute url
> @@ -485,7 +412,7 @@ cmd_init()
>   			# Possibly a url relative to parent
>   			case "$url" in
>   			./*|../*)
> -				url=$(resolve_relative_url "$url") || exit
> +				url=$(git submodule--helper resolve_relative_url "$url") || exit
>   				;;
>   			esac
>   			git config submodule."$name".url "$url" ||
> @@ -1190,9 +1117,9 @@ cmd_sync()
>   			# guarantee a trailing /
>   			up_path=${up_path%/}/ &&
>   			# path from submodule work tree to submodule origin repo
> -			sub_origin_url=$(resolve_relative_url "$url" "$up_path") &&
> +			sub_origin_url=$(git submodule--helper resolve_relative_url "$url" "$up_path") &&
>   			# path from superproject work tree to submodule origin repo
> -			super_config_url=$(resolve_relative_url "$url") || exit
> +			super_config_url=$(git submodule--helper resolve_relative_url "$url") || exit
>   			;;
>   		*)
>   			sub_origin_url="$url"
>

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] submodule: Port resolve_relative_url from shell to C
  2015-12-10  6:48 ` Johannes Sixt
@ 2015-12-16 22:36   ` Stefan Beller
  0 siblings, 0 replies; 12+ messages in thread
From: Stefan Beller @ 2015-12-16 22:36 UTC (permalink / raw)
  To: Johannes Sixt; +Cc: Junio C Hamano, git@vger.kernel.org, Jens Lehmann

On Wed, Dec 9, 2015 at 10:48 PM, Johannes Sixt <j6t@kdbg.org> wrote:
> Am 10.12.2015 um 02:07 schrieb Stefan Beller:
>>
>> This reimplements the helper function `resolve_relative_url` in shell
>> in C. This functionality is needed in C for introducing the groups
>> feature later on. When using groups, the user should not need to run
>> `git submodule init`, but it should be implicit at all appropriate places,
>> which are all in C code. As the we would not just call out to `git
>> submodule init`, but do a more fine grained structure there, we actually
>> need all the init functionality in C before attempting the groups
>> feature. To get the init functionality in C, rewriting the
>> resolve_relative_url subfunction is a major step.
>
>
> I see lots of '/', but no is_dir_sep() in the C version. Did you consider
> that local URLs can use a backslash as path separator on Windows? In the
> shell version, this did not matter because bash converts the backslashes to
> forward slashes for us. But when rewritten in C, this does not happen.

I see. That's a pity. :(

>
> Valid URLs are
>
>   D:\foo\bar.git
>   \\server\share\foo\bar
>   ..\..\foo\bar

I am staring at the code in desperation as backslashes
in Linux are valid for file names, i.e.:
"/tmp/testfile\" is a valid filename

Now look at the code where the first slash occurs, it's

    if (strip_suffix(remoteurl, "/", &len))
        remoteurl[len] = '\0';

The intention is to strip off the last character if it is a directory separator.
So in the unix world we want to keep "/tmp/testfile\" as it is,
whereas in Windows
we want to chop off the backslash (because there is no file with a
backslash allowed,
it is the directory separator?)

So what I think I am going to do for next round is something like

    static int has_same_dir_prefix(const char *str, const char **out)
    {
    #ifdef GIT_WINDOWS_NATIVE
        return skip_prefix(str, "./", out)
           || skip_prefix(str, ".\\", out);
    #else
        return skip_prefix(str, "./", out);
    #endif
    }

    static int has_upper_dir_prefix(const char *str, const char **out)
    {
    #ifdef GIT_WINDOWS_NATIVE
        return skip_prefix(str, "../", out)
           || skip_prefix(str, "..\\", out);
    #else
        return skip_prefix(str, "../", out);
    #endif
    }

in the submodule helper function, or alternatively in the wrapper.c ?
and then rely on these functions being accurate.

Would that approach make sense?

Thanks,
Stefan


>
> and all of them with some or all backslashes replaced by forward slashes.
>
> See also connect.c:url_is_local_not_ssh, which ensures that the first
> example above is considered a local path with a drive letter, not a remote
> ssh path.
>
>
>>
>> This also improves the performance:
>> (Best out of 3) time ./t7400-submodule-basic.sh
>> Before:
>> real    0m9.575s
>> user    0m2.683s
>> sys     0m6.773s
>> After:
>> real    0m9.293s
>> user    0m2.691s
>> sys     0m6.549s
>>
>> Signed-off-by: Stefan Beller <sbeller@google.com>
>> ---
>>
>>   This applies on origin/master, and I'd carry as its own feature branch
>>   as I am nowhere near done with the groups feature after reading Jens
>> feedback.
>>   (It took me a while to identify this as a next best step.)
>>
>>   Thanks,
>>   Stefan
>>
>>   builtin/submodule--helper.c | 120
>> ++++++++++++++++++++++++++++++++++++++++++++
>>   git-submodule.sh            |  81 ++----------------------------
>>   2 files changed, 124 insertions(+), 77 deletions(-)
>>
>> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
>> index f4c3eff..f48b5b5 100644
>> --- a/builtin/submodule--helper.c
>> +++ b/builtin/submodule--helper.c
>> @@ -9,6 +9,125 @@
>>   #include "submodule-config.h"
>>   #include "string-list.h"
>>   #include "run-command.h"
>> +#include "remote.h"
>> +#include "refs.h"
>> +
>> +static const char *get_default_remote(void)
>> +{
>> +       char *dest = NULL;
>> +       unsigned char sha1[20];
>> +       int flag;
>> +       struct strbuf sb = STRBUF_INIT;
>> +       const char *refname = resolve_ref_unsafe("HEAD", 0, sha1, &flag);
>> +
>> +       if (!refname)
>> +               die("No such ref: HEAD");
>> +
>> +       refname = shorten_unambiguous_ref(refname, 0);
>> +       strbuf_addf(&sb, "branch.%s.remote", refname);
>> +       if (git_config_get_string(sb.buf, &dest))
>> +               return "origin";
>> +       else
>> +               return xstrdup(dest);
>> +}
>> +
>> +/*
>> + * The function takes at most 2 arguments. The first argument is the
>> + * URL that navigates to the submodule origin repo. When relative, this
>> URL
>> + * is relative to the superproject origin URL repo. The second up_path
>> + * argument, if specified, is the relative path that navigates
>> + * from the submodule working tree to the superproject working tree.
>> + *
>> + * The output of the function is the origin URL of the submodule.
>> + *
>> + * The output will either be an absolute URL or filesystem path (if the
>> + * superproject origin URL is an absolute URL or filesystem path,
>> + * respectively) or a relative file system path (if the superproject
>> + * origin URL is a relative file system path).
>> + *
>> + * When the output is a relative file system path, the path is either
>> + * relative to the submodule working tree, if up_path is specified, or to
>> + * the superproject working tree otherwise.
>> + */
>> +static const char *relative_url(const char *url, const char *up_path)
>> +{
>> +       int is_relative = 0;
>> +       size_t len;
>> +       char *remoteurl = NULL;
>> +       char *sep = "/";
>> +       const char *out;
>> +       struct strbuf sb = STRBUF_INIT;
>> +       const char *remote = get_default_remote();
>> +       strbuf_addf(&sb, "remote.%s.url", remote);
>> +
>> +       if (git_config_get_string(sb.buf, &remoteurl))
>> +               /* the repository is its own authoritative upstream */
>> +               remoteurl = xgetcwd();
>> +
>> +       if (strip_suffix(remoteurl, "/", &len))
>> +               remoteurl[len] = '\0';
>> +
>> +       if (strchr(remoteurl, ':') || skip_prefix(remoteurl, "/", &out))
>> +               is_relative = 0;
>> +       else if (skip_prefix(remoteurl, "./", &out) ||
>> +                   skip_prefix(remoteurl, "../", &out))
>> +               is_relative = 1;
>> +       else {
>> +               is_relative = 1;
>> +               strbuf_reset(&sb);
>> +               strbuf_addf(&sb, "./%s", remoteurl);
>> +               remoteurl = strbuf_detach(&sb, NULL);
>> +       }
>> +
>> +       while (url) {
>> +               if (skip_prefix(url, "../", &out)) {
>> +                       char *rfind;
>> +                       url = out;
>> +
>> +                       rfind = strrchr(remoteurl, '/');
>> +                       if (rfind)
>> +                               *rfind = '\0';
>> +                       else {
>> +                               rfind = strrchr(remoteurl, ':');
>> +                               if (rfind) {
>> +                                       *rfind = '\0';
>> +                                       sep = ":";
>> +                               } else {
>> +                                       if (is_relative || !strcmp(".",
>> remoteurl))
>> +                                               die(N_("cannot strip one
>> component off url '%s'"), remoteurl);
>> +                                       else
>> +                                               remoteurl = ".";
>> +                               }
>> +                       }
>> +               } else if (skip_prefix(url, "./", &out))
>> +                       url = out;
>> +               else
>> +                       break;
>> +       }
>> +       strbuf_reset(&sb);
>> +       strbuf_addf(&sb, "%s%s%s", remoteurl, sep, url);
>> +
>> +       if (!skip_prefix(sb.buf, "./", &out))
>> +               out = sb.buf;
>> +       out = xstrdup(out);
>> +
>> +       strbuf_reset(&sb);
>> +       strbuf_addf(&sb, "%s%s", is_relative && up_path ? up_path : "",
>> out);
>> +
>> +       free((char*)out);
>> +       return strbuf_detach(&sb, NULL);
>> +}
>> +
>> +static int resolve_relative_url(int argc, const char **argv, const char
>> *prefix)
>> +{
>> +       if (argc == 2)
>> +               printf("%s\n", relative_url(argv[1], NULL));
>> +       else if (argc == 3)
>> +               printf("%s\n", relative_url(argv[1], argv[2]));
>> +       else
>> +               die("BUG: resolve_relative_url only accepts one or two
>> arguments");
>> +       return 0;
>> +}
>>
>>   struct module_list {
>>         const struct cache_entry **entries;
>> @@ -264,6 +383,7 @@ static struct cmd_struct commands[] = {
>>         {"list", module_list},
>>         {"name", module_name},
>>         {"clone", module_clone},
>> +       {"resolve_relative_url", resolve_relative_url},
>>   };
>>
>>   int cmd_submodule__helper(int argc, const char **argv, const char
>> *prefix)
>> diff --git a/git-submodule.sh b/git-submodule.sh
>> index 9bc5c5f..6a7a3e4 100755
>> --- a/git-submodule.sh
>> +++ b/git-submodule.sh
>> @@ -46,79 +46,6 @@ prefix=
>>   custom_name=
>>   depth=
>>
>> -# The function takes at most 2 arguments. The first argument is the
>> -# URL that navigates to the submodule origin repo. When relative, this
>> URL
>> -# is relative to the superproject origin URL repo. The second up_path
>> -# argument, if specified, is the relative path that navigates
>> -# from the submodule working tree to the superproject working tree.
>> -#
>> -# The output of the function is the origin URL of the submodule.
>> -#
>> -# The output will either be an absolute URL or filesystem path (if the
>> -# superproject origin URL is an absolute URL or filesystem path,
>> -# respectively) or a relative file system path (if the superproject
>> -# origin URL is a relative file system path).
>> -#
>> -# When the output is a relative file system path, the path is either
>> -# relative to the submodule working tree, if up_path is specified, or to
>> -# the superproject working tree otherwise.
>> -resolve_relative_url ()
>> -{
>> -       remote=$(get_default_remote)
>> -       remoteurl=$(git config "remote.$remote.url") ||
>> -               remoteurl=$(pwd) # the repository is its own authoritative
>> upstream
>> -       url="$1"
>> -       remoteurl=${remoteurl%/}
>> -       sep=/
>> -       up_path="$2"
>> -
>> -       case "$remoteurl" in
>> -       *:*|/*)
>> -               is_relative=
>> -               ;;
>> -       ./*|../*)
>> -               is_relative=t
>> -               ;;
>> -       *)
>> -               is_relative=t
>> -               remoteurl="./$remoteurl"
>> -               ;;
>> -       esac
>> -
>> -       while test -n "$url"
>> -       do
>> -               case "$url" in
>> -               ../*)
>> -                       url="${url#../}"
>> -                       case "$remoteurl" in
>> -                       */*)
>> -                               remoteurl="${remoteurl%/*}"
>> -                               ;;
>> -                       *:*)
>> -                               remoteurl="${remoteurl%:*}"
>> -                               sep=:
>> -                               ;;
>> -                       *)
>> -                               if test -z "$is_relative" || test "." =
>> "$remoteurl"
>> -                               then
>> -                                       die "$(eval_gettext "cannot strip
>> one component off url '\$remoteurl'")"
>> -                               else
>> -                                       remoteurl=.
>> -                               fi
>> -                               ;;
>> -                       esac
>> -                       ;;
>> -               ./*)
>> -                       url="${url#./}"
>> -                       ;;
>> -               *)
>> -                       break;;
>> -               esac
>> -       done
>> -       remoteurl="$remoteurl$sep${url%/}"
>> -       echo "${is_relative:+${up_path}}${remoteurl#./}"
>> -}
>> -
>>   # Resolve a path to be relative to another path.  This is intended for
>>   # converting submodule paths when git-submodule is run in a subdirectory
>>   # and only handles paths where the directory separator is '/'.
>> @@ -281,7 +208,7 @@ cmd_add()
>>                 die "$(gettext "Relative path can only be used from the
>> toplevel of the working tree")"
>>
>>                 # dereference source url relative to parent's url
>> -               realrepo=$(resolve_relative_url "$repo") || exit
>> +               realrepo=$(git submodule--helper resolve_relative_url
>> "$repo") || exit
>>                 ;;
>>         *:*|/*)
>>                 # absolute url
>> @@ -485,7 +412,7 @@ cmd_init()
>>                         # Possibly a url relative to parent
>>                         case "$url" in
>>                         ./*|../*)
>> -                               url=$(resolve_relative_url "$url") || exit
>> +                               url=$(git submodule--helper
>> resolve_relative_url "$url") || exit
>>                                 ;;
>>                         esac
>>                         git config submodule."$name".url "$url" ||
>> @@ -1190,9 +1117,9 @@ cmd_sync()
>>                         # guarantee a trailing /
>>                         up_path=${up_path%/}/ &&
>>                         # path from submodule work tree to submodule
>> origin repo
>> -                       sub_origin_url=$(resolve_relative_url "$url"
>> "$up_path") &&
>> +                       sub_origin_url=$(git submodule--helper
>> resolve_relative_url "$url" "$up_path") &&
>>                         # path from superproject work tree to submodule
>> origin repo
>> -                       super_config_url=$(resolve_relative_url "$url") ||
>> exit
>> +                       super_config_url=$(git submodule--helper
>> resolve_relative_url "$url") || exit
>>                         ;;
>>                 *)
>>                         sub_origin_url="$url"
>>
>

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCHv2] Porting resolve_relative_url from shell to C
  2015-12-10  1:07 [PATCH] submodule: Port resolve_relative_url from shell to C Stefan Beller
  2015-12-10  6:48 ` Johannes Sixt
@ 2015-12-17  0:26 ` Stefan Beller
  2015-12-17  7:47   ` Torsten Bögershausen
  2015-12-17  0:26 ` [PATCHv2] submodule: Port " Stefan Beller
  2 siblings, 1 reply; 12+ messages in thread
From: Stefan Beller @ 2015-12-17  0:26 UTC (permalink / raw)
  To: sbeller, git; +Cc: gitster, jens.lehmann, j6t

A new version of the patch, which spells out more its intent and
may actually work in Windows.

Any comment welcome,
Thanks,
Stefan

Stefan Beller (1):
  submodule: Port resolve_relative_url from shell to C

 builtin/submodule--helper.c | 151 ++++++++++++++++++++++++++++++++++++++++++++
 git-submodule.sh            |  81 ++----------------------
 2 files changed, 155 insertions(+), 77 deletions(-)

interdiff to previous version:

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index f48b5b5..b925bed 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -31,6 +31,36 @@ static const char *get_default_remote(void)
 		return xstrdup(dest);
 }
 
+static int has_same_dir_prefix(const char *str, const char **out)
+{
+#ifdef GIT_WINDOWS_NATIVE
+	return skip_prefix(str, "./", out)
+		|| skip_prefix(str, ".\\", out);
+#else
+	return skip_prefix(str, "./", out);
+#endif
+}
+
+static int has_upper_dir_prefix(const char *str, const char **out)
+{
+#ifdef GIT_WINDOWS_NATIVE
+	return skip_prefix(str, "../", out)
+		|| skip_prefix(str, "..\\", out);
+#else
+	return skip_prefix(str, "../", out);
+#endif
+}
+
+static char *last_dir_separator(const char *str)
+{
+#ifdef GIT_WINDOWS_NATIVE
+	return strrchr(str, "/")
+		|| strrchr(str, "\\");
+#else
+	return strrchr(str, '/');
+#endif
+}
+
 /*
  * The function takes at most 2 arguments. The first argument is the
  * URL that navigates to the submodule origin repo. When relative, this URL
@@ -64,13 +94,14 @@ static const char *relative_url(const char *url, const char *up_path)
 		/* the repository is its own authoritative upstream */
 		remoteurl = xgetcwd();
 
-	if (strip_suffix(remoteurl, "/", &len))
+	len = strlen(remoteurl);
+	if (is_dir_sep(remoteurl[len]))
 		remoteurl[len] = '\0';
 
-	if (strchr(remoteurl, ':') || skip_prefix(remoteurl, "/", &out))
+	if (strchr(remoteurl, ':') || is_dir_sep(*remoteurl))
 		is_relative = 0;
-	else if (skip_prefix(remoteurl, "./", &out) ||
-		    skip_prefix(remoteurl, "../", &out))
+	else if (has_same_dir_prefix(remoteurl, &out) ||
+		    has_upper_dir_prefix(remoteurl, &out))
 		is_relative = 1;
 	else {
 		is_relative = 1;
@@ -80,11 +111,11 @@ static const char *relative_url(const char *url, const char *up_path)
 	}
 
 	while (url) {
-		if (skip_prefix(url, "../", &out)) {
+		if (has_upper_dir_prefix(url, &out)) {
 			char *rfind;
 			url = out;
 
-			rfind = strrchr(remoteurl, '/');
+			rfind = last_dir_separator(remoteurl);
 			if (rfind)
 				*rfind = '\0';
 			else {
@@ -99,7 +130,7 @@ static const char *relative_url(const char *url, const char *up_path)
 						remoteurl = ".";
 				}
 			}
-		} else if (skip_prefix(url, "./", &out))
+		} else if (has_same_dir_prefix(url, &out))
 			url = out;
 		else
 			break;
@@ -107,7 +138,7 @@ static const char *relative_url(const char *url, const char *up_path)
 	strbuf_reset(&sb);
 	strbuf_addf(&sb, "%s%s%s", remoteurl, sep, url);
 
-	if (!skip_prefix(sb.buf, "./", &out))
+	if (!has_same_dir_prefix(sb.buf, &out))
 		out = sb.buf;
 	out = xstrdup(out);
 

-- 
2.7.0.rc1.2.gfc39790.dirty

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCHv2] submodule: Port resolve_relative_url from shell to C
  2015-12-10  1:07 [PATCH] submodule: Port resolve_relative_url from shell to C Stefan Beller
  2015-12-10  6:48 ` Johannes Sixt
  2015-12-17  0:26 ` [PATCHv2] Porting " Stefan Beller
@ 2015-12-17  0:26 ` Stefan Beller
  2015-12-17  8:17   ` Eric Sunshine
  2015-12-17 18:55   ` Johannes Sixt
  2 siblings, 2 replies; 12+ messages in thread
From: Stefan Beller @ 2015-12-17  0:26 UTC (permalink / raw)
  To: sbeller, git; +Cc: gitster, jens.lehmann, j6t

This reimplements the helper function `resolve_relative_url` in shell
in C. This functionality is needed in C for introducing the groups
feature later on. When using groups, the user should not need to run
`git submodule init`, but it should be implicit at all appropriate places,
which are all in C code. As the we would not just call out to `git
submodule init`, but do a more fine grained structure there, we actually
need all the init functionality in C before attempting the groups
feature. To get the init functionality in C, rewriting the
resolve_relative_url subfunction is a major step.

This also improves the performance:
(Best out of 3) time ./t7400-submodule-basic.sh
Before:
real	0m9.575s
user	0m2.683s
sys	0m6.773s
After:
real	0m9.293s
user	0m2.691s
sys	0m6.549s

That's about 3%.

Signed-off-by: Stefan Beller <sbeller@google.com>
---
 builtin/submodule--helper.c | 151 ++++++++++++++++++++++++++++++++++++++++++++
 git-submodule.sh            |  81 ++----------------------
 2 files changed, 155 insertions(+), 77 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index f4c3eff..b925bed 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -9,6 +9,156 @@
 #include "submodule-config.h"
 #include "string-list.h"
 #include "run-command.h"
+#include "remote.h"
+#include "refs.h"
+
+static const char *get_default_remote(void)
+{
+	char *dest = NULL;
+	unsigned char sha1[20];
+	int flag;
+	struct strbuf sb = STRBUF_INIT;
+	const char *refname = resolve_ref_unsafe("HEAD", 0, sha1, &flag);
+
+	if (!refname)
+		die("No such ref: HEAD");
+
+	refname = shorten_unambiguous_ref(refname, 0);
+	strbuf_addf(&sb, "branch.%s.remote", refname);
+	if (git_config_get_string(sb.buf, &dest))
+		return "origin";
+	else
+		return xstrdup(dest);
+}
+
+static int has_same_dir_prefix(const char *str, const char **out)
+{
+#ifdef GIT_WINDOWS_NATIVE
+	return skip_prefix(str, "./", out)
+		|| skip_prefix(str, ".\\", out);
+#else
+	return skip_prefix(str, "./", out);
+#endif
+}
+
+static int has_upper_dir_prefix(const char *str, const char **out)
+{
+#ifdef GIT_WINDOWS_NATIVE
+	return skip_prefix(str, "../", out)
+		|| skip_prefix(str, "..\\", out);
+#else
+	return skip_prefix(str, "../", out);
+#endif
+}
+
+static char *last_dir_separator(const char *str)
+{
+#ifdef GIT_WINDOWS_NATIVE
+	return strrchr(str, "/")
+		|| strrchr(str, "\\");
+#else
+	return strrchr(str, '/');
+#endif
+}
+
+/*
+ * The function takes at most 2 arguments. The first argument is the
+ * URL that navigates to the submodule origin repo. When relative, this URL
+ * is relative to the superproject origin URL repo. The second up_path
+ * argument, if specified, is the relative path that navigates
+ * from the submodule working tree to the superproject working tree.
+ *
+ * The output of the function is the origin URL of the submodule.
+ *
+ * The output will either be an absolute URL or filesystem path (if the
+ * superproject origin URL is an absolute URL or filesystem path,
+ * respectively) or a relative file system path (if the superproject
+ * origin URL is a relative file system path).
+ *
+ * When the output is a relative file system path, the path is either
+ * relative to the submodule working tree, if up_path is specified, or to
+ * the superproject working tree otherwise.
+ */
+static const char *relative_url(const char *url, const char *up_path)
+{
+	int is_relative = 0;
+	size_t len;
+	char *remoteurl = NULL;
+	char *sep = "/";
+	const char *out;
+	struct strbuf sb = STRBUF_INIT;
+	const char *remote = get_default_remote();
+	strbuf_addf(&sb, "remote.%s.url", remote);
+
+	if (git_config_get_string(sb.buf, &remoteurl))
+		/* the repository is its own authoritative upstream */
+		remoteurl = xgetcwd();
+
+	len = strlen(remoteurl);
+	if (is_dir_sep(remoteurl[len]))
+		remoteurl[len] = '\0';
+
+	if (strchr(remoteurl, ':') || is_dir_sep(*remoteurl))
+		is_relative = 0;
+	else if (has_same_dir_prefix(remoteurl, &out) ||
+		    has_upper_dir_prefix(remoteurl, &out))
+		is_relative = 1;
+	else {
+		is_relative = 1;
+		strbuf_reset(&sb);
+		strbuf_addf(&sb, "./%s", remoteurl);
+		remoteurl = strbuf_detach(&sb, NULL);
+	}
+
+	while (url) {
+		if (has_upper_dir_prefix(url, &out)) {
+			char *rfind;
+			url = out;
+
+			rfind = last_dir_separator(remoteurl);
+			if (rfind)
+				*rfind = '\0';
+			else {
+				rfind = strrchr(remoteurl, ':');
+				if (rfind) {
+					*rfind = '\0';
+					sep = ":";
+				} else {
+					if (is_relative || !strcmp(".", remoteurl))
+						die(N_("cannot strip one component off url '%s'"), remoteurl);
+					else
+						remoteurl = ".";
+				}
+			}
+		} else if (has_same_dir_prefix(url, &out))
+			url = out;
+		else
+			break;
+	}
+	strbuf_reset(&sb);
+	strbuf_addf(&sb, "%s%s%s", remoteurl, sep, url);
+
+	if (!has_same_dir_prefix(sb.buf, &out))
+		out = sb.buf;
+	out = xstrdup(out);
+
+	strbuf_reset(&sb);
+	strbuf_addf(&sb, "%s%s", is_relative && up_path ? up_path : "", out);
+
+	free((char*)out);
+	return strbuf_detach(&sb, NULL);
+}
+
+static int resolve_relative_url(int argc, const char **argv, const char *prefix)
+{
+	if (argc == 2)
+		printf("%s\n", relative_url(argv[1], NULL));
+	else if (argc == 3)
+		printf("%s\n", relative_url(argv[1], argv[2]));
+	else
+		die("BUG: resolve_relative_url only accepts one or two arguments");
+	return 0;
+}
 
 struct module_list {
 	const struct cache_entry **entries;
@@ -264,6 +414,7 @@ static struct cmd_struct commands[] = {
 	{"list", module_list},
 	{"name", module_name},
 	{"clone", module_clone},
+	{"resolve_relative_url", resolve_relative_url},
 };
 
 int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
diff --git a/git-submodule.sh b/git-submodule.sh
index 9bc5c5f..6a7a3e4 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -46,79 +46,6 @@ prefix=
 custom_name=
 depth=
 
-# The function takes at most 2 arguments. The first argument is the
-# URL that navigates to the submodule origin repo. When relative, this URL
-# is relative to the superproject origin URL repo. The second up_path
-# argument, if specified, is the relative path that navigates
-# from the submodule working tree to the superproject working tree.
-#
-# The output of the function is the origin URL of the submodule.
-#
-# The output will either be an absolute URL or filesystem path (if the
-# superproject origin URL is an absolute URL or filesystem path,
-# respectively) or a relative file system path (if the superproject
-# origin URL is a relative file system path).
-#
-# When the output is a relative file system path, the path is either
-# relative to the submodule working tree, if up_path is specified, or to
-# the superproject working tree otherwise.
-resolve_relative_url ()
-{
-	remote=$(get_default_remote)
-	remoteurl=$(git config "remote.$remote.url") ||
-		remoteurl=$(pwd) # the repository is its own authoritative upstream
-	url="$1"
-	remoteurl=${remoteurl%/}
-	sep=/
-	up_path="$2"
-
-	case "$remoteurl" in
-	*:*|/*)
-		is_relative=
-		;;
-	./*|../*)
-		is_relative=t
-		;;
-	*)
-		is_relative=t
-		remoteurl="./$remoteurl"
-		;;
-	esac
-
-	while test -n "$url"
-	do
-		case "$url" in
-		../*)
-			url="${url#../}"
-			case "$remoteurl" in
-			*/*)
-				remoteurl="${remoteurl%/*}"
-				;;
-			*:*)
-				remoteurl="${remoteurl%:*}"
-				sep=:
-				;;
-			*)
-				if test -z "$is_relative" || test "." = "$remoteurl"
-				then
-					die "$(eval_gettext "cannot strip one component off url '\$remoteurl'")"
-				else
-					remoteurl=.
-				fi
-				;;
-			esac
-			;;
-		./*)
-			url="${url#./}"
-			;;
-		*)
-			break;;
-		esac
-	done
-	remoteurl="$remoteurl$sep${url%/}"
-	echo "${is_relative:+${up_path}}${remoteurl#./}"
-}
-
 # Resolve a path to be relative to another path.  This is intended for
 # converting submodule paths when git-submodule is run in a subdirectory
 # and only handles paths where the directory separator is '/'.
@@ -281,7 +208,7 @@ cmd_add()
 		die "$(gettext "Relative path can only be used from the toplevel of the working tree")"
 
 		# dereference source url relative to parent's url
-		realrepo=$(resolve_relative_url "$repo") || exit
+		realrepo=$(git submodule--helper resolve_relative_url "$repo") || exit
 		;;
 	*:*|/*)
 		# absolute url
@@ -485,7 +412,7 @@ cmd_init()
 			# Possibly a url relative to parent
 			case "$url" in
 			./*|../*)
-				url=$(resolve_relative_url "$url") || exit
+				url=$(git submodule--helper resolve_relative_url "$url") || exit
 				;;
 			esac
 			git config submodule."$name".url "$url" ||
@@ -1190,9 +1117,9 @@ cmd_sync()
 			# guarantee a trailing /
 			up_path=${up_path%/}/ &&
 			# path from submodule work tree to submodule origin repo
-			sub_origin_url=$(resolve_relative_url "$url" "$up_path") &&
+			sub_origin_url=$(git submodule--helper resolve_relative_url "$url" "$up_path") &&
 			# path from superproject work tree to submodule origin repo
-			super_config_url=$(resolve_relative_url "$url") || exit
+			super_config_url=$(git submodule--helper resolve_relative_url "$url") || exit
 			;;
 		*)
 			sub_origin_url="$url"
-- 
2.7.0.rc1.2.gfc39790.dirty

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCHv2] Porting resolve_relative_url from shell to C
  2015-12-17  0:26 ` [PATCHv2] Porting " Stefan Beller
@ 2015-12-17  7:47   ` Torsten Bögershausen
  0 siblings, 0 replies; 12+ messages in thread
From: Torsten Bögershausen @ 2015-12-17  7:47 UTC (permalink / raw)
  To: Stefan Beller, git; +Cc: gitster, jens.lehmann, j6t

On 17.12.15 01:26, Stefan Beller wrote:
> A new version of the patch, which spells out more its intent and
> may actually work in Windows.
> 
> Any comment welcome,
> Thanks,
> Stefan
> 
> Stefan Beller (1):
>   submodule: Port resolve_relative_url from shell to C
> 
>  builtin/submodule--helper.c | 151 ++++++++++++++++++++++++++++++++++++++++++++
>  git-submodule.sh            |  81 ++----------------------
>  2 files changed, 155 insertions(+), 77 deletions(-)
> 
> interdiff to previous version:
> 
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index f48b5b5..b925bed 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -31,6 +31,36 @@ static const char *get_default_remote(void)
>  		return xstrdup(dest);
>  }
>  
> +static int has_same_dir_prefix(const char *str, const char **out)
> +{
> +#ifdef GIT_WINDOWS_NATIVE
Should that be
if defined(GIT_WINDOWS_NATIVE) || defined(__CYGWIN__)

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCHv2] submodule: Port resolve_relative_url from shell to C
  2015-12-17  0:26 ` [PATCHv2] submodule: Port " Stefan Beller
@ 2015-12-17  8:17   ` Eric Sunshine
  2015-12-17 18:55   ` Johannes Sixt
  1 sibling, 0 replies; 12+ messages in thread
From: Eric Sunshine @ 2015-12-17  8:17 UTC (permalink / raw)
  To: Stefan Beller; +Cc: Git List, Junio C Hamano, Jens Lehmann, Johannes Sixt

A rather superficial review...

On Wed, Dec 16, 2015 at 7:26 PM, Stefan Beller <sbeller@google.com> wrote:
> This reimplements the helper function `resolve_relative_url` in shell

s/This reimplements/Reimplement/

> in C. This functionality is needed in C for introducing the groups
> feature later on. When using groups, the user should not need to run
> `git submodule init`, but it should be implicit at all appropriate places,
> which are all in C code. As the we would not just call out to `git

I guess you mean "As then we..." or something?

> submodule init`, but do a more fine grained structure there, we actually
> need all the init functionality in C before attempting the groups
> feature. To get the init functionality in C, rewriting the
> resolve_relative_url subfunction is a major step.
>
> Signed-off-by: Stefan Beller <sbeller@google.com>
> ---
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> @@ -9,6 +9,156 @@
> +static const char *get_default_remote(void)
> +{
> +       char *dest = NULL;
> +       unsigned char sha1[20];
> +       int flag;
> +       struct strbuf sb = STRBUF_INIT;
> +       const char *refname = resolve_ref_unsafe("HEAD", 0, sha1, &flag);
> +
> +       if (!refname)
> +               die("No such ref: HEAD");
> +
> +       refname = shorten_unambiguous_ref(refname, 0);
> +       strbuf_addf(&sb, "branch.%s.remote", refname);
> +       if (git_config_get_string(sb.buf, &dest))
> +               return "origin";
> +       else
> +               return xstrdup(dest);

Leaking the strbuf at both returns.

And, leaking the strdup'd dest (in the caller), but I suppose that's
intentional?

> +}
> +
> +static int has_same_dir_prefix(const char *str, const char **out)
> +{
> +#ifdef GIT_WINDOWS_NATIVE
> +       return skip_prefix(str, "./", out)
> +               || skip_prefix(str, ".\\", out);
> +#else
> +       return skip_prefix(str, "./", out);
> +#endif
> +}
> +
> +static int has_upper_dir_prefix(const char *str, const char **out)
> +{
> +#ifdef GIT_WINDOWS_NATIVE
> +       return skip_prefix(str, "../", out)
> +               || skip_prefix(str, "..\\", out);
> +#else
> +       return skip_prefix(str, "../", out);
> +#endif
> +}
> +
> +static char *last_dir_separator(const char *str)
> +{
> +#ifdef GIT_WINDOWS_NATIVE
> +       return strrchr(str, "/")
> +               || strrchr(str, "\\");
> +#else
> +       return strrchr(str, '/');
> +#endif
> +}

Cleaner would be to move the #if's outside the functions:

    #ifdef GIT_WINDOWS_NATIVE

    /* Windows implementations */
    static int has_same_dir_prefix(...) {...}
    static int has_upper_dir_prefix(...) {...}
    static char *last_dir_separator(...) {...}

    #else

    /* POSIX implementations */
    static int has_same_dir_prefix(...) {...}
    static int has_upper_dir_prefix(...) {...}
    static char *last_dir_separator(...) {...}

    #endif

> +static const char *relative_url(const char *url, const char *up_path)
> +{
> +       int is_relative = 0;
> +       size_t len;
> +       char *remoteurl = NULL;
> +       char *sep = "/";

'sep' only ever holds a single character, so why not declare it 'char'
rather than 'char *'? (And, adjust the format string of strbuf_addf(),
of course.)

> +       const char *out;
> +       [...]
> +       while (url) {
> +               if (has_upper_dir_prefix(url, &out)) {
> +                       [...]
> +                       if (rfind)
> +                               *rfind = '\0';
> +                       else {
> +                               rfind = strrchr(remoteurl, ':');
> +                               if (rfind) {
> +                                       *rfind = '\0';
> +                                       sep = ":";
> +                               } else {
> +                                       [...]
> +                               }
> +                       }
> +               } else if (has_same_dir_prefix(url, &out))
> +                       url = out;
> +               else
> +                       break;
> +       }
> +       strbuf_reset(&sb);
> +       strbuf_addf(&sb, "%s%s%s", remoteurl, sep, url);
> +
> +       if (!has_same_dir_prefix(sb.buf, &out))
> +               out = sb.buf;
> +       out = xstrdup(out);
> +
> +       strbuf_reset(&sb);
> +       strbuf_addf(&sb, "%s%s", is_relative && up_path ? up_path : "", out);
> +
> +       free((char*)out);

Why declare 'out' const if you know you will be freeing it?

> +       return strbuf_detach(&sb, NULL);
> +}
> +
> +static int resolve_relative_url(int argc, const char **argv, const char *prefix)
> +{
> +       if (argc == 2)
> +               printf("%s\n", relative_url(argv[1], NULL));
> +       else if (argc == 3)
> +               printf("%s\n", relative_url(argv[1], argv[2]));
> +       else
> +               die("BUG: resolve_relative_url only accepts one or two arguments");
> +       return 0;
> +}
>
>  struct module_list {
>         const struct cache_entry **entries;
> @@ -264,6 +414,7 @@ static struct cmd_struct commands[] = {
>         {"list", module_list},
>         {"name", module_name},
>         {"clone", module_clone},
> +       {"resolve_relative_url", resolve_relative_url},

Can we please use hyphens rather than underscores, and name this
"resolve-relative-url" instead? Quoting from a review[1] of an earlier
version of git-submodule--helper:

    ... these subcommands would be better spelled with a hyphen than
    an underscore. If I recall correctly, the arguments for using
    underscore were (1) a less noisy diff, (2) these are internal
    commands nobody will be typing anyhow. However, (1) the diff
    noise will be the same with hyphens, and (2) people will want to
    test these commands manually anyhow, and its easier to type
    hyphens and easier to remember them since the precedent for
    hyphens in command-names is already well established.

    Also, the reason that the original shell code used underscores
    was because hyphens are not valid characters in shell function
    names, but that's an implementation detail which shouldn't be
    allowed to bleed over to user-facing interface design (and these
    subcommands are user-facing).

[1]: http://article.gmane.org/gmane.comp.version-control.git/276947

>  };

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCHv2] submodule: Port resolve_relative_url from shell to C
  2015-12-17  0:26 ` [PATCHv2] submodule: Port " Stefan Beller
  2015-12-17  8:17   ` Eric Sunshine
@ 2015-12-17 18:55   ` Johannes Sixt
  2015-12-17 19:17     ` Johannes Sixt
                       ` (2 more replies)
  1 sibling, 3 replies; 12+ messages in thread
From: Johannes Sixt @ 2015-12-17 18:55 UTC (permalink / raw)
  To: Stefan Beller; +Cc: git, gitster, jens.lehmann

Am 17.12.2015 um 01:26 schrieb Stefan Beller:
> This reimplements the helper function `resolve_relative_url` in shell
> in C. This functionality is needed in C for introducing the groups
> feature later on. When using groups, the user should not need to run
> `git submodule init`, but it should be implicit at all appropriate places,
> which are all in C code. As the we would not just call out to `git
> submodule init`, but do a more fine grained structure there, we actually
> need all the init functionality in C before attempting the groups
> feature. To get the init functionality in C, rewriting the
> resolve_relative_url subfunction is a major step.
>
> This also improves the performance:
> (Best out of 3) time ./t7400-submodule-basic.sh
> Before:
> real	0m9.575s
> user	0m2.683s
> sys	0m6.773s
> After:
> real	0m9.293s
> user	0m2.691s
> sys	0m6.549s
>
> That's about 3%.

I appreciate this effort as it should help us on Windows. Although the
numbers (and my own timings) suggest that this is only a small step
forward. That's not surprising as the patch removes only two forks.

As to the implementation, find a patch below that removes the ifdefs
and a few other suggestions. It is a mechanical conversion without
understanding what relative_url() does. I have the gut feeling that the
two strbuf_addf towards the end of the function can be contracted and
the temporarily allocate copy in 'out' can be removed.

If there were a few examples in the comment above the function, it
would be much simpler to understand.

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index b925bed..8ec0975 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -11,6 +11,7 @@
 #include "run-command.h"
 #include "remote.h"
 #include "refs.h"
+#include "connect.h"
 
 static const char *get_default_remote(void)
 {
@@ -31,34 +32,23 @@ static const char *get_default_remote(void)
 		return xstrdup(dest);
 }
 
-static int has_same_dir_prefix(const char *str, const char **out)
+static int starts_with_dot_slash(const char *str)
 {
-#ifdef GIT_WINDOWS_NATIVE
-	return skip_prefix(str, "./", out)
-		|| skip_prefix(str, ".\\", out);
-#else
-	return skip_prefix(str, "./", out);
-#endif
+	return str[0] == '.' && is_dir_sep(str[1]);
 }
 
-static int has_upper_dir_prefix(const char *str, const char **out)
+static int starts_with_dot_dot_slash(const char *str)
 {
-#ifdef GIT_WINDOWS_NATIVE
-	return skip_prefix(str, "../", out)
-		|| skip_prefix(str, "..\\", out);
-#else
-	return skip_prefix(str, "../", out);
-#endif
+	return str[0] == '.' && str[1] == '.' && is_dir_sep(str[2]);
 }
 
-static char *last_dir_separator(const char *str)
+static char *last_dir_separator(char *str)
 {
-#ifdef GIT_WINDOWS_NATIVE
-	return strrchr(str, "/")
-		|| strrchr(str, "\\");
-#else
-	return strrchr(str, '/');
-#endif
+	char* p = str + strlen(str);
+	while (p-- != str)
+		if (is_dir_sep(*p))
+			return p;
+	return NULL;
 }
 
 /*
@@ -85,9 +75,10 @@ static const char *relative_url(const char *url, const char *up_path)
 	size_t len;
 	char *remoteurl = NULL;
 	char *sep = "/";
-	const char *out;
+	char *out;
 	struct strbuf sb = STRBUF_INIT;
 	const char *remote = get_default_remote();
+
 	strbuf_addf(&sb, "remote.%s.url", remote);
 
 	if (git_config_get_string(sb.buf, &remoteurl))
@@ -98,22 +89,23 @@ static const char *relative_url(const char *url, const char *up_path)
 	if (is_dir_sep(remoteurl[len]))
 		remoteurl[len] = '\0';
 
-	if (strchr(remoteurl, ':') || is_dir_sep(*remoteurl))
+	if (!url_is_local_not_ssh(remoteurl) || is_absolute_path(remoteurl))
 		is_relative = 0;
-	else if (has_same_dir_prefix(remoteurl, &out) ||
-		    has_upper_dir_prefix(remoteurl, &out))
+	else if (starts_with_dot_slash(remoteurl) ||
+		    starts_with_dot_dot_slash(remoteurl))
 		is_relative = 1;
 	else {
 		is_relative = 1;
 		strbuf_reset(&sb);
 		strbuf_addf(&sb, "./%s", remoteurl);
+		free(remoteurl);
 		remoteurl = strbuf_detach(&sb, NULL);
 	}
 
 	while (url) {
-		if (has_upper_dir_prefix(url, &out)) {
+		if (starts_with_dot_dot_slash(url)) {
 			char *rfind;
-			url = out;
+			url += 3;
 
 			rfind = last_dir_separator(remoteurl);
 			if (rfind)
@@ -130,22 +122,23 @@ static const char *relative_url(const char *url, const char *up_path)
 						remoteurl = ".";
 				}
 			}
-		} else if (has_same_dir_prefix(url, &out))
-			url = out;
-		else
+		} else if (starts_with_dot_slash(url)) {
+			url += 2;
+		} else
 			break;
 	}
 	strbuf_reset(&sb);
 	strbuf_addf(&sb, "%s%s%s", remoteurl, sep, url);
 
-	if (!has_same_dir_prefix(sb.buf, &out))
-		out = sb.buf;
-	out = xstrdup(out);
+	if (starts_with_dot_slash(sb.buf))
+		out = strdup(sb.buf + 2);
+	else
+		out = xstrdup(sb.buf);
 
 	strbuf_reset(&sb);
 	strbuf_addf(&sb, "%s%s", is_relative && up_path ? up_path : "", out);
 
-	free((char*)out);
+	free(out);
 	return strbuf_detach(&sb, NULL);
 }
 

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCHv2] submodule: Port resolve_relative_url from shell to C
  2015-12-17 18:55   ` Johannes Sixt
@ 2015-12-17 19:17     ` Johannes Sixt
  2015-12-18  3:27     ` Jeff King
  2016-01-11 20:17     ` Stefan Beller
  2 siblings, 0 replies; 12+ messages in thread
From: Johannes Sixt @ 2015-12-17 19:17 UTC (permalink / raw)
  To: Stefan Beller; +Cc: git, gitster, jens.lehmann

Am 17.12.2015 um 19:55 schrieb Johannes Sixt:
> As to the implementation, find a patch below that removes the ifdefs
> and a few other suggestions.

The word "offers" is missing in this last half-sentence ;)

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCHv2] submodule: Port resolve_relative_url from shell to C
  2015-12-17 18:55   ` Johannes Sixt
  2015-12-17 19:17     ` Johannes Sixt
@ 2015-12-18  3:27     ` Jeff King
  2016-01-11 20:17     ` Stefan Beller
  2 siblings, 0 replies; 12+ messages in thread
From: Jeff King @ 2015-12-18  3:27 UTC (permalink / raw)
  To: Johannes Sixt; +Cc: Stefan Beller, git, gitster, jens.lehmann

On Thu, Dec 17, 2015 at 07:55:43PM +0100, Johannes Sixt wrote:

> -static int has_same_dir_prefix(const char *str, const char **out)
> +static int starts_with_dot_slash(const char *str)
>  {
> -#ifdef GIT_WINDOWS_NATIVE
> -	return skip_prefix(str, "./", out)
> -		|| skip_prefix(str, ".\\", out);
> -#else
> -	return skip_prefix(str, "./", out);
> -#endif
> +	return str[0] == '.' && is_dir_sep(str[1]);
>  }
>  
> -static int has_upper_dir_prefix(const char *str, const char **out)
> +static int starts_with_dot_dot_slash(const char *str)
>  {
> -#ifdef GIT_WINDOWS_NATIVE
> -	return skip_prefix(str, "../", out)
> -		|| skip_prefix(str, "..\\", out);
> -#else
> -	return skip_prefix(str, "../", out);
> -#endif
> +	return str[0] == '.' && str[1] == '.' && is_dir_sep(str[2]);
>  }

As the set of prefixes you are looking is probably bounded, it may not
be worth generalizing this. But I wondered if something like:

  /*
   * Like skip_prefix, but consider any "/" in the prefix as a
   * directory separator for the platform.
   */
  int skip_prefix_fs(const char *str, const char *prefix, const char **out)
  {
	while (1) {
		if (!*prefix) {
			*out = str;
			return 1;
		} else if (*prefix == '/') {
			if (!is_dir_sep(*str))
				return 0;
		} else {
			if (*str != *prefix)
				return 0;
		}
		str++;
		prefix++;
	}
  }

  ...
  /* works on all platforms! */
  if (skip_prefix_fs(foo, "./", &out))
	...

would be helpful. I don't know if there are other opportunities in the
code base that could make use of this. If it's just these two sites,
it's probably not worth it.

-Peff

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCHv2] submodule: Port resolve_relative_url from shell to C
  2015-12-17 18:55   ` Johannes Sixt
  2015-12-17 19:17     ` Johannes Sixt
  2015-12-18  3:27     ` Jeff King
@ 2016-01-11 20:17     ` Stefan Beller
  2 siblings, 0 replies; 12+ messages in thread
From: Stefan Beller @ 2016-01-11 20:17 UTC (permalink / raw)
  To: Johannes Sixt; +Cc: git@vger.kernel.org, Junio C Hamano, Jens Lehmann

I forgot to send out the draft last year.

On Thu, Dec 17, 2015 at 10:55 AM, Johannes Sixt <j6t@kdbg.org> wrote:
>> That's about 3%.
>
> I appreciate this effort as it should help us on Windows. Although the
> numbers (and my own timings) suggest that this is only a small step
> forward. That's not surprising as the patch removes only two forks.

ok. Probably the timings are not as important here anyways.

>
> As to the implementation, find a patch below that removes the ifdefs
> and a few other suggestions. It is a mechanical conversion without
> understanding what relative_url() does. I have the gut feeling that the
> two strbuf_addf towards the end of the function can be contracted and
> the temporarily allocate copy in 'out' can be removed.

I have the gut feeling, too. But I could not quite write down the solution.

>
> If there were a few examples in the comment above the function, it
> would be much simpler to understand.

I agree. So rewording the comment in the next reroll.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCHv2] submodule: Port resolve_relative_url from shell to C
@ 2016-01-12 23:35 Stefan Beller
  0 siblings, 0 replies; 12+ messages in thread
From: Stefan Beller @ 2016-01-12 23:35 UTC (permalink / raw)
  To: gitster, git; +Cc: peff, j6t, jens.lehmann, Stefan Beller

Later on we want to deprecate the `git submodule init` command and make
it implicit in other submodule commands. As these other commands are
written in C already, we'd need the init functionality in C, too.
The `resolve_relative_url` function is a major part of that init
functionality, so start by porting this function to C.

As I was porting the functionality I noticed some odds with the inputs.
To fully understand the situation I added some logging to the function
temporarily to capture all calls to the function throughout the test
suite. Duplicates have been removed and all unique testing inputs have
been recorded into t0060.

Signed-off-by: Stefan Beller <sbeller@google.com>
---

I tried considering an alternative implementation for `relative_url`,
which is not a direct translation of the shell code. There are some
advanced path/url functions, such as `normalize_path_copy`, however
using that function is not straightforward as it seems. The idea would
have been to use that on a concatenation of remoteurl and url, however
there are cases like ("foo/." "../.") to result in "foo/.", so we really
need to count the slashes ourselves.

This applies on top of current master (2.7.0).

Thanks,
Stefan

---
 builtin/submodule--helper.c | 191 ++++++++++++++++++++++++++++++++++++++++++++
 git-submodule.sh            |  81 +------------------
 t/t0060-path-utils.sh       |  41 ++++++++++
 3 files changed, 236 insertions(+), 77 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index f4c3eff..db9d627 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -9,6 +9,195 @@
 #include "submodule-config.h"
 #include "string-list.h"
 #include "run-command.h"
+#include "remote.h"
+#include "refs.h"
+#include "connect.h"
+
+static char *get_default_remote(void)
+{
+	char *dest = NULL, *ret;
+	unsigned char sha1[20];
+	int flag;
+	struct strbuf sb = STRBUF_INIT;
+	const char *refname = resolve_ref_unsafe("HEAD", 0, sha1, &flag);
+
+	if (!refname)
+		die("No such ref: HEAD");
+
+	refname = shorten_unambiguous_ref(refname, 0);
+	strbuf_addf(&sb, "branch.%s.remote", refname);
+	if (git_config_get_string(sb.buf, &dest))
+		ret = xstrdup("origin");
+	else
+		ret = xstrdup(dest);
+
+	strbuf_release(&sb);
+	return ret;
+}
+
+static int starts_with_dot_slash(const char *str)
+{
+	return str[0] == '.' && is_dir_sep(str[1]);
+}
+
+static int starts_with_dot_dot_slash(const char *str)
+{
+	return str[0] == '.' && str[1] == '.' && is_dir_sep(str[2]);
+}
+
+static char *last_dir_separator(char *str)
+{
+	char* p = str + strlen(str);
+	while (p-- != str)
+		if (is_dir_sep(*p))
+			return p;
+	return NULL;
+}
+
+/*
+ * The `url` argument is the URL that navigates to the submodule origin
+ * repo. When relative, this URL is relative to the superproject origin
+ * URL repo. The `up_path` argument, if specified, is the relative
+ * path that navigates from the submodule working tree to the superproject
+ * working tree. Returns the origin URL of the submodule.
+ *
+ * Return either an absolute URL or filesystem path (if the superproject
+ * origin URL is an absolute URL or filesystem path, respectively) or a
+ * relative file system path (if the superproject origin URL is a relative
+ * file system path).
+ *
+ * When the output is a relative file system path, the path is either
+ * relative to the submodule working tree, if up_path is specified, or to
+ * the superproject working tree otherwise.
+ */
+static char *relative_url(const char *remote_url,
+				const char *url,
+				const char *up_path)
+{
+	int is_relative = 0;
+	int colonsep = 0;
+	char *out;
+	char *remoteurl = xstrdup(remote_url);
+	struct strbuf sb = STRBUF_INIT;
+	size_t len;
+
+	len = strlen(remoteurl);
+	if (is_dir_sep(remoteurl[len]))
+		remoteurl[len] = '\0';
+
+	if (!url_is_local_not_ssh(remoteurl) || is_absolute_path(remoteurl))
+		is_relative = 0;
+	else {
+		is_relative = 1;
+
+		/* prepend a './' to ensure all relative remoteurls start with
+		 * './' or '../' */
+		if (!starts_with_dot_slash(remoteurl) &&
+		    !starts_with_dot_dot_slash(remoteurl)) {
+			strbuf_reset(&sb);
+			strbuf_addf(&sb, "./%s", remoteurl);
+			free(remoteurl);
+			remoteurl = strbuf_detach(&sb, NULL);
+		}
+	}
+	/* When the url starts with '../', remove that and the last directory
+	 * in remoteurl */
+	while (url) {
+		if (starts_with_dot_dot_slash(url)) {
+			char *rfind;
+			url += 3;
+
+			rfind = last_dir_separator(remoteurl);
+			if (rfind)
+				*rfind = '\0';
+			else {
+				rfind = strrchr(remoteurl, ':');
+				if (rfind) {
+					*rfind = '\0';
+					colonsep = 1;
+				} else {
+					if (is_relative || !strcmp(".", remoteurl))
+						die(N_("cannot strip one component off url '%s'"), remoteurl);
+					else
+						remoteurl = ".";
+				}
+			}
+		} else if (starts_with_dot_slash(url)) {
+			url += 2;
+		} else
+			break;
+	}
+	strbuf_reset(&sb);
+	strbuf_addf(&sb, "%s%s%s", remoteurl, colonsep ? ":" : "/", url);
+
+	if (starts_with_dot_slash(sb.buf))
+		out = xstrdup(sb.buf + 2);
+	else
+		out = xstrdup(sb.buf);
+	strbuf_reset(&sb);
+
+	free(remoteurl);
+	if (!up_path || !is_relative)
+		return out;
+	else {
+		strbuf_addf(&sb, "%s%s", up_path, out);
+
+		free(out);
+		return strbuf_detach(&sb, NULL);
+	}
+}
+
+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("BUG: 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);
+	printf("%s\n", res);
+
+	free(res);
+	return 0;
+}
+
+static int resolve_relative_url_test(int argc, const char **argv, const char *prefix)
+{
+	char *remoteurl, *res;
+	const char *up_path, *url;
+
+	if (argc != 4)
+		die("BUG: resolve_relative_url only accepts three arguments: <up_path> <remoteurl> <url>");
+
+	up_path = argv[1];
+	remoteurl = xstrdup(argv[2]);
+	url = argv[3];
+
+	if (!strcmp(up_path, "(null)"))
+		up_path = NULL;
+
+	res = relative_url(remoteurl, url, up_path);
+	printf("%s\n", res);
+
+	free(res);
+	return 0;
+}
 
 struct module_list {
 	const struct cache_entry **entries;
@@ -264,6 +453,8 @@ static struct cmd_struct commands[] = {
 	{"list", module_list},
 	{"name", module_name},
 	{"clone", module_clone},
+	{"resolve-relative-url", resolve_relative_url},
+	{"resolve-relative-url-test", resolve_relative_url_test},
 };
 
 int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
diff --git a/git-submodule.sh b/git-submodule.sh
index 9bc5c5f..3e409af 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -46,79 +46,6 @@ prefix=
 custom_name=
 depth=
 
-# The function takes at most 2 arguments. The first argument is the
-# URL that navigates to the submodule origin repo. When relative, this URL
-# is relative to the superproject origin URL repo. The second up_path
-# argument, if specified, is the relative path that navigates
-# from the submodule working tree to the superproject working tree.
-#
-# The output of the function is the origin URL of the submodule.
-#
-# The output will either be an absolute URL or filesystem path (if the
-# superproject origin URL is an absolute URL or filesystem path,
-# respectively) or a relative file system path (if the superproject
-# origin URL is a relative file system path).
-#
-# When the output is a relative file system path, the path is either
-# relative to the submodule working tree, if up_path is specified, or to
-# the superproject working tree otherwise.
-resolve_relative_url ()
-{
-	remote=$(get_default_remote)
-	remoteurl=$(git config "remote.$remote.url") ||
-		remoteurl=$(pwd) # the repository is its own authoritative upstream
-	url="$1"
-	remoteurl=${remoteurl%/}
-	sep=/
-	up_path="$2"
-
-	case "$remoteurl" in
-	*:*|/*)
-		is_relative=
-		;;
-	./*|../*)
-		is_relative=t
-		;;
-	*)
-		is_relative=t
-		remoteurl="./$remoteurl"
-		;;
-	esac
-
-	while test -n "$url"
-	do
-		case "$url" in
-		../*)
-			url="${url#../}"
-			case "$remoteurl" in
-			*/*)
-				remoteurl="${remoteurl%/*}"
-				;;
-			*:*)
-				remoteurl="${remoteurl%:*}"
-				sep=:
-				;;
-			*)
-				if test -z "$is_relative" || test "." = "$remoteurl"
-				then
-					die "$(eval_gettext "cannot strip one component off url '\$remoteurl'")"
-				else
-					remoteurl=.
-				fi
-				;;
-			esac
-			;;
-		./*)
-			url="${url#./}"
-			;;
-		*)
-			break;;
-		esac
-	done
-	remoteurl="$remoteurl$sep${url%/}"
-	echo "${is_relative:+${up_path}}${remoteurl#./}"
-}
-
 # Resolve a path to be relative to another path.  This is intended for
 # converting submodule paths when git-submodule is run in a subdirectory
 # and only handles paths where the directory separator is '/'.
@@ -281,7 +208,7 @@ cmd_add()
 		die "$(gettext "Relative path can only be used from the toplevel of the working tree")"
 
 		# dereference source url relative to parent's url
-		realrepo=$(resolve_relative_url "$repo") || exit
+		realrepo=$(git submodule--helper resolve-relative-url "$repo") || exit
 		;;
 	*:*|/*)
 		# absolute url
@@ -485,7 +412,7 @@ cmd_init()
 			# Possibly a url relative to parent
 			case "$url" in
 			./*|../*)
-				url=$(resolve_relative_url "$url") || exit
+				url=$(git submodule--helper resolve-relative-url "$url") || exit
 				;;
 			esac
 			git config submodule."$name".url "$url" ||
@@ -1190,9 +1117,9 @@ cmd_sync()
 			# guarantee a trailing /
 			up_path=${up_path%/}/ &&
 			# path from submodule work tree to submodule origin repo
-			sub_origin_url=$(resolve_relative_url "$url" "$up_path") &&
+			sub_origin_url=$(git submodule--helper resolve-relative-url "$url" "$up_path") &&
 			# path from superproject work tree to submodule origin repo
-			super_config_url=$(resolve_relative_url "$url") || exit
+			super_config_url=$(git submodule--helper resolve-relative-url "$url") || exit
 			;;
 		*)
 			sub_origin_url="$url"
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 627ef85..2ae1bbd 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -19,6 +19,12 @@ relative_path() {
 	"test \"\$(test-path-utils relative_path '$1' '$2')\" = '$expected'"
 }
 
+test_submodule_relative_url() {
+	expected="$4"
+	test_expect_success "test_submodule_relative_url: $1 $2 $3 => $4" \
+	"test \"\$(git submodule--helper resolve-relative-url-test '$1' '$2' '$3')\" = '$expected'"
+}
+
 test_git_path() {
 	test_expect_success "git-path $1 $2 => $3" "
 		$1 git rev-parse --git-path $2 >actual &&
@@ -286,4 +292,39 @@ test_git_path GIT_COMMON_DIR=bar config                   bar/config
 test_git_path GIT_COMMON_DIR=bar packed-refs              bar/packed-refs
 test_git_path GIT_COMMON_DIR=bar shallow                  bar/shallow
 
+test_submodule_relative_url "(null)" "../foo/bar" "../bar/a/b/c" "../foo/bar/a/b/c"
+test_submodule_relative_url "../../../" "../foo/bar" "../bar/a/b/c" "../../../../foo/bar/a/b/c"
+test_submodule_relative_url "(null)" "../foo/bar" "../submodule" "../foo/submodule"
+test_submodule_relative_url "../" "../foo/bar" "../submodule" "../../foo/submodule"
+test_submodule_relative_url "(null)" "../foo/submodule" "../submodule" "../foo/submodule"
+test_submodule_relative_url "../" "../foo/submodule" "../submodule" "../../foo/submodule"
+test_submodule_relative_url "(null)" "../foo" "../submodule" "../submodule"
+test_submodule_relative_url "../" "../foo" "../submodule" "../../submodule"
+test_submodule_relative_url "(null)" "./foo/bar" "../submodule" "foo/submodule"
+test_submodule_relative_url "../" "./foo/bar" "../submodule" "../foo/submodule"
+test_submodule_relative_url "(null)" "./foo" "../submodule" "submodule"
+test_submodule_relative_url "../" "./foo" "../submodule" "../submodule"
+test_submodule_relative_url "(null)" "//somewhere else/repo" "../subrepo" "//somewhere else/subrepo"
+test_submodule_relative_url "(null)" "/u//trash directory.t7406-submodule-update/subsuper_update_r" "../subsubsuper_update_r" "/u//trash directory.t7406-submodule-update/subsubsuper_update_r"
+test_submodule_relative_url "(null)" "/u//trash directory.t7406-submodule-update/super_update_r2" "../subsuper_update_r" "/u//trash directory.t7406-submodule-update/subsuper_update_r"
+test_submodule_relative_url "(null)" "/u/trash directory.t3600-rm/." "../." "/u/trash directory.t3600-rm/."
+test_submodule_relative_url "(null)" "/u/trash directory.t3600-rm" "./." "/u/trash directory.t3600-rm/."
+test_submodule_relative_url "(null)" "/u/trash directory.t7400-submodule-basic/addtest" "../repo" "/u/trash directory.t7400-submodule-basic/repo"
+test_submodule_relative_url "../" "/u/trash directory.t7400-submodule-basic/addtest" "../repo" "/u/trash directory.t7400-submodule-basic/repo"
+test_submodule_relative_url "(null)" "/u/trash directory.t7400-submodule-basic" "./å äö" "/u/trash directory.t7400-submodule-basic/å äö"
+test_submodule_relative_url "(null)" "/u/trash directory.t7403-submodule-sync/." "../submodule" "/u/trash directory.t7403-submodule-sync/submodule"
+test_submodule_relative_url "(null)" "/u/trash directory.t7407-submodule-foreach/submodule" "../submodule" "/u/trash directory.t7407-submodule-foreach/submodule"
+test_submodule_relative_url "(null)" "/u/trash directory.t7409-submodule-detached-worktree/home2/../remote" "../bundle1" "/u/trash directory.t7409-submodule-detached-worktree/home2/../bundle1"
+test_submodule_relative_url "(null)" "/u/trash directory.t7613-merge-submodule/submodule_update_repo" "./." "/u/trash directory.t7613-merge-submodule/submodule_update_repo/."
+test_submodule_relative_url "(null)" "file:///tmp/repo" "../subrepo" "file:///tmp/subrepo"
+test_submodule_relative_url "(null)" "foo/bar" "../submodule" "foo/submodule"
+test_submodule_relative_url "../" "foo/bar" "../submodule" "../foo/submodule"
+test_submodule_relative_url "(null)" "foo" "../submodule" "submodule"
+test_submodule_relative_url "../" "foo" "../submodule" "../submodule"
+test_submodule_relative_url "(null)" "helper:://hostname/repo" "../subrepo" "helper:://hostname/subrepo"
+test_submodule_relative_url "(null)" "ssh://hostname/repo" "../subrepo" "ssh://hostname/subrepo"
+test_submodule_relative_url "(null)" "ssh://hostname:22/repo" "../subrepo" "ssh://hostname:22/subrepo"
+test_submodule_relative_url "(null)" "user@host:path/to/repo" "../subrepo" "user@host:path/to/subrepo"
+test_submodule_relative_url "(null)" "user@host:repo" "../subrepo" "user@host:subrepo"
+
 test_done
-- 
2.7.0.9.g9d9d16f.dirty

^ permalink raw reply related	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2016-01-12 23:35 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-12-10  1:07 [PATCH] submodule: Port resolve_relative_url from shell to C Stefan Beller
2015-12-10  6:48 ` Johannes Sixt
2015-12-16 22:36   ` Stefan Beller
2015-12-17  0:26 ` [PATCHv2] Porting " Stefan Beller
2015-12-17  7:47   ` Torsten Bögershausen
2015-12-17  0:26 ` [PATCHv2] submodule: Port " Stefan Beller
2015-12-17  8:17   ` Eric Sunshine
2015-12-17 18:55   ` Johannes Sixt
2015-12-17 19:17     ` Johannes Sixt
2015-12-18  3:27     ` Jeff King
2016-01-11 20:17     ` Stefan Beller
  -- strict thread matches above, loose matches on Subject: below --
2016-01-12 23:35 Stefan Beller

Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).