{"thread":{"id":"41687","subject":"[PATCH 2/2] submodule: port init from shell to C","startedAt":"2016-03-15T00:15:50Z","lastAt":"2016-03-15T00:15:52Z","messageCount":3,"participants":["Stefan Beller"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"280752","messageId":"1458000952-4043-1-git-send-email-sbeller@google.com","threadId":"41687","inReplyTo":null,"subject":"[PATCH 0/2] Port `git submodule init` from shell to C","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-03-15T00:15:50Z","receivedAt":"2016-03-15T00:15:50Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"This applies on top of origin/sb/submodule-parallel-update.\nBy having the `init` functionality in C, we can reference it easier\nfrom other parts in the code.\n\nThanks for the reviews a long time ago!\n\nJunio,\n  I fixed the NEEDSWORK comment for relative_url\n  and thought about the memory allocation\nDuy,\n  I fixed the i18n issues introduced.\nJohannes,\n  I took $PWD instead of absolute paths. According to your last message, this\n  may still break on Windows for \ntest_submodule_relative_url \"(null)\" \"$PWD/home2/../remote\" \"../bundle1\" \"$PWD/home2/../bundle1\"\n\nThere are however no further tests starting with /.\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 | 317 +++++++++++++++++++++++++++++++++++++++++++-\n git-submodule.sh            | 118 +----------------\n submodule.c                 |  21 +++\n submodule.h                 |   1 +\n t/t0060-path-utils.sh       |  43 ++++++\n 5 files changed, 385 insertions(+), 115 deletions(-)\n\nDiff to origin/sb/submodule-init:\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 213af2e..d942463 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@@ -94,12 +94,14 @@ static int chop_last_dir(char **remoteurl, int is_relative)\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+ * remote_url      url              outcome          expectation\n+ * http://a.com/b  ../c             http://a.com/c   as is\n+ * http://a.com/b  ../../c          http://c         error out\n+ * http://a.com/b  ../../../c       http:/c          error out\n+ * http://a.com/b  ../../../../c    http:c           error out\n+ * http://a.com/b  ../../../../../c    .:c           error out\n+ * NEEDSWORK: Given how chop_last_dir() works, this function is broken\n+ * when a local part has a colon in its path component, too.\n  */\n static char *relative_url(const char *remote_url,\n \t\t\t\tconst char *url,\n@@ -146,6 +148,7 @@ static char *relative_url(const char *remote_url,\n \t}\n \tstrbuf_reset(&sb);\n \tstrbuf_addf(&sb, \"%s%s%s\", remoteurl, colonsep ? \":\" : \"/\", url);\n+\tfree(remoteurl);\n \n \tif (starts_with_dot_slash(sb.buf))\n \t\tout = xstrdup(sb.buf + 2);\n@@ -153,7 +156,6 @@ static char *relative_url(const char *remote_url,\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@@ -299,7 +301,7 @@ static int module_init(int argc, const char **argv, const char *prefix)\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__QUIET(&quiet, N_(\"Suppress output for initializing a submodule\")),\n \t\tOPT_END()\n \t};\n \ndiff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh\nindex 8a1579c..579c1fa 100755\n--- a/t/t0060-path-utils.sh\n+++ b/t/t0060-path-utils.sh\n@@ -293,35 +293,36 @@ 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\" \"../submodule\" \"../../submodule\"\n+test_submodule_relative_url \"../\" \"../foo/bar\" \"../submodule\" \"../../foo/submodule\"\n+test_submodule_relative_url \"../\" \"../foo/submodule\" \"../submodule\" \"../../foo/submodule\"\n+test_submodule_relative_url \"../\" \"./foo\" \"../submodule\" \"../submodule\"\n+test_submodule_relative_url \"../\" \"./foo/bar\" \"../submodule\" \"../foo/submodule\"\n test_submodule_relative_url \"../../../\" \"../foo/bar\" \"../sub/a/b/c\" \"../../../../foo/sub/a/b/c\"\n+test_submodule_relative_url \"../\" \"$PWD/addtest\" \"../repo\" \"$PWD/repo\"\n+test_submodule_relative_url \"../\" \"foo/bar\" \"../submodule\" \"../foo/submodule\"\n+test_submodule_relative_url \"../\" \"foo\" \"../submodule\" \"../submodule\"\n+\n+test_submodule_relative_url \"(null)\" \"../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)\" \"$PWD/subsuper_update_r\" \"../subsubsuper_update_r\" \"$PWD/subsubsuper_update_r\"\n+test_submodule_relative_url \"(null)\" \"$PWD/super_update_r2\" \"../subsuper_update_r\" \"$PWD/subsuper_update_r\"\n+test_submodule_relative_url \"(null)\" \"$PWD/.\" \"../.\" \"$PWD/.\"\n+test_submodule_relative_url \"(null)\" \"$PWD\" \"./.\" \"$PWD/.\"\n+test_submodule_relative_url \"(null)\" \"$PWD/addtest\" \"../repo\" \"$PWD/repo\"\n+test_submodule_relative_url \"(null)\" \"$PWD\" \"./å äö\" \"$PWD/å äö\"\n+test_submodule_relative_url \"(null)\" \"$PWD/.\" \"../submodule\" \"$PWD/submodule\"\n+test_submodule_relative_url \"(null)\" \"$PWD/submodule\" \"../submodule\" \"$PWD/submodule\"\n+test_submodule_relative_url \"(null)\" \"$PWD/home2/../remote\" \"../bundle1\" \"$PWD/home2/../bundle1\"\n+test_submodule_relative_url \"(null)\" \"$PWD/submodule_update_repo\" \"./.\" \"$PWD/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\n\n-- \n2.7.0.rc0.46.g8f16ed4.dirty\n"},{"id":"280753","messageId":"1458000952-4043-2-git-send-email-sbeller@google.com","threadId":"41687","inReplyTo":"1458000952-4043-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-03-15T00:15:51Z","receivedAt":"2016-03-15T00:15:51Z","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 | 210 +++++++++++++++++++++++++++++++++++++++++++-\n git-submodule.sh            |  81 +----------------\n t/t0060-path-utils.sh       |  43 +++++++++\n 3 files changed, 256 insertions(+), 78 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex a484945..bc7cf87 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -9,6 +9,212 @@\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: %s\"), \"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          expectation\n+ * http://a.com/b  ../c             http://a.com/c   as is\n+ * http://a.com/b  ../../c          http://c         error out\n+ * http://a.com/b  ../../../c       http:/c          error out\n+ * http://a.com/b  ../../../../c    http:c           error out\n+ * http://a.com/b  ../../../../../c    .:c           error out\n+ * NEEDSWORK: Given how chop_last_dir() works, this function is broken\n+ * when a local part has a colon in its path component, too.\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+\tfree(remoteurl);\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+\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@@ -515,7 +721,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 86018ee..b3290f8 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@@ -1178,9 +1105,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 627ef85..579c1fa 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@@ -286,4 +293,40 @@ 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 \"../\" \"../foo\" \"../submodule\" \"../../submodule\"\n+test_submodule_relative_url \"../\" \"../foo/bar\" \"../submodule\" \"../../foo/submodule\"\n+test_submodule_relative_url \"../\" \"../foo/submodule\" \"../submodule\" \"../../foo/submodule\"\n+test_submodule_relative_url \"../\" \"./foo\" \"../submodule\" \"../submodule\"\n+test_submodule_relative_url \"../\" \"./foo/bar\" \"../submodule\" \"../foo/submodule\"\n+test_submodule_relative_url \"../../../\" \"../foo/bar\" \"../sub/a/b/c\" \"../../../../foo/sub/a/b/c\"\n+test_submodule_relative_url \"../\" \"$PWD/addtest\" \"../repo\" \"$PWD/repo\"\n+test_submodule_relative_url \"../\" \"foo/bar\" \"../submodule\" \"../foo/submodule\"\n+test_submodule_relative_url \"../\" \"foo\" \"../submodule\" \"../submodule\"\n+\n+test_submodule_relative_url \"(null)\" \"../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 \"(null)\" \"../foo/submodule\" \"../submodule\" \"../foo/submodule\"\n+test_submodule_relative_url \"(null)\" \"../foo\" \"../submodule\" \"../submodule\"\n+test_submodule_relative_url \"(null)\" \"./foo/bar\" \"../submodule\" \"foo/submodule\"\n+test_submodule_relative_url \"(null)\" \"./foo\" \"../submodule\" \"submodule\"\n+test_submodule_relative_url \"(null)\" \"//somewhere else/repo\" \"../subrepo\" \"//somewhere else/subrepo\"\n+test_submodule_relative_url \"(null)\" \"$PWD/subsuper_update_r\" \"../subsubsuper_update_r\" \"$PWD/subsubsuper_update_r\"\n+test_submodule_relative_url \"(null)\" \"$PWD/super_update_r2\" \"../subsuper_update_r\" \"$PWD/subsuper_update_r\"\n+test_submodule_relative_url \"(null)\" \"$PWD/.\" \"../.\" \"$PWD/.\"\n+test_submodule_relative_url \"(null)\" \"$PWD\" \"./.\" \"$PWD/.\"\n+test_submodule_relative_url \"(null)\" \"$PWD/addtest\" \"../repo\" \"$PWD/repo\"\n+test_submodule_relative_url \"(null)\" \"$PWD\" \"./å äö\" \"$PWD/å äö\"\n+test_submodule_relative_url \"(null)\" \"$PWD/.\" \"../submodule\" \"$PWD/submodule\"\n+test_submodule_relative_url \"(null)\" \"$PWD/submodule\" \"../submodule\" \"$PWD/submodule\"\n+test_submodule_relative_url \"(null)\" \"$PWD/home2/../remote\" \"../bundle1\" \"$PWD/home2/../bundle1\"\n+test_submodule_relative_url \"(null)\" \"$PWD/submodule_update_repo\" \"./.\" \"$PWD/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 \"(null)\" \"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.0.rc0.46.g8f16ed4.dirty\n"},{"id":"280751","messageId":"1458000952-4043-3-git-send-email-sbeller@google.com","threadId":"41687","inReplyTo":"1458000952-4043-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-03-15T00:15:52Z","receivedAt":"2016-03-15T00:15:52Z","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>\nSigned-off-by: Junio C Hamano <gitster@pobox.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 bc7cf87..d942463 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -216,6 +216,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, N_(\"Suppress output for initializing 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@@ -724,6 +830,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 b3290f8..97a3097 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 4bd14de..458189c 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -237,6 +237,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 3166608..8cdc4bc 100644\n--- a/submodule.h\n+++ b/submodule.h\n@@ -38,6 +38,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.0.rc0.46.g8f16ed4.dirty\n"}]}