{"thread":{"id":"41405","subject":"[PATCH 0/2] Port `git submodule init` from shell to C","startedAt":"2016-02-12T23:39:14Z","lastAt":"2016-03-02T17:34:12Z","messageCount":9,"participants":["Stefan Beller","Junio C Hamano","Duy Nguyen","Johannes Sixt"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"278050","messageId":"1455320356-15778-1-git-send-email-sbeller@google.com","threadId":"41405","inReplyTo":null,"subject":"[PATCH 0/2] Port `git submodule init` from shell to C","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-02-12T23:39:14Z","receivedAt":"2016-02-12T23:39:14Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"This applies on top of the just sent series which is going to replace\nsb/submodule-parallel-update, replacing sb/submodule-init.\n\n* Using enums for the submodule update strategy now.\n\nThanks,\nStefan\n\nStefan Beller (2):\n  submodule: port resolve_relative_url from shell to C\n  submodule: port init from shell to C\n\n builtin/submodule--helper.c | 315 +++++++++++++++++++++++++++++++++++++++++++-\n git-submodule.sh            | 118 +----------------\n submodule.c                 |  21 +++\n submodule.h                 |   1 +\n t/t0060-path-utils.sh       |  42 ++++++\n 5 files changed, 382 insertions(+), 115 deletions(-)\n\n-- \n2.7.1.292.g18a4ced.dirty\n"},{"id":"278052","messageId":"1455320356-15778-2-git-send-email-sbeller@google.com","threadId":"41405","inReplyTo":"1455320356-15778-1-git-send-email-sbeller@google.com","subject":"[PATCH 1/2] submodule: port resolve_relative_url from shell to C","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-02-12T23:39:15Z","receivedAt":"2016-02-12T23:39:15Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"Later on we want to automatically call `git submodule init` from\nother commands, such that the users don't have to initialize the\nsubmodule themselves.  As these other commands are written in C\nalready, we'd need the init functionality in C, too.  The\n`resolve_relative_url` function is a large part of that init\nfunctionality, so start by porting this function to C.\n\nTo create the tests in t0060, the function `resolve_relative_url`\nwas temporarily enhanced to write all inputs and output to disk\nwhen running the test suite. The added tests in this patch are\na small selection thereof.\n\nSigned-off-by: Stefan Beller <sbeller@google.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin/submodule--helper.c | 208 +++++++++++++++++++++++++++++++++++++++++++-\n git-submodule.sh            |  81 +----------------\n t/t0060-path-utils.sh       |  42 +++++++++\n 3 files changed, 253 insertions(+), 78 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 65bdc14..d1e9118 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -9,6 +9,210 @@\n #include \"submodule-config.h\"\n #include \"string-list.h\"\n #include \"run-command.h\"\n+#include \"remote.h\"\n+#include \"refs.h\"\n+#include \"connect.h\"\n+\n+static char *get_default_remote(void)\n+{\n+\tchar *dest = NULL, *ret;\n+\tunsigned char sha1[20];\n+\tint flag = 0;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tconst char *refname = resolve_ref_unsafe(\"HEAD\", 0, sha1, &flag);\n+\n+\tif (!refname)\n+\t\tdie(\"No such ref: HEAD\");\n+\n+\t/* detached HEAD */\n+\tif (!strcmp(refname, \"HEAD\"))\n+\t\treturn xstrdup(\"origin\");\n+\n+\tif (!skip_prefix(refname, \"refs/heads/\", &refname))\n+\t\tdie(_(\"Expecting a full ref name, got %s\"), refname);\n+\n+\tstrbuf_addf(&sb, \"branch.%s.remote\", refname);\n+\tif (git_config_get_string(sb.buf, &dest))\n+\t\tret = xstrdup(\"origin\");\n+\telse\n+\t\tret = dest;\n+\n+\tstrbuf_release(&sb);\n+\treturn ret;\n+}\n+\n+static int starts_with_dot_slash(const char *str)\n+{\n+\treturn str[0] == '.' && is_dir_sep(str[1]);\n+}\n+\n+static int starts_with_dot_dot_slash(const char *str)\n+{\n+\treturn str[0] == '.' && str[1] == '.' && is_dir_sep(str[2]);\n+}\n+\n+/*\n+ * Returns 1 if it was the last chop before ':'.\n+ */\n+static int chop_last_dir(char **remoteurl, int is_relative)\n+{\n+\tchar *rfind = find_last_dir_sep(*remoteurl);\n+\tif (rfind) {\n+\t\t*rfind = '\\0';\n+\t\treturn 0;\n+\t}\n+\n+\trfind = strrchr(*remoteurl, ':');\n+\tif (rfind) {\n+\t\t*rfind = '\\0';\n+\t\treturn 1;\n+\t}\n+\n+\tif (is_relative || !strcmp(\".\", *remoteurl))\n+\t\tdie(_(\"cannot strip one component off url '%s'\"),\n+\t\t\t*remoteurl);\n+\n+\tfree(*remoteurl);\n+\t*remoteurl = xstrdup(\".\");\n+\treturn 0;\n+}\n+\n+/*\n+ * The `url` argument is the URL that navigates to the submodule origin\n+ * repo. When relative, this URL is relative to the superproject origin\n+ * URL repo. The `up_path` argument, if specified, is the relative\n+ * path that navigates from the submodule working tree to the superproject\n+ * working tree. Returns the origin URL of the submodule.\n+ *\n+ * Return either an absolute URL or filesystem path (if the superproject\n+ * origin URL is an absolute URL or filesystem path, respectively) or a\n+ * relative file system path (if the superproject origin URL is a relative\n+ * file system path).\n+ *\n+ * When the output is a relative file system path, the path is either\n+ * relative to the submodule working tree, if up_path is specified, or to\n+ * the superproject working tree otherwise.\n+ *\n+ * NEEDSWORK: This works incorrectly on the domain and protocol part.\n+ * remote_url      url              outcome          correct\n+ * http://a.com/b  ../c             http://a.com/c   yes\n+ * http://a.com/b  ../../c          http://c         no (domain should be kept)\n+ * http://a.com/b  ../../../c       http:/c          no\n+ * http://a.com/b  ../../../../c    http:c           no\n+ * http://a.com/b  ../../../../../c    .:c           no\n+ */\n+static char *relative_url(const char *remote_url,\n+\t\t\t\tconst char *url,\n+\t\t\t\tconst char *up_path)\n+{\n+\tint is_relative = 0;\n+\tint colonsep = 0;\n+\tchar *out;\n+\tchar *remoteurl = xstrdup(remote_url);\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tsize_t len = strlen(remoteurl);\n+\n+\tif (is_dir_sep(remoteurl[len]))\n+\t\tremoteurl[len] = '\\0';\n+\n+\tif (!url_is_local_not_ssh(remoteurl) || is_absolute_path(remoteurl))\n+\t\tis_relative = 0;\n+\telse {\n+\t\tis_relative = 1;\n+\t\t/*\n+\t\t * Prepend a './' to ensure all relative\n+\t\t * remoteurls start with './' or '../'\n+\t\t */\n+\t\tif (!starts_with_dot_slash(remoteurl) &&\n+\t\t    !starts_with_dot_dot_slash(remoteurl)) {\n+\t\t\tstrbuf_reset(&sb);\n+\t\t\tstrbuf_addf(&sb, \"./%s\", remoteurl);\n+\t\t\tfree(remoteurl);\n+\t\t\tremoteurl = strbuf_detach(&sb, NULL);\n+\t\t}\n+\t}\n+\t/*\n+\t * When the url starts with '../', remove that and the\n+\t * last directory in remoteurl.\n+\t */\n+\twhile (url) {\n+\t\tif (starts_with_dot_dot_slash(url)) {\n+\t\t\turl += 3;\n+\t\t\tcolonsep |= chop_last_dir(&remoteurl, is_relative);\n+\t\t} else if (starts_with_dot_slash(url))\n+\t\t\turl += 2;\n+\t\telse\n+\t\t\tbreak;\n+\t}\n+\tstrbuf_reset(&sb);\n+\tstrbuf_addf(&sb, \"%s%s%s\", remoteurl, colonsep ? \":\" : \"/\", url);\n+\n+\tif (starts_with_dot_slash(sb.buf))\n+\t\tout = xstrdup(sb.buf + 2);\n+\telse\n+\t\tout = xstrdup(sb.buf);\n+\tstrbuf_reset(&sb);\n+\n+\tfree(remoteurl);\n+\tif (!up_path || !is_relative)\n+\t\treturn out;\n+\n+\tstrbuf_addf(&sb, \"%s%s\", up_path, out);\n+\tfree(out);\n+\treturn strbuf_detach(&sb, NULL);\n+}\n+\n+static int resolve_relative_url(int argc, const char **argv, const char *prefix)\n+{\n+\tchar *remoteurl = NULL;\n+\tchar *remote = get_default_remote();\n+\tconst char *up_path = NULL;\n+\tchar *res;\n+\tconst char *url;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\n+\tif (argc != 2 && argc != 3)\n+\t\tdie(\"resolve-relative-url only accepts one or two arguments\");\n+\n+\turl = argv[1];\n+\tstrbuf_addf(&sb, \"remote.%s.url\", remote);\n+\tfree(remote);\n+\n+\tif (git_config_get_string(sb.buf, &remoteurl))\n+\t\t/* the repository is its own authoritative upstream */\n+\t\tremoteurl = xgetcwd();\n+\n+\tif (argc == 3)\n+\t\tup_path = argv[2];\n+\n+\tres = relative_url(remoteurl, url, up_path);\n+\tputs(res);\n+\tfree(res);\n+\tfree(remoteurl);\n+\treturn 0;\n+}\n+\n+static int resolve_relative_url_test(int argc, const char **argv, const char *prefix)\n+{\n+\tchar *remoteurl, *res;\n+\tconst char *up_path, *url;\n+\n+\tif (argc != 4)\n+\t\tdie(\"resolve-relative-url-test only accepts three arguments: <up_path> <remoteurl> <url>\");\n+\n+\tup_path = argv[1];\n+\tremoteurl = xstrdup(argv[2]);\n+\turl = argv[3];\n+\n+\tif (!strcmp(up_path, \"(null)\"))\n+\t\tup_path = NULL;\n+\n+\tres = relative_url(remoteurl, url, up_path);\n+\tputs(res);\n+\tfree(res);\n+\tfree(remoteurl);\n+\treturn 0;\n+}\n \n struct module_list {\n \tconst struct cache_entry **entries;\n@@ -502,7 +706,9 @@ static struct cmd_struct commands[] = {\n \t{\"list\", module_list},\n \t{\"name\", module_name},\n \t{\"clone\", module_clone},\n-\t{\"update-clone\", update_clone}\n+\t{\"update-clone\", update_clone},\n+\t{\"resolve-relative-url\", resolve_relative_url},\n+\t{\"resolve-relative-url-test\", resolve_relative_url_test},\n };\n \n int cmd_submodule__helper(int argc, const char **argv, const char *prefix)\ndiff --git a/git-submodule.sh b/git-submodule.sh\nindex 10c5af9..615ef9b 100755\n--- a/git-submodule.sh\n+++ b/git-submodule.sh\n@@ -46,79 +46,6 @@ prefix=\n custom_name=\n depth=\n \n-# The function takes at most 2 arguments. The first argument is the\n-# URL that navigates to the submodule origin repo. When relative, this URL\n-# is relative to the superproject origin URL repo. The second up_path\n-# argument, if specified, is the relative path that navigates\n-# from the submodule working tree to the superproject working tree.\n-#\n-# The output of the function is the origin URL of the submodule.\n-#\n-# The output will either be an absolute URL or filesystem path (if the\n-# superproject origin URL is an absolute URL or filesystem path,\n-# respectively) or a relative file system path (if the superproject\n-# origin URL is a relative file system path).\n-#\n-# When the output is a relative file system path, the path is either\n-# relative to the submodule working tree, if up_path is specified, or to\n-# the superproject working tree otherwise.\n-resolve_relative_url ()\n-{\n-\tremote=$(get_default_remote)\n-\tremoteurl=$(git config \"remote.$remote.url\") ||\n-\t\tremoteurl=$(pwd) # the repository is its own authoritative upstream\n-\turl=\"$1\"\n-\tremoteurl=${remoteurl%/}\n-\tsep=/\n-\tup_path=\"$2\"\n-\n-\tcase \"$remoteurl\" in\n-\t*:*|/*)\n-\t\tis_relative=\n-\t\t;;\n-\t./*|../*)\n-\t\tis_relative=t\n-\t\t;;\n-\t*)\n-\t\tis_relative=t\n-\t\tremoteurl=\"./$remoteurl\"\n-\t\t;;\n-\tesac\n-\n-\twhile test -n \"$url\"\n-\tdo\n-\t\tcase \"$url\" in\n-\t\t../*)\n-\t\t\turl=\"${url#../}\"\n-\t\t\tcase \"$remoteurl\" in\n-\t\t\t*/*)\n-\t\t\t\tremoteurl=\"${remoteurl%/*}\"\n-\t\t\t\t;;\n-\t\t\t*:*)\n-\t\t\t\tremoteurl=\"${remoteurl%:*}\"\n-\t\t\t\tsep=:\n-\t\t\t\t;;\n-\t\t\t*)\n-\t\t\t\tif test -z \"$is_relative\" || test \".\" = \"$remoteurl\"\n-\t\t\t\tthen\n-\t\t\t\t\tdie \"$(eval_gettext \"cannot strip one component off url '\\$remoteurl'\")\"\n-\t\t\t\telse\n-\t\t\t\t\tremoteurl=.\n-\t\t\t\tfi\n-\t\t\t\t;;\n-\t\t\tesac\n-\t\t\t;;\n-\t\t./*)\n-\t\t\turl=\"${url#./}\"\n-\t\t\t;;\n-\t\t*)\n-\t\t\tbreak;;\n-\t\tesac\n-\tdone\n-\tremoteurl=\"$remoteurl$sep${url%/}\"\n-\techo \"${is_relative:+${up_path}}${remoteurl#./}\"\n-}\n-\n # Resolve a path to be relative to another path.  This is intended for\n # converting submodule paths when git-submodule is run in a subdirectory\n # and only handles paths where the directory separator is '/'.\n@@ -281,7 +208,7 @@ cmd_add()\n \t\tdie \"$(gettext \"Relative path can only be used from the toplevel of the working tree\")\"\n \n \t\t# dereference source url relative to parent's url\n-\t\trealrepo=$(resolve_relative_url \"$repo\") || exit\n+\t\trealrepo=$(git submodule--helper resolve-relative-url \"$repo\") || exit\n \t\t;;\n \t*:*|/*)\n \t\t# absolute url\n@@ -485,7 +412,7 @@ cmd_init()\n \t\t\t# Possibly a url relative to parent\n \t\t\tcase \"$url\" in\n \t\t\t./*|../*)\n-\t\t\t\turl=$(resolve_relative_url \"$url\") || exit\n+\t\t\t\turl=$(git submodule--helper resolve-relative-url \"$url\") || exit\n \t\t\t\t;;\n \t\t\tesac\n \t\t\tgit config submodule.\"$name\".url \"$url\" ||\n@@ -1176,9 +1103,9 @@ cmd_sync()\n \t\t\t# guarantee a trailing /\n \t\t\tup_path=${up_path%/}/ &&\n \t\t\t# path from submodule work tree to submodule origin repo\n-\t\t\tsub_origin_url=$(resolve_relative_url \"$url\" \"$up_path\") &&\n+\t\t\tsub_origin_url=$(git submodule--helper resolve-relative-url \"$url\" \"$up_path\") &&\n \t\t\t# path from superproject work tree to submodule origin repo\n-\t\t\tsuper_config_url=$(resolve_relative_url \"$url\") || exit\n+\t\t\tsuper_config_url=$(git submodule--helper resolve-relative-url \"$url\") || exit\n \t\t\t;;\n \t\t*)\n \t\t\tsub_origin_url=\"$url\"\ndiff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh\nindex f0152a7..0d2176b 100755\n--- a/t/t0060-path-utils.sh\n+++ b/t/t0060-path-utils.sh\n@@ -19,6 +19,13 @@ relative_path() {\n \t\"test \\\"\\$(test-path-utils relative_path '$1' '$2')\\\" = '$expected'\"\n }\n \n+test_submodule_relative_url() {\n+\ttest_expect_success \"test_submodule_relative_url: $1 $2 $3 => $4\" \"\n+\t\tactual=\\$(git submodule--helper resolve-relative-url-test '$1' '$2' '$3') &&\n+\t\ttest \\\"\\$actual\\\" = '$4'\n+\t\"\n+}\n+\n test_git_path() {\n \ttest_expect_success \"git-path $1 $2 => $3\" \"\n \t\t$1 git rev-parse --git-path $2 >actual &&\n@@ -289,4 +296,39 @@ test_git_path GIT_COMMON_DIR=bar config                   bar/config\n test_git_path GIT_COMMON_DIR=bar packed-refs              bar/packed-refs\n test_git_path GIT_COMMON_DIR=bar shallow                  bar/shallow\n \n+test_submodule_relative_url \"(null)\" \"../foo/bar\" \"../sub/a/b/c\" \"../foo/sub/a/b/c\"\n+test_submodule_relative_url \"../../../\" \"../foo/bar\" \"../sub/a/b/c\" \"../../../../foo/sub/a/b/c\"\n+test_submodule_relative_url \"(null)\" \"../foo/bar\" \"../submodule\" \"../foo/submodule\"\n+test_submodule_relative_url \"../\" \"../foo/bar\" \"../submodule\" \"../../foo/submodule\"\n+test_submodule_relative_url \"(null)\" \"../foo/submodule\" \"../submodule\" \"../foo/submodule\"\n+test_submodule_relative_url \"../\" \"../foo/submodule\" \"../submodule\" \"../../foo/submodule\"\n+test_submodule_relative_url \"(null)\" \"../foo\" \"../submodule\" \"../submodule\"\n+test_submodule_relative_url \"../\" \"../foo\" \"../submodule\" \"../../submodule\"\n+test_submodule_relative_url \"(null)\" \"./foo/bar\" \"../submodule\" \"foo/submodule\"\n+test_submodule_relative_url \"../\" \"./foo/bar\" \"../submodule\" \"../foo/submodule\"\n+test_submodule_relative_url \"(null)\" \"./foo\" \"../submodule\" \"submodule\"\n+test_submodule_relative_url \"../\" \"./foo\" \"../submodule\" \"../submodule\"\n+test_submodule_relative_url \"(null)\" \"//somewhere else/repo\" \"../subrepo\" \"//somewhere else/subrepo\"\n+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\"\n+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\"\n+test_submodule_relative_url \"(null)\" \"/u/trash directory.t3600-rm/.\" \"../.\" \"/u/trash directory.t3600-rm/.\"\n+test_submodule_relative_url \"(null)\" \"/u/trash directory.t3600-rm\" \"./.\" \"/u/trash directory.t3600-rm/.\"\n+test_submodule_relative_url \"(null)\" \"/u/trash directory.t7400-submodule-basic/addtest\" \"../repo\" \"/u/trash directory.t7400-submodule-basic/repo\"\n+test_submodule_relative_url \"../\" \"/u/trash directory.t7400-submodule-basic/addtest\" \"../repo\" \"/u/trash directory.t7400-submodule-basic/repo\"\n+test_submodule_relative_url \"(null)\" \"/u/trash directory.t7400-submodule-basic\" \"./å äö\" \"/u/trash directory.t7400-submodule-basic/å äö\"\n+test_submodule_relative_url \"(null)\" \"/u/trash directory.t7403-submodule-sync/.\" \"../submodule\" \"/u/trash directory.t7403-submodule-sync/submodule\"\n+test_submodule_relative_url \"(null)\" \"/u/trash directory.t7407-submodule-foreach/submodule\" \"../submodule\" \"/u/trash directory.t7407-submodule-foreach/submodule\"\n+test_submodule_relative_url \"(null)\" \"/u/trash directory.t7409-submodule-detached-worktree/home2/../remote\" \"../bundle1\" \"/u/trash directory.t7409-submodule-detached-worktree/home2/../bundle1\"\n+test_submodule_relative_url \"(null)\" \"/u/trash directory.t7613-merge-submodule/submodule_update_repo\" \"./.\" \"/u/trash directory.t7613-merge-submodule/submodule_update_repo/.\"\n+test_submodule_relative_url \"(null)\" \"file:///tmp/repo\" \"../subrepo\" \"file:///tmp/subrepo\"\n+test_submodule_relative_url \"(null)\" \"foo/bar\" \"../submodule\" \"foo/submodule\"\n+test_submodule_relative_url \"../\" \"foo/bar\" \"../submodule\" \"../foo/submodule\"\n+test_submodule_relative_url \"(null)\" \"foo\" \"../submodule\" \"submodule\"\n+test_submodule_relative_url \"../\" \"foo\" \"../submodule\" \"../submodule\"\n+test_submodule_relative_url \"(null)\" \"helper:://hostname/repo\" \"../subrepo\" \"helper:://hostname/subrepo\"\n+test_submodule_relative_url \"(null)\" \"ssh://hostname/repo\" \"../subrepo\" \"ssh://hostname/subrepo\"\n+test_submodule_relative_url \"(null)\" \"ssh://hostname:22/repo\" \"../subrepo\" \"ssh://hostname:22/subrepo\"\n+test_submodule_relative_url \"(null)\" \"user@host:path/to/repo\" \"../subrepo\" \"user@host:path/to/subrepo\"\n+test_submodule_relative_url \"(null)\" \"user@host:repo\" \"../subrepo\" \"user@host:subrepo\"\n+\n test_done\n-- \n2.7.1.292.g18a4ced.dirty\n"},{"id":"278051","messageId":"1455320356-15778-3-git-send-email-sbeller@google.com","threadId":"41405","inReplyTo":"1455320356-15778-1-git-send-email-sbeller@google.com","subject":"[PATCH 2/2] submodule: port init from shell to C","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-02-12T23:39:16Z","receivedAt":"2016-02-12T23:39:16Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"By having the `init` functionality in C, we can reference it easier\nfrom other parts in the code.\n\nSigned-off-by: Stefan Beller <sbeller@google.com>\n---\n builtin/submodule--helper.c | 107 ++++++++++++++++++++++++++++++++++++++++++++\n git-submodule.sh            |  39 +---------------\n submodule.c                 |  21 +++++++++\n submodule.h                 |   1 +\n 4 files changed, 130 insertions(+), 38 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex d1e9118..30e623a 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -214,6 +214,112 @@ static int resolve_relative_url_test(int argc, const char **argv, const char *pr\n \treturn 0;\n }\n \n+static void init_submodule(const char *path, const char *prefix, int quiet)\n+{\n+\tconst struct submodule *sub;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tchar *url = NULL;\n+\tconst char *upd = NULL;\n+\tchar *cwd = xgetcwd();\n+\tconst char *displaypath = relative_path(cwd, prefix, &sb);\n+\n+\t/* Only loads from .gitmodules, no overlay with .git/config */\n+\tgitmodules_config();\n+\n+\tsub = submodule_from_path(null_sha1, path);\n+\n+\t/*\n+\t * Copy url setting when it is not set yet.\n+\t * To look up the url in .git/config, we must not fall back to\n+\t * .gitmodules, so look it up directly.\n+\t */\n+\tstrbuf_reset(&sb);\n+\tstrbuf_addf(&sb, \"submodule.%s.url\", sub->name);\n+\tif (git_config_get_string(sb.buf, &url)) {\n+\t\turl = xstrdup(sub->url);\n+\n+\t\tif (!url)\n+\t\t\tdie(_(\"No url found for submodule path '%s' in .gitmodules\"),\n+\t\t\t\tdisplaypath);\n+\n+\t\t/* Possibly a url relative to parent */\n+\t\tif (starts_with_dot_dot_slash(url) ||\n+\t\t    starts_with_dot_slash(url)) {\n+\t\t\tchar *remoteurl;\n+\t\t\tchar *remote = get_default_remote();\n+\t\t\tstruct strbuf remotesb = STRBUF_INIT;\n+\t\t\tstrbuf_addf(&remotesb, \"remote.%s.url\", remote);\n+\t\t\tfree(remote);\n+\n+\t\t\tif (git_config_get_string(remotesb.buf, &remoteurl))\n+\t\t\t\t/*\n+\t\t\t\t * The repository is its own\n+\t\t\t\t * authoritative upstream\n+\t\t\t\t */\n+\t\t\t\tremoteurl = xgetcwd();\n+\t\t\turl = relative_url(remoteurl, url, NULL);\n+\t\t\tstrbuf_release(&remotesb);\n+\t\t\tfree(remoteurl);\n+\t\t}\n+\n+\t\tif (git_config_set(sb.buf, url))\n+\t\t\tdie(_(\"Failed to register url for submodule path '%s'\"),\n+\t\t\t    displaypath);\n+\t\tif (!quiet)\n+\t\t\tprintf(_(\"Submodule '%s' (%s) registered for path '%s'\\n\"),\n+\t\t\t\tsub->name, url, displaypath);\n+\t}\n+\n+\t/* Copy \"update\" setting when it is not set yet */\n+\tstrbuf_reset(&sb);\n+\tstrbuf_addf(&sb, \"submodule.%s.update\", sub->name);\n+\tif (git_config_get_string_const(sb.buf, &upd) &&\n+\t    sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) {\n+\t\tif (sub->update_strategy.type == SM_UPDATE_COMMAND) {\n+\t\t\tfprintf(stderr, _(\"warning: command update mode suggested for submodule '%s'\\n\"),\n+\t\t\t\tsub->name);\n+\t\t\tupd = \"none\";\n+\t\t} else\n+\t\t\tupd = submodule_strategy_to_string(&sub->update_strategy);\n+\n+\t\tif (git_config_set(sb.buf, upd))\n+\t\t\tdie(_(\"Failed to register update mode for submodule path '%s'\"), displaypath);\n+\t}\n+\tstrbuf_release(&sb);\n+\tfree(cwd);\n+\tfree(url);\n+}\n+\n+static int module_init(int argc, const char **argv, const char *prefix)\n+{\n+\tint quiet = 0;\n+\tint i;\n+\n+\tstruct option module_init_options[] = {\n+\t\tOPT_STRING(0, \"prefix\", &prefix,\n+\t\t\t   N_(\"path\"),\n+\t\t\t   N_(\"alternative anchor for relative paths\")),\n+\t\tOPT__QUIET(&quiet, \"Suppress output for initialzing a submodule\"),\n+\t\tOPT_END()\n+\t};\n+\n+\tconst char *const git_submodule_helper_usage[] = {\n+\t\tN_(\"git submodule--helper init [<path>]\"),\n+\t\tNULL\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, module_init_options,\n+\t\t\t     git_submodule_helper_usage, 0);\n+\n+\tif (argc == 0)\n+\t\tdie(_(\"Pass at least one submodule\"));\n+\n+\tfor (i = 0; i < argc; i++)\n+\t\tinit_submodule(argv[i], prefix, quiet);\n+\n+\treturn 0;\n+}\n+\n struct module_list {\n \tconst struct cache_entry **entries;\n \tint alloc, nr;\n@@ -709,6 +815,7 @@ static struct cmd_struct commands[] = {\n \t{\"update-clone\", update_clone},\n \t{\"resolve-relative-url\", resolve_relative_url},\n \t{\"resolve-relative-url-test\", resolve_relative_url_test},\n+\t{\"init\", module_init}\n };\n \n int cmd_submodule__helper(int argc, const char **argv, const char *prefix)\ndiff --git a/git-submodule.sh b/git-submodule.sh\nindex 615ef9b..6fce0dc 100755\n--- a/git-submodule.sh\n+++ b/git-submodule.sh\n@@ -398,45 +398,8 @@ cmd_init()\n \twhile read mode sha1 stage sm_path\n \tdo\n \t\tdie_if_unmatched \"$mode\"\n-\t\tname=$(git submodule--helper name \"$sm_path\") || exit\n-\n-\t\tdisplaypath=$(relative_path \"$sm_path\")\n-\n-\t\t# Copy url setting when it is not set yet\n-\t\tif test -z \"$(git config \"submodule.$name.url\")\"\n-\t\tthen\n-\t\t\turl=$(git config -f .gitmodules submodule.\"$name\".url)\n-\t\t\ttest -z \"$url\" &&\n-\t\t\tdie \"$(eval_gettext \"No url found for submodule path '\\$displaypath' in .gitmodules\")\"\n-\n-\t\t\t# Possibly a url relative to parent\n-\t\t\tcase \"$url\" in\n-\t\t\t./*|../*)\n-\t\t\t\turl=$(git submodule--helper resolve-relative-url \"$url\") || exit\n-\t\t\t\t;;\n-\t\t\tesac\n-\t\t\tgit config submodule.\"$name\".url \"$url\" ||\n-\t\t\tdie \"$(eval_gettext \"Failed to register url for submodule path '\\$displaypath'\")\"\n \n-\t\t\tsay \"$(eval_gettext \"Submodule '\\$name' (\\$url) registered for path '\\$displaypath'\")\"\n-\t\tfi\n-\n-\t\t# Copy \"update\" setting when it is not set yet\n-\t\tif upd=\"$(git config -f .gitmodules submodule.\"$name\".update)\" &&\n-\t\t   test -n \"$upd\" &&\n-\t\t   test -z \"$(git config submodule.\"$name\".update)\"\n-\t\tthen\n-\t\t\tcase \"$upd\" in\n-\t\t\tcheckout | rebase | merge | none)\n-\t\t\t\t;; # known modes of updating\n-\t\t\t*)\n-\t\t\t\techo >&2 \"warning: unknown update mode '$upd' suggested for submodule '$name'\"\n-\t\t\t\tupd=none\n-\t\t\t\t;;\n-\t\t\tesac\n-\t\t\tgit config submodule.\"$name\".update \"$upd\" ||\n-\t\t\tdie \"$(eval_gettext \"Failed to register update mode for submodule path '\\$displaypath'\")\"\n-\t\tfi\n+\t\tgit submodule--helper init ${GIT_QUIET:+--quiet} \"$sm_path\" || exit\n \tdone\n }\n \ndiff --git a/submodule.c b/submodule.c\nindex 263cb2a..37d6b8d 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -235,6 +235,27 @@ int parse_submodule_update_strategy(const char *value,\n \treturn 0;\n }\n \n+const char *submodule_strategy_to_string(const struct submodule_update_strategy *s)\n+{\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tswitch (s->type) {\n+\tcase SM_UPDATE_CHECKOUT:\n+\t\treturn \"checkout\";\n+\tcase SM_UPDATE_MERGE:\n+\t\treturn \"merge\";\n+\tcase SM_UPDATE_REBASE:\n+\t\treturn \"rebase\";\n+\tcase SM_UPDATE_NONE:\n+\t\treturn \"none\";\n+\tcase SM_UPDATE_UNSPECIFIED:\n+\t\treturn NULL;\n+\tcase SM_UPDATE_COMMAND:\n+\t\tstrbuf_addf(&sb, \"!%s\", s->command);\n+\t\treturn strbuf_detach(&sb, 0);\n+\t}\n+\treturn NULL;\n+}\n+\n void handle_ignore_submodules_arg(struct diff_options *diffopt,\n \t\t\t\t  const char *arg)\n {\ndiff --git a/submodule.h b/submodule.h\nindex 7ef3775..ff4c4f3 100644\n--- a/submodule.h\n+++ b/submodule.h\n@@ -39,6 +39,7 @@ int submodule_config(const char *var, const char *value, void *cb);\n void gitmodules_config(void);\n int parse_submodule_update_strategy(const char *value,\n \t\tstruct submodule_update_strategy *dst);\n+const char *submodule_strategy_to_string(const struct submodule_update_strategy *s);\n void handle_ignore_submodules_arg(struct diff_options *diffopt, const char *);\n void show_submodule_summary(FILE *f, const char *path,\n \t\tconst char *line_prefix,\n-- \n2.7.1.292.g18a4ced.dirty\n"},{"id":"278571","messageId":"xmqqsi0pg439.fsf@gitster.mtv.corp.google.com","threadId":"41405","inReplyTo":"1455320356-15778-2-git-send-email-sbeller@google.com","subject":"Re: [PATCH 1/2] submodule: port resolve_relative_url from shell to C","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-02-18T20:23:38Z","receivedAt":"2016-02-18T20:23:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Stefan Beller <sbeller@google.com> writes:\n\n> +static int starts_with_dot_slash(const char *str)\n> +{\n> +\treturn str[0] == '.' && is_dir_sep(str[1]);\n> +}\n> +\n> +static int starts_with_dot_dot_slash(const char *str)\n> +{\n> +\treturn str[0] == '.' && str[1] == '.' && is_dir_sep(str[2]);\n> +}\n> +\n> +/*\n> + * Returns 1 if it was the last chop before ':'.\n> + */\n> +static int chop_last_dir(char **remoteurl, int is_relative)\n> +{\n> +\tchar *rfind = find_last_dir_sep(*remoteurl);\n> +\tif (rfind) {\n> +\t\t*rfind = '\\0';\n> +\t\treturn 0;\n> +\t}\n> +\n> +\trfind = strrchr(*remoteurl, ':');\n> +\tif (rfind) {\n> +\t\t*rfind = '\\0';\n> +\t\treturn 1;\n> +\t}\n> +\n> +\tif (is_relative || !strcmp(\".\", *remoteurl))\n> +\t\tdie(_(\"cannot strip one component off url '%s'\"),\n> +\t\t\t*remoteurl);\n> +\n> +\tfree(*remoteurl);\n> +\t*remoteurl = xstrdup(\".\");\n> +\treturn 0;\n> +}\n> +\n> +/*\n> + * The `url` argument is the URL that navigates to the submodule origin\n> + * repo. When relative, this URL is relative to the superproject origin\n> + * URL repo. The `up_path` argument, if specified, is the relative\n> + * path that navigates from the submodule working tree to the superproject\n> + * working tree. Returns the origin URL of the submodule.\n> + *\n> + * Return either an absolute URL or filesystem path (if the superproject\n> + * origin URL is an absolute URL or filesystem path, respectively) or a\n> + * relative file system path (if the superproject origin URL is a relative\n> + * file system path).\n> + *\n> + * When the output is a relative file system path, the path is either\n> + * relative to the submodule working tree, if up_path is specified, or to\n> + * the superproject working tree otherwise.\n> + *\n> + * NEEDSWORK: This works incorrectly on the domain and protocol part.\n> + * remote_url      url              outcome          correct\n> + * http://a.com/b  ../c             http://a.com/c   yes\n> + * http://a.com/b  ../../c          http://c         no (domain should be kept)\n> + * http://a.com/b  ../../../c       http:/c          no\n> + * http://a.com/b  ../../../../c    http:c           no\n> + * http://a.com/b  ../../../../../c    .:c           no\n> + */\n\nNot just \"no\" but we should say what the expected outcome is.  I\nthink all of them should error out?\n\nGiven how chop_last_dir() works, I think this function is broken\nwhen a local part has a colon in its path component, too.  I do not\nthink the scripted version did any better on such a URL; just\nanother thing that NEEDSWORK comment should list as a thing to be\nfixed in the future.\n"},{"id":"278572","messageId":"xmqqmvqxg30y.fsf@gitster.mtv.corp.google.com","threadId":"41405","inReplyTo":"1455320356-15778-3-git-send-email-sbeller@google.com","subject":"Re: [PATCH 2/2] submodule: port init from shell to C","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-02-18T20:46:37Z","receivedAt":"2016-02-18T20:46:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Stefan Beller <sbeller@google.com> writes:\n\n> By having the `init` functionality in C, we can reference it easier\n> from other parts in the code.\n>\n> Signed-off-by: Stefan Beller <sbeller@google.com>\n> ---\n>  builtin/submodule--helper.c | 107 ++++++++++++++++++++++++++++++++++++++++++++\n>  git-submodule.sh            |  39 +---------------\n>  submodule.c                 |  21 +++++++++\n>  submodule.h                 |   1 +\n>  4 files changed, 130 insertions(+), 38 deletions(-)\n>\n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> index d1e9118..30e623a 100644\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -214,6 +214,112 @@ static int resolve_relative_url_test(int argc, const char **argv, const char *pr\n>  \treturn 0;\n>  }\n>  \n> +static void init_submodule(const char *path, const char *prefix, int quiet)\n> +{\n> +\tconst struct submodule *sub;\n> +\tstruct strbuf sb = STRBUF_INIT;\n> +\tchar *url = NULL;\n> +\tconst char *upd = NULL;\n> +\tchar *cwd = xgetcwd();\n> +\tconst char *displaypath = relative_path(cwd, prefix, &sb);\n> +\n> +\t/* Only loads from .gitmodules, no overlay with .git/config */\n> +\tgitmodules_config();\n\nThis feeds submodule_config() function with the contents of\n\".gitmodules\".\n\n> +\tsub = submodule_from_path(null_sha1, path);\n> +\n> +\t/*\n> +\t * Copy url setting when it is not set yet.\n> +\t * To look up the url in .git/config, we must not fall back to\n> +\t * .gitmodules, so look it up directly.\n> +\t */\n> +\tstrbuf_reset(&sb);\n> +\tstrbuf_addf(&sb, \"submodule.%s.url\", sub->name);\n> +\tif (git_config_get_string(sb.buf, &url)) {\n> +\t\turl = xstrdup(sub->url);\n> +\t\tif (!url)\n> +\t\t\tdie(_(\"No url found for submodule path '%s' in .gitmodules\"),\n> +\t\t\t\tdisplaypath);\n\nI am assuming that this corresponds to these lines in the original\nscripted version:\n\n\t\turl=$(git config -f .gitmodules submodule.\"$name\".url)\n\t\ttest -z \"$url\" &&\n\t\tdie \"$(eval_gettext \"No url found for submodule path...\n\nbut what makes git_config_get_string() to read from \".gitmodules\"\nfile?  Doesn't it read from $GIT_DIR/config & ~/.gitconfig instead?\n\n> +\t\t/* Possibly a url relative to parent */\n> +\t\tif (starts_with_dot_dot_slash(url) ||\n> +\t\t    starts_with_dot_slash(url)) {\n> +\t\t\tchar *remoteurl;\n> +\t\t\tchar *remote = get_default_remote();\n> +\t\t\tstruct strbuf remotesb = STRBUF_INIT;\n> +\t\t\tstrbuf_addf(&remotesb, \"remote.%s.url\", remote);\n> +\t\t\tfree(remote);\n> +\n> +\t\t\tif (git_config_get_string(remotesb.buf, &remoteurl))\n> +\t\t\t\t/*\n> +\t\t\t\t * The repository is its own\n> +\t\t\t\t * authoritative upstream\n> +\t\t\t\t */\n> +\t\t\t\tremoteurl = xgetcwd();\n> +\t\t\turl = relative_url(remoteurl, url, NULL);\n> +\t\t\tstrbuf_release(&remotesb);\n> +\t\t\tfree(remoteurl);\n\nDoes the code inside this block correspond to this single line in\nthe original?\n\n\turl=$(git submodule--helper resolve-relative-url \"$url\") || exit\n\nIt seems to be doing quite a different thing, though.\n\n> +\t\t}\n> +\n> +\t\tif (git_config_set(sb.buf, url))\n> +\t\t\tdie(_(\"Failed to register url for submodule path '%s'\"),\n> +\t\t\t    displaypath);\n> +\t\tif (!quiet)\n> +\t\t\tprintf(_(\"Submodule '%s' (%s) registered for path '%s'\\n\"),\n> +\t\t\t\tsub->name, url, displaypath);\n> +\t}\n> +\n> +\t/* Copy \"update\" setting when it is not set yet */\n> +\tstrbuf_reset(&sb);\n> +\tstrbuf_addf(&sb, \"submodule.%s.update\", sub->name);\n> +\tif (git_config_get_string_const(sb.buf, &upd) &&\n\n\n\nThis part of the code is supposed to read from in-tree \".gitmodules\"\nand copy to $GIT_DIR/config (i.e. git_config_set() below), but\nagain, I am not sure what makes this read from \".gitmodules\".\n\nPuzzled.\n\n> +\t    sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) {\n> +\t\tif (sub->update_strategy.type == SM_UPDATE_COMMAND) {\n> +\t\t\tfprintf(stderr, _(\"warning: command update mode suggested for submodule '%s'\\n\"),\n> +\t\t\t\tsub->name);\n> +\t\t\tupd = \"none\";\n> +\t\t} else\n> +\t\t\tupd = submodule_strategy_to_string(&sub->update_strategy);\n> +\n> +\t\tif (git_config_set(sb.buf, upd))\n> +\t\t\tdie(_(\"Failed to register update mode for submodule path '%s'\"), displaypath);\n> +\t}\n> +\tstrbuf_release(&sb);\n> +\tfree(cwd);\n> +\tfree(url);\n> +}\n"},{"id":"278593","messageId":"xmqqmvqxehk6.fsf@gitster.mtv.corp.google.com","threadId":"41405","inReplyTo":"xmqqmvqxg30y.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH 2/2] submodule: port init from shell to C","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-02-18T23:15:37Z","receivedAt":"2016-02-18T23:15:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n>> +\tstrbuf_reset(&sb);\n>> +\tstrbuf_addf(&sb, \"submodule.%s.url\", sub->name);\n>> +\tif (git_config_get_string(sb.buf, &url)) {\n>> +\t\turl = xstrdup(sub->url);\n>> +\t\tif (!url)\n>> +\t\t\tdie(_(\"No url found for submodule path '%s' in .gitmodules\"),\n>> +\t\t\t\tdisplaypath);\n>\n> I am assuming that this corresponds to these lines in the original\n> scripted version:\n>\n> \t\turl=$(git config -f .gitmodules submodule.\"$name\".url)\n> \t\ttest -z \"$url\" &&\n> \t\tdie \"$(eval_gettext \"No url found for submodule path...\n>\n> but what makes git_config_get_string() to read from \".gitmodules\"\n> file?  Doesn't it read from $GIT_DIR/config & ~/.gitconfig instead?\n\nI am starting to suspect that reading of \".gitmodules\" in the\ncontext of \"git submodule\" command is a good use case for the\nconfigset API.  It wants to read variables from \".gitmodules\" and\nthe regular configuration file, and cares about where they come\nfrom, illustrated by this codepath.  Read URL from .gitmodules, do\nsomething to it, and update the regular configuration file.  Read\nUpdate from .gitmodules, do some verification, and selectively store\nit in the regular configuration file.  There may be cases where you\nwant to check if a variable is in the regular configuration file\n(i.e. read from there), see its value in \".gitmodules\", and\nconditionally update the regular configuration file (i.e. write into\nit).  The configset API was designed to help implement this kind of\nthing in a clean manner (i.e. initialize one, add \".gitmodules\"\nfile, and then configset_get_* on it, without affecting what you\nread and write with the regular config_get_*/config_set_* to the\nregular configuration file).\n"},{"id":"279631","messageId":"20160227082821.GA14856@lanh","threadId":"41405","inReplyTo":"1455320356-15778-2-git-send-email-sbeller@google.com","subject":"Re: [PATCH 1/2] submodule: port resolve_relative_url from shell to C","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-27T08:28:22Z","receivedAt":"2016-02-27T08:28:22Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Feb 12, 2016 at 03:39:15PM -0800, Stefan Beller wrote:\n> Later on we want to automatically call `git submodule init` from\n> other commands, such that the users don't have to initialize the\n> submodule themselves.  As these other commands are written in C\n> already, we'd need the init functionality in C, too.  The\n> `resolve_relative_url` function is a large part of that init\n> functionality, so start by porting this function to C.\n> \n> To create the tests in t0060, the function `resolve_relative_url`\n> was temporarily enhanced to write all inputs and output to disk\n> when running the test suite. The added tests in this patch are\n> a small selection thereof.\n> \n> Signed-off-by: Stefan Beller <sbeller@google.com>\n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> ---\n>  builtin/submodule--helper.c | 208 +++++++++++++++++++++++++++++++++++++++++++-\n\ni18n fixes. Can you squash this patch in when it's re-rolled?\n\n-- 8< --\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex f4a0fd7..a6e54fa 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -22,7 +22,7 @@ static char *get_default_remote(void)\n \tconst char *refname = resolve_ref_unsafe(\"HEAD\", 0, sha1, &flag);\n \n \tif (!refname)\n-\t\tdie(\"No such ref: HEAD\");\n+\t\tdie(_(\"No such ref: %s\"), \"HEAD\");\n \n \t/* detached HEAD */\n \tif (!strcmp(refname, \"HEAD\"))\n-- 8< --\n"},{"id":"280070","messageId":"56D7210D.8050708@kdbg.org","threadId":"41405","inReplyTo":"1455320356-15778-2-git-send-email-sbeller@google.com","subject":"Re: [PATCH 1/2] submodule: port resolve_relative_url from shell to C","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2016-03-02T17:21:17Z","receivedAt":"2016-03-02T17:21:17Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 13.02.2016 um 00:39 schrieb Stefan Beller:\n> @@ -289,4 +296,39 @@ test_git_path GIT_COMMON_DIR=bar config                   bar/config\n>   test_git_path GIT_COMMON_DIR=bar packed-refs              bar/packed-refs\n>   test_git_path GIT_COMMON_DIR=bar shallow                  bar/shallow\n>   \n> +test_submodule_relative_url \"(null)\" \"../foo/bar\" \"../sub/a/b/c\" \"../foo/sub/a/b/c\"\n> +test_submodule_relative_url \"../../../\" \"../foo/bar\" \"../sub/a/b/c\" \"../../../../foo/sub/a/b/c\"\n> +test_submodule_relative_url \"(null)\" \"../foo/bar\" \"../submodule\" \"../foo/submodule\"\n> +test_submodule_relative_url \"../\" \"../foo/bar\" \"../submodule\" \"../../foo/submodule\"\n> +test_submodule_relative_url \"(null)\" \"../foo/submodule\" \"../submodule\" \"../foo/submodule\"\n> +test_submodule_relative_url \"../\" \"../foo/submodule\" \"../submodule\" \"../../foo/submodule\"\n> +test_submodule_relative_url \"(null)\" \"../foo\" \"../submodule\" \"../submodule\"\n> +test_submodule_relative_url \"../\" \"../foo\" \"../submodule\" \"../../submodule\"\n> +test_submodule_relative_url \"(null)\" \"./foo/bar\" \"../submodule\" \"foo/submodule\"\n> +test_submodule_relative_url \"../\" \"./foo/bar\" \"../submodule\" \"../foo/submodule\"\n> +test_submodule_relative_url \"(null)\" \"./foo\" \"../submodule\" \"submodule\"\n> +test_submodule_relative_url \"../\" \"./foo\" \"../submodule\" \"../submodule\"\n> +test_submodule_relative_url \"(null)\" \"//somewhere else/repo\" \"../subrepo\" \"//somewhere else/subrepo\"\n> +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\"\n> +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\"\n> +test_submodule_relative_url \"(null)\" \"/u/trash directory.t3600-rm/.\" \"../.\" \"/u/trash directory.t3600-rm/.\"\n> +test_submodule_relative_url \"(null)\" \"/u/trash directory.t3600-rm\" \"./.\" \"/u/trash directory.t3600-rm/.\"\n> +test_submodule_relative_url \"(null)\" \"/u/trash directory.t7400-submodule-basic/addtest\" \"../repo\" \"/u/trash directory.t7400-submodule-basic/repo\"\n> +test_submodule_relative_url \"../\" \"/u/trash directory.t7400-submodule-basic/addtest\" \"../repo\" \"/u/trash directory.t7400-submodule-basic/repo\"\n> +test_submodule_relative_url \"(null)\" \"/u/trash directory.t7400-submodule-basic\" \"./å äö\" \"/u/trash directory.t7400-submodule-basic/å äö\"\n> +test_submodule_relative_url \"(null)\" \"/u/trash directory.t7403-submodule-sync/.\" \"../submodule\" \"/u/trash directory.t7403-submodule-sync/submodule\"\n> +test_submodule_relative_url \"(null)\" \"/u/trash directory.t7407-submodule-foreach/submodule\" \"../submodule\" \"/u/trash directory.t7407-submodule-foreach/submodule\"\n> +test_submodule_relative_url \"(null)\" \"/u/trash directory.t7409-submodule-detached-worktree/home2/../remote\" \"../bundle1\" \"/u/trash directory.t7409-submodule-detached-worktree/home2/../bundle1\"\n> +test_submodule_relative_url \"(null)\" \"/u/trash directory.t7613-merge-submodule/submodule_update_repo\" \"./.\" \"/u/trash directory.t7613-merge-submodule/submodule_update_repo/.\"\n\nThe tests with absolute paths all fail on Windows. The reason is that\ngit.exe sees mangled paths and 'git submodule--helper\nresolve-relative-url-test' produces mangled paths (that begins with a\ndrive letter), whereas the test script expects POSIX paths. The pattern\nI currently use to fix this is\n\ntest_submodule_relative_url \"(null)\" \"$PWD/addtest\" \"../repo\" \"$(pwd)/repo\"\n\n(In our test scripts, $PWD is a POSIX style path and $(pwd) is a\nWindows style path).\n\nWith this change, the penultimate case above still fails because the\n'home2/..' gets lost somewhere in the actual output, which I still have\nto debug.\n\nThe two cases beginning with '/u//' cannot be tested on Windows.\nAre they important? Are the doubled slashes intentional?\n\n> +test_submodule_relative_url \"(null)\" \"file:///tmp/repo\" \"../subrepo\" \"file:///tmp/subrepo\"\n> +test_submodule_relative_url \"(null)\" \"foo/bar\" \"../submodule\" \"foo/submodule\"\n> +test_submodule_relative_url \"../\" \"foo/bar\" \"../submodule\" \"../foo/submodule\"\n> +test_submodule_relative_url \"(null)\" \"foo\" \"../submodule\" \"submodule\"\n> +test_submodule_relative_url \"../\" \"foo\" \"../submodule\" \"../submodule\"\n> +test_submodule_relative_url \"(null)\" \"helper:://hostname/repo\" \"../subrepo\" \"helper:://hostname/subrepo\"\n> +test_submodule_relative_url \"(null)\" \"ssh://hostname/repo\" \"../subrepo\" \"ssh://hostname/subrepo\"\n> +test_submodule_relative_url \"(null)\" \"ssh://hostname:22/repo\" \"../subrepo\" \"ssh://hostname:22/subrepo\"\n> +test_submodule_relative_url \"(null)\" \"user@host:path/to/repo\" \"../subrepo\" \"user@host:path/to/subrepo\"\n> +test_submodule_relative_url \"(null)\" \"user@host:repo\" \"../subrepo\" \"user@host:subrepo\"\n> +\n>   test_done\n> \n"},{"id":"280073","messageId":"CAGZ79kZBXEnXdH1Zm0jg61Gianm6WesAgaf2WA57C63hwOafww@mail.gmail.com","threadId":"41405","inReplyTo":"56D7210D.8050708@kdbg.org","subject":"Re: [PATCH 1/2] submodule: port resolve_relative_url from shell to C","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-03-02T17:34:12Z","receivedAt":"2016-03-02T17:34:12Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Wed, Mar 2, 2016 at 9:21 AM, Johannes Sixt <j6t@kdbg.org> wrote:\n> Am 13.02.2016 um 00:39 schrieb Stefan Beller:\n>> @@ -289,4 +296,39 @@ test_git_path GIT_COMMON_DIR=bar config                   bar/config\n>>   test_git_path GIT_COMMON_DIR=bar packed-refs              bar/packed-refs\n>>   test_git_path GIT_COMMON_DIR=bar shallow                  bar/shallow\n>>\n>> +test_submodule_relative_url \"(null)\" \"../foo/bar\" \"../sub/a/b/c\" \"../foo/sub/a/b/c\"\n>> +test_submodule_relative_url \"../../../\" \"../foo/bar\" \"../sub/a/b/c\" \"../../../../foo/sub/a/b/c\"\n>> +test_submodule_relative_url \"(null)\" \"../foo/bar\" \"../submodule\" \"../foo/submodule\"\n>> +test_submodule_relative_url \"../\" \"../foo/bar\" \"../submodule\" \"../../foo/submodule\"\n>> +test_submodule_relative_url \"(null)\" \"../foo/submodule\" \"../submodule\" \"../foo/submodule\"\n>> +test_submodule_relative_url \"../\" \"../foo/submodule\" \"../submodule\" \"../../foo/submodule\"\n>> +test_submodule_relative_url \"(null)\" \"../foo\" \"../submodule\" \"../submodule\"\n>> +test_submodule_relative_url \"../\" \"../foo\" \"../submodule\" \"../../submodule\"\n>> +test_submodule_relative_url \"(null)\" \"./foo/bar\" \"../submodule\" \"foo/submodule\"\n>> +test_submodule_relative_url \"../\" \"./foo/bar\" \"../submodule\" \"../foo/submodule\"\n>> +test_submodule_relative_url \"(null)\" \"./foo\" \"../submodule\" \"submodule\"\n>> +test_submodule_relative_url \"../\" \"./foo\" \"../submodule\" \"../submodule\"\n>> +test_submodule_relative_url \"(null)\" \"//somewhere else/repo\" \"../subrepo\" \"//somewhere else/subrepo\"\n>> +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\"\n>> +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\"\n>> +test_submodule_relative_url \"(null)\" \"/u/trash directory.t3600-rm/.\" \"../.\" \"/u/trash directory.t3600-rm/.\"\n>> +test_submodule_relative_url \"(null)\" \"/u/trash directory.t3600-rm\" \"./.\" \"/u/trash directory.t3600-rm/.\"\n>> +test_submodule_relative_url \"(null)\" \"/u/trash directory.t7400-submodule-basic/addtest\" \"../repo\" \"/u/trash directory.t7400-submodule-basic/repo\"\n>> +test_submodule_relative_url \"../\" \"/u/trash directory.t7400-submodule-basic/addtest\" \"../repo\" \"/u/trash directory.t7400-submodule-basic/repo\"\n>> +test_submodule_relative_url \"(null)\" \"/u/trash directory.t7400-submodule-basic\" \"./å äö\" \"/u/trash directory.t7400-submodule-basic/å äö\"\n>> +test_submodule_relative_url \"(null)\" \"/u/trash directory.t7403-submodule-sync/.\" \"../submodule\" \"/u/trash directory.t7403-submodule-sync/submodule\"\n>> +test_submodule_relative_url \"(null)\" \"/u/trash directory.t7407-submodule-foreach/submodule\" \"../submodule\" \"/u/trash directory.t7407-submodule-foreach/submodule\"\n>> +test_submodule_relative_url \"(null)\" \"/u/trash directory.t7409-submodule-detached-worktree/home2/../remote\" \"../bundle1\" \"/u/trash directory.t7409-submodule-detached-worktree/home2/../bundle1\"\n>> +test_submodule_relative_url \"(null)\" \"/u/trash directory.t7613-merge-submodule/submodule_update_repo\" \"./.\" \"/u/trash directory.t7613-merge-submodule/submodule_update_repo/.\"\n>\n> The tests with absolute paths all fail on Windows. The reason is that\n> git.exe sees mangled paths and 'git submodule--helper\n> resolve-relative-url-test' produces mangled paths (that begins with a\n> drive letter), whereas the test script expects POSIX paths. The pattern\n> I currently use to fix this is\n>\n> test_submodule_relative_url \"(null)\" \"$PWD/addtest\" \"../repo\" \"$(pwd)/repo\"\n>\n> (In our test scripts, $PWD is a POSIX style path and $(pwd) is a\n> Windows style path).\n>\n> With this change, the penultimate case above still fails because the\n> 'home2/..' gets lost somewhere in the actual output, which I still have\n> to debug.\n>\n> The two cases beginning with '/u//' cannot be tested on Windows.\n> Are they important? Are the doubled slashes intentional?\n\nThe way I got the test cases is by inserting a debug printf into the\nshell script\nand then running the test suite (as that ought to work correct and\ncover everything).\n\nAnd the double slash is intentional, somewhere in the test suite we have a case\nwhere that occurs. It may be a calling error?\n\nThanks for looking into this on the Windows side :)\n\nThanks,\nStefan\n\n>\n>> +test_submodule_relative_url \"(null)\" \"file:///tmp/repo\" \"../subrepo\" \"file:///tmp/subrepo\"\n>> +test_submodule_relative_url \"(null)\" \"foo/bar\" \"../submodule\" \"foo/submodule\"\n>> +test_submodule_relative_url \"../\" \"foo/bar\" \"../submodule\" \"../foo/submodule\"\n>> +test_submodule_relative_url \"(null)\" \"foo\" \"../submodule\" \"submodule\"\n>> +test_submodule_relative_url \"../\" \"foo\" \"../submodule\" \"../submodule\"\n>> +test_submodule_relative_url \"(null)\" \"helper:://hostname/repo\" \"../subrepo\" \"helper:://hostname/subrepo\"\n>> +test_submodule_relative_url \"(null)\" \"ssh://hostname/repo\" \"../subrepo\" \"ssh://hostname/subrepo\"\n>> +test_submodule_relative_url \"(null)\" \"ssh://hostname:22/repo\" \"../subrepo\" \"ssh://hostname:22/subrepo\"\n>> +test_submodule_relative_url \"(null)\" \"user@host:path/to/repo\" \"../subrepo\" \"user@host:path/to/subrepo\"\n>> +test_submodule_relative_url \"(null)\" \"user@host:repo\" \"../subrepo\" \"user@host:subrepo\"\n>> +\n>>   test_done\n>>\n>\n"}]}