[PATCH 1/2] submodule: port resolve_relative_url from shell to C
Later on we want to automatically call `git submodule init` from other commands, such that the users don't have to initialize the submodule themselves. 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 large part of that init functionality, so start by porting this function to C. To create the tests in t0060, the function `resolve_relative_url` was temporarily enhanced to write all inputs and output to disk when running the test suite. The added tests in this patch are a small selection thereof. Signed-off-by: Stefan BellerSigned-off-by: Junio C Hamano --- builtin/submodule--helper.c | 209 +++- git-submodule.sh| 81 + t/t0060-path-utils.sh | 46 ++ 3 files changed, 258 insertions(+), 78 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 864dd18..46946b0 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -9,6 +9,211 @@ #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]; + struct strbuf sb = STRBUF_INIT; + const char *refname = resolve_ref_unsafe("HEAD", 0, sha1, NULL); + + if (!refname) + die(_("No such ref: %s"), "HEAD"); + + /* detached HEAD */ + if (!strcmp(refname, "HEAD")) + return xstrdup("origin"); + + if (!skip_prefix(refname, "refs/heads/", )) + die(_("Expecting a full ref name, got %s"), refname); + + strbuf_addf(, "branch.%s.remote", refname); + if (git_config_get_string(sb.buf, )) + ret = xstrdup("origin"); + else + ret = dest; + + strbuf_release(); + 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]); +} + +/* + * Returns 1 if it was the last chop before ':'. + */ +static int chop_last_dir(char **remoteurl, int is_relative) +{ + char *rfind = find_last_dir_sep(*remoteurl); + if (rfind) { + *rfind = '\0'; + return 0; + } + + rfind = strrchr(*remoteurl, ':'); + if (rfind) { + *rfind = '\0'; + return 1; + } + + if (is_relative || !strcmp(".", *remoteurl)) + die(_("cannot strip one component off url '%s'"), + *remoteurl); + + free(*remoteurl); + *remoteurl = xstrdup("."); + return 0; +} + +/* + * 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. + * + * NEEDSWORK: This works incorrectly on the domain and protocol part. + * remote_url url outcome expectation + * http://a.com/b ../c http://a.com/c as is + * http://a.com/b ../../c http://c error out + * http://a.com/b ../../../c http:/c error out + * http://a.com/b ../../../../chttp:c error out + * http://a.com/b ../../../../../c.:c error out + * NEEDSWORK: Given how chop_last_dir() works, this function is broken + * when a local part has a colon in its path component, too. + */ +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 = 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 +*
Re: [PATCH 1/2] submodule: port resolve_relative_url from shell to C
On Thu, Apr 14, 2016 at 12:35 PM, Johannes Sixtwrote: > Am 14.04.2016 um 20:18 schrieb Stefan Beller: >> @@ -298,4 +305,40 @@ 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 "../" "../foo" "../submodule" "../../submodule" >> +test_submodule_relative_url "../" "../foo/bar" "../submodule" >> "../../foo/submodule" >> +test_submodule_relative_url "../" "../foo/submodule" "../submodule" >> "../../foo/submodule" >> +test_submodule_relative_url "../" "./foo" "../submodule" "../submodule" >> +test_submodule_relative_url "../" "./foo/bar" "../submodule" >> "../foo/submodule" >> +test_submodule_relative_url "../../../" "../foo/bar" "../sub/a/b/c" >> "../../../../foo/sub/a/b/c" >> +test_submodule_relative_url "../" "$PWD/addtest" "../repo" "$PWD/repo" >> +test_submodule_relative_url "../" "foo/bar" "../submodule" >> "../foo/submodule" >> +test_submodule_relative_url "../" "foo" "../submodule" "../submodule" >> + >> +test_submodule_relative_url "(null)" "../foo/bar" "../sub/a/b/c" >> "../foo/sub/a/b/c" >> +test_submodule_relative_url "(null)" "../foo/bar" "../submodule" >> "../foo/submodule" >> +test_submodule_relative_url "(null)" "../foo/submodule" "../submodule" >> "../foo/submodule" >> +test_submodule_relative_url "(null)" "../foo" "../submodule" "../submodule" >> +test_submodule_relative_url "(null)" "./foo/bar" "../submodule" >> "foo/submodule" >> +test_submodule_relative_url "(null)" "./foo" "../submodule" "submodule" >> +test_submodule_relative_url "(null)" "//somewhere else/repo" "../subrepo" >> "//somewhere else/subrepo" >> +test_submodule_relative_url "(null)" "$PWD/subsuper_update_r" >> "../subsubsuper_update_r" "$PWD/subsubsuper_update_r" >> +test_submodule_relative_url "(null)" "$PWD/super_update_r2" >> "../subsuper_update_r" "$PWD/subsuper_update_r" >> +test_submodule_relative_url "(null)" "$PWD/." "../." "$PWD/." >> +test_submodule_relative_url "(null)" "$PWD" "./." "$PWD/." >> +test_submodule_relative_url "(null)" "$PWD/addtest" "../repo" "$PWD/repo" >> +test_submodule_relative_url "(null)" "$PWD" "./å äö" "$PWD/å äö" >> +test_submodule_relative_url "(null)" "$PWD/." "../submodule" >> "$PWD/submodule" >> +test_submodule_relative_url "(null)" "$PWD/submodule" "../submodule" >> "$PWD/submodule" >> +test_submodule_relative_url "(null)" "$PWD/home2/../remote" "../bundle1" >> "$PWD/home2/../bundle1" >> +test_submodule_relative_url "(null)" "$PWD/submodule_update_repo" "./." >> "$PWD/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 "(null)" "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 >> > > I am very sorry that I am chiming in again so late. I forgot to mention > that this requires a fixup on Windows as below. I won't mind to submit > the fixup as a follow-on patch, but you could also squash it in if yet > another round is required. Thanks a lot for testing a patch on Windows! I'll pick this up in a resend. > > diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh > index 579c1fa..1d19fbb 100755 > --- a/t/t0060-path-utils.sh > +++ b/t/t0060-path-utils.sh > @@ -293,13 +293,16 @@ 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 > > +# In the tests below, the distinction between $PWD and $(pwd) is important: > +# on Windows, $PWD is POSIX style (/c/foo), $(pwd) has drive letter (c:/foo). > + > test_submodule_relative_url "../" "../foo" "../submodule" "../../submodule" > test_submodule_relative_url "../" "../foo/bar" "../submodule" > "../../foo/submodule" > test_submodule_relative_url "../" "../foo/submodule" "../submodule" > "../../foo/submodule" > test_submodule_relative_url "../" "./foo" "../submodule" "../submodule" > test_submodule_relative_url "../" "./foo/bar" "../submodule" > "../foo/submodule" > test_submodule_relative_url "../../../" "../foo/bar" "../sub/a/b/c" > "../../../../foo/sub/a/b/c" > -test_submodule_relative_url "../" "$PWD/addtest"
Re: [PATCH 1/2] submodule: port resolve_relative_url from shell to C
Am 14.04.2016 um 20:18 schrieb Stefan Beller: > @@ -298,4 +305,40 @@ 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 "../" "../foo" "../submodule" "../../submodule" > +test_submodule_relative_url "../" "../foo/bar" "../submodule" > "../../foo/submodule" > +test_submodule_relative_url "../" "../foo/submodule" "../submodule" > "../../foo/submodule" > +test_submodule_relative_url "../" "./foo" "../submodule" "../submodule" > +test_submodule_relative_url "../" "./foo/bar" "../submodule" > "../foo/submodule" > +test_submodule_relative_url "../../../" "../foo/bar" "../sub/a/b/c" > "../../../../foo/sub/a/b/c" > +test_submodule_relative_url "../" "$PWD/addtest" "../repo" "$PWD/repo" > +test_submodule_relative_url "../" "foo/bar" "../submodule" "../foo/submodule" > +test_submodule_relative_url "../" "foo" "../submodule" "../submodule" > + > +test_submodule_relative_url "(null)" "../foo/bar" "../sub/a/b/c" > "../foo/sub/a/b/c" > +test_submodule_relative_url "(null)" "../foo/bar" "../submodule" > "../foo/submodule" > +test_submodule_relative_url "(null)" "../foo/submodule" "../submodule" > "../foo/submodule" > +test_submodule_relative_url "(null)" "../foo" "../submodule" "../submodule" > +test_submodule_relative_url "(null)" "./foo/bar" "../submodule" > "foo/submodule" > +test_submodule_relative_url "(null)" "./foo" "../submodule" "submodule" > +test_submodule_relative_url "(null)" "//somewhere else/repo" "../subrepo" > "//somewhere else/subrepo" > +test_submodule_relative_url "(null)" "$PWD/subsuper_update_r" > "../subsubsuper_update_r" "$PWD/subsubsuper_update_r" > +test_submodule_relative_url "(null)" "$PWD/super_update_r2" > "../subsuper_update_r" "$PWD/subsuper_update_r" > +test_submodule_relative_url "(null)" "$PWD/." "../." "$PWD/." > +test_submodule_relative_url "(null)" "$PWD" "./." "$PWD/." > +test_submodule_relative_url "(null)" "$PWD/addtest" "../repo" "$PWD/repo" > +test_submodule_relative_url "(null)" "$PWD" "./å äö" "$PWD/å äö" > +test_submodule_relative_url "(null)" "$PWD/." "../submodule" "$PWD/submodule" > +test_submodule_relative_url "(null)" "$PWD/submodule" "../submodule" > "$PWD/submodule" > +test_submodule_relative_url "(null)" "$PWD/home2/../remote" "../bundle1" > "$PWD/home2/../bundle1" > +test_submodule_relative_url "(null)" "$PWD/submodule_update_repo" "./." > "$PWD/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 "(null)" "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 > I am very sorry that I am chiming in again so late. I forgot to mention that this requires a fixup on Windows as below. I won't mind to submit the fixup as a follow-on patch, but you could also squash it in if yet another round is required. diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh index 579c1fa..1d19fbb 100755 --- a/t/t0060-path-utils.sh +++ b/t/t0060-path-utils.sh @@ -293,13 +293,16 @@ 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 +# In the tests below, the distinction between $PWD and $(pwd) is important: +# on Windows, $PWD is POSIX style (/c/foo), $(pwd) has drive letter (c:/foo). + test_submodule_relative_url "../" "../foo" "../submodule" "../../submodule" test_submodule_relative_url "../" "../foo/bar" "../submodule" "../../foo/submodule" test_submodule_relative_url "../" "../foo/submodule" "../submodule" "../../foo/submodule" test_submodule_relative_url "../" "./foo" "../submodule" "../submodule" test_submodule_relative_url "../" "./foo/bar" "../submodule" "../foo/submodule" test_submodule_relative_url "../../../" "../foo/bar" "../sub/a/b/c" "../../../../foo/sub/a/b/c" -test_submodule_relative_url "../" "$PWD/addtest" "../repo" "$PWD/repo" +test_submodule_relative_url "../" "$PWD/addtest" "../repo" "$(pwd)/repo" test_submodule_relative_url "../" "foo/bar" "../submodule" "../foo/submodule" test_submodule_relative_url "../" "foo" "../submodule" "../submodule" @@ -310,16 +313,16 @@
[PATCH 1/2] submodule: port resolve_relative_url from shell to C
Later on we want to automatically call `git submodule init` from other commands, such that the users don't have to initialize the submodule themselves. 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 large part of that init functionality, so start by porting this function to C. To create the tests in t0060, the function `resolve_relative_url` was temporarily enhanced to write all inputs and output to disk when running the test suite. The added tests in this patch are a small selection thereof. Signed-off-by: Stefan BellerSigned-off-by: Junio C Hamano --- builtin/submodule--helper.c | 209 +++- git-submodule.sh| 81 + t/t0060-path-utils.sh | 43 + 3 files changed, 255 insertions(+), 78 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 864dd18..46946b0 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -9,6 +9,211 @@ #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]; + struct strbuf sb = STRBUF_INIT; + const char *refname = resolve_ref_unsafe("HEAD", 0, sha1, NULL); + + if (!refname) + die(_("No such ref: %s"), "HEAD"); + + /* detached HEAD */ + if (!strcmp(refname, "HEAD")) + return xstrdup("origin"); + + if (!skip_prefix(refname, "refs/heads/", )) + die(_("Expecting a full ref name, got %s"), refname); + + strbuf_addf(, "branch.%s.remote", refname); + if (git_config_get_string(sb.buf, )) + ret = xstrdup("origin"); + else + ret = dest; + + strbuf_release(); + 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]); +} + +/* + * Returns 1 if it was the last chop before ':'. + */ +static int chop_last_dir(char **remoteurl, int is_relative) +{ + char *rfind = find_last_dir_sep(*remoteurl); + if (rfind) { + *rfind = '\0'; + return 0; + } + + rfind = strrchr(*remoteurl, ':'); + if (rfind) { + *rfind = '\0'; + return 1; + } + + if (is_relative || !strcmp(".", *remoteurl)) + die(_("cannot strip one component off url '%s'"), + *remoteurl); + + free(*remoteurl); + *remoteurl = xstrdup("."); + return 0; +} + +/* + * 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. + * + * NEEDSWORK: This works incorrectly on the domain and protocol part. + * remote_url url outcome expectation + * http://a.com/b ../c http://a.com/c as is + * http://a.com/b ../../c http://c error out + * http://a.com/b ../../../c http:/c error out + * http://a.com/b ../../../../chttp:c error out + * http://a.com/b ../../../../../c.:c error out + * NEEDSWORK: Given how chop_last_dir() works, this function is broken + * when a local part has a colon in its path component, too. + */ +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 = 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 +*
[PATCH 1/2] submodule: port resolve_relative_url from shell to C
Later on we want to automatically call `git submodule init` from other commands, such that the users don't have to initialize the submodule themselves. 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 large part of that init functionality, so start by porting this function to C. To create the tests in t0060, the function `resolve_relative_url` was temporarily enhanced to write all inputs and output to disk when running the test suite. The added tests in this patch are a small selection thereof. Signed-off-by: Stefan BellerSigned-off-by: Junio C Hamano --- builtin/submodule--helper.c | 209 +++- git-submodule.sh| 81 + t/t0060-path-utils.sh | 43 + 3 files changed, 255 insertions(+), 78 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index a484945..2ab3662 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -9,6 +9,211 @@ #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]; + struct strbuf sb = STRBUF_INIT; + const char *refname = resolve_ref_unsafe("HEAD", 0, sha1, NULL); + + if (!refname) + die(_("No such ref: %s"), "HEAD"); + + /* detached HEAD */ + if (!strcmp(refname, "HEAD")) + return xstrdup("origin"); + + if (!skip_prefix(refname, "refs/heads/", )) + die(_("Expecting a full ref name, got %s"), refname); + + strbuf_addf(, "branch.%s.remote", refname); + if (git_config_get_string(sb.buf, )) + ret = xstrdup("origin"); + else + ret = dest; + + strbuf_release(); + 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]); +} + +/* + * Returns 1 if it was the last chop before ':'. + */ +static int chop_last_dir(char **remoteurl, int is_relative) +{ + char *rfind = find_last_dir_sep(*remoteurl); + if (rfind) { + *rfind = '\0'; + return 0; + } + + rfind = strrchr(*remoteurl, ':'); + if (rfind) { + *rfind = '\0'; + return 1; + } + + if (is_relative || !strcmp(".", *remoteurl)) + die(_("cannot strip one component off url '%s'"), + *remoteurl); + + free(*remoteurl); + *remoteurl = xstrdup("."); + return 0; +} + +/* + * 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. + * + * NEEDSWORK: This works incorrectly on the domain and protocol part. + * remote_url url outcome expectation + * http://a.com/b ../c http://a.com/c as is + * http://a.com/b ../../c http://c error out + * http://a.com/b ../../../c http:/c error out + * http://a.com/b ../../../../chttp:c error out + * http://a.com/b ../../../../../c.:c error out + * NEEDSWORK: Given how chop_last_dir() works, this function is broken + * when a local part has a colon in its path component, too. + */ +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 = 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 +*
[PATCH 1/2] submodule: port resolve_relative_url from shell to C
Later on we want to automatically call `git submodule init` from other commands, such that the users don't have to initialize the submodule themselves. 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 large part of that init functionality, so start by porting this function to C. To create the tests in t0060, the function `resolve_relative_url` was temporarily enhanced to write all inputs and output to disk when running the test suite. The added tests in this patch are a small selection thereof. Signed-off-by: Stefan BellerSigned-off-by: Junio C Hamano --- builtin/submodule--helper.c | 210 +++- git-submodule.sh| 81 + t/t0060-path-utils.sh | 43 + 3 files changed, 256 insertions(+), 78 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index a484945..bc7cf87 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -9,6 +9,212 @@ #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 = 0; + struct strbuf sb = STRBUF_INIT; + const char *refname = resolve_ref_unsafe("HEAD", 0, sha1, ); + + if (!refname) + die(_("No such ref: %s"), "HEAD"); + + /* detached HEAD */ + if (!strcmp(refname, "HEAD")) + return xstrdup("origin"); + + if (!skip_prefix(refname, "refs/heads/", )) + die(_("Expecting a full ref name, got %s"), refname); + + strbuf_addf(, "branch.%s.remote", refname); + if (git_config_get_string(sb.buf, )) + ret = xstrdup("origin"); + else + ret = dest; + + strbuf_release(); + 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]); +} + +/* + * Returns 1 if it was the last chop before ':'. + */ +static int chop_last_dir(char **remoteurl, int is_relative) +{ + char *rfind = find_last_dir_sep(*remoteurl); + if (rfind) { + *rfind = '\0'; + return 0; + } + + rfind = strrchr(*remoteurl, ':'); + if (rfind) { + *rfind = '\0'; + return 1; + } + + if (is_relative || !strcmp(".", *remoteurl)) + die(_("cannot strip one component off url '%s'"), + *remoteurl); + + free(*remoteurl); + *remoteurl = xstrdup("."); + return 0; +} + +/* + * 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. + * + * NEEDSWORK: This works incorrectly on the domain and protocol part. + * remote_url url outcome expectation + * http://a.com/b ../c http://a.com/c as is + * http://a.com/b ../../c http://c error out + * http://a.com/b ../../../c http:/c error out + * http://a.com/b ../../../../chttp:c error out + * http://a.com/b ../../../../../c.:c error out + * NEEDSWORK: Given how chop_last_dir() works, this function is broken + * when a local part has a colon in its path component, too. + */ +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 = 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 +
Re: [PATCH 1/2] submodule: port resolve_relative_url from shell to C
On Wed, Mar 2, 2016 at 9:21 AM, Johannes Sixtwrote: > Am 13.02.2016 um 00:39 schrieb Stefan Beller: >> @@ -289,4 +296,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" "../sub/a/b/c" >> "../foo/sub/a/b/c" >> +test_submodule_relative_url "../../../" "../foo/bar" "../sub/a/b/c" >> "../../../../foo/sub/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/." > > The tests with absolute paths all fail on Windows. The reason is that > git.exe sees mangled paths and 'git submodule--helper > resolve-relative-url-test' produces mangled paths (that begins with a > drive letter), whereas the test script expects POSIX paths. The pattern > I currently use to fix this is > > test_submodule_relative_url "(null)" "$PWD/addtest" "../repo" "$(pwd)/repo" > > (In our test scripts, $PWD is a POSIX style path and $(pwd) is a > Windows style path). > > With this change, the penultimate case above still fails because the > 'home2/..' gets lost somewhere in the actual output, which I still have > to debug. > > The two cases beginning with '/u//' cannot be tested on Windows. > Are they important? Are the doubled slashes intentional? The way I got the test cases is by inserting a debug printf into the shell script and then running the test suite (as that ought to work correct and cover everything). And the double slash is intentional, somewhere in the test suite we have a case where that occurs. It may be a calling error? Thanks for looking into this on the Windows side :) Thanks, Stefan > >> +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"
Re: [PATCH 1/2] submodule: port resolve_relative_url from shell to C
Am 13.02.2016 um 00:39 schrieb Stefan Beller: > @@ -289,4 +296,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" "../sub/a/b/c" > "../foo/sub/a/b/c" > +test_submodule_relative_url "../../../" "../foo/bar" "../sub/a/b/c" > "../../../../foo/sub/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/." The tests with absolute paths all fail on Windows. The reason is that git.exe sees mangled paths and 'git submodule--helper resolve-relative-url-test' produces mangled paths (that begins with a drive letter), whereas the test script expects POSIX paths. The pattern I currently use to fix this is test_submodule_relative_url "(null)" "$PWD/addtest" "../repo" "$(pwd)/repo" (In our test scripts, $PWD is a POSIX style path and $(pwd) is a Windows style path). With this change, the penultimate case above still fails because the 'home2/..' gets lost somewhere in the actual output, which I still have to debug. The two cases beginning with '/u//' cannot be tested on Windows. Are they important? Are the doubled slashes intentional? > +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 > -- To unsubscribe from this list: send the line "unsubscribe git" in the
Re: [PATCH 1/2] submodule: port resolve_relative_url from shell to C
On Fri, Feb 12, 2016 at 03:39:15PM -0800, Stefan Beller wrote: > Later on we want to automatically call `git submodule init` from > other commands, such that the users don't have to initialize the > submodule themselves. 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 large part of that init > functionality, so start by porting this function to C. > > To create the tests in t0060, the function `resolve_relative_url` > was temporarily enhanced to write all inputs and output to disk > when running the test suite. The added tests in this patch are > a small selection thereof. > > Signed-off-by: Stefan Beller> Signed-off-by: Junio C Hamano > --- > builtin/submodule--helper.c | 208 > +++- i18n fixes. Can you squash this patch in when it's re-rolled? -- 8< -- diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index f4a0fd7..a6e54fa 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -22,7 +22,7 @@ static char *get_default_remote(void) const char *refname = resolve_ref_unsafe("HEAD", 0, sha1, ); if (!refname) - die("No such ref: HEAD"); + die(_("No such ref: %s"), "HEAD"); /* detached HEAD */ if (!strcmp(refname, "HEAD")) -- 8< -- -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 1/2] submodule: port resolve_relative_url from shell to C
Stefan Bellerwrites: > +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]); > +} > + > +/* > + * Returns 1 if it was the last chop before ':'. > + */ > +static int chop_last_dir(char **remoteurl, int is_relative) > +{ > + char *rfind = find_last_dir_sep(*remoteurl); > + if (rfind) { > + *rfind = '\0'; > + return 0; > + } > + > + rfind = strrchr(*remoteurl, ':'); > + if (rfind) { > + *rfind = '\0'; > + return 1; > + } > + > + if (is_relative || !strcmp(".", *remoteurl)) > + die(_("cannot strip one component off url '%s'"), > + *remoteurl); > + > + free(*remoteurl); > + *remoteurl = xstrdup("."); > + return 0; > +} > + > +/* > + * 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. > + * > + * NEEDSWORK: This works incorrectly on the domain and protocol part. > + * remote_url url outcome correct > + * http://a.com/b ../c http://a.com/c yes > + * http://a.com/b ../../c http://c no (domain should be > kept) > + * http://a.com/b ../../../c http:/c no > + * http://a.com/b ../../../../chttp:c no > + * http://a.com/b ../../../../../c.:c no > + */ Not just "no" but we should say what the expected outcome is. I think all of them should error out? Given how chop_last_dir() works, I think this function is broken when a local part has a colon in its path component, too. I do not think the scripted version did any better on such a URL; just another thing that NEEDSWORK comment should list as a thing to be fixed in the future. -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 1/2] submodule: port resolve_relative_url from shell to C
Later on we want to automatically call `git submodule init` from other commands, such that the users don't have to initialize the submodule themselves. 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 large part of that init functionality, so start by porting this function to C. To create the tests in t0060, the function `resolve_relative_url` was temporarily enhanced to write all inputs and output to disk when running the test suite. The added tests in this patch are a small selection thereof. Signed-off-by: Stefan BellerSigned-off-by: Junio C Hamano --- builtin/submodule--helper.c | 208 +++- git-submodule.sh| 81 + t/t0060-path-utils.sh | 42 + 3 files changed, 253 insertions(+), 78 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 65bdc14..d1e9118 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -9,6 +9,210 @@ #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 = 0; + struct strbuf sb = STRBUF_INIT; + const char *refname = resolve_ref_unsafe("HEAD", 0, sha1, ); + + if (!refname) + die("No such ref: HEAD"); + + /* detached HEAD */ + if (!strcmp(refname, "HEAD")) + return xstrdup("origin"); + + if (!skip_prefix(refname, "refs/heads/", )) + die(_("Expecting a full ref name, got %s"), refname); + + strbuf_addf(, "branch.%s.remote", refname); + if (git_config_get_string(sb.buf, )) + ret = xstrdup("origin"); + else + ret = dest; + + strbuf_release(); + 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]); +} + +/* + * Returns 1 if it was the last chop before ':'. + */ +static int chop_last_dir(char **remoteurl, int is_relative) +{ + char *rfind = find_last_dir_sep(*remoteurl); + if (rfind) { + *rfind = '\0'; + return 0; + } + + rfind = strrchr(*remoteurl, ':'); + if (rfind) { + *rfind = '\0'; + return 1; + } + + if (is_relative || !strcmp(".", *remoteurl)) + die(_("cannot strip one component off url '%s'"), + *remoteurl); + + free(*remoteurl); + *remoteurl = xstrdup("."); + return 0; +} + +/* + * 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. + * + * NEEDSWORK: This works incorrectly on the domain and protocol part. + * remote_url url outcome correct + * http://a.com/b ../c http://a.com/c yes + * http://a.com/b ../../c http://c no (domain should be kept) + * http://a.com/b ../../../c http:/c no + * http://a.com/b ../../../../chttp:c no + * http://a.com/b ../../../../../c.:c no + */ +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 = 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) && +