On Wed, Dec 9, 2015 at 10:48 PM, Johannes Sixt <j6t@xxxxxxxx> 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@xxxxxxxxxx> >> --- >> >> 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" >> > -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html