From: Johannes Sixt <j6t@kdbg.org>
To: Stefan Beller <sbeller@google.com>
Cc: gitster@pobox.com, git@vger.kernel.org, peff@peff.net,
jens.lehmann@web.de
Subject: Re: [PATCHv3] submodule: Port resolve_relative_url from shell to C
Date: Wed, 13 Jan 2016 08:42:18 +0100 [thread overview]
Message-ID: <5695FFDA.9050409@kdbg.org> (raw)
In-Reply-To: <1452643808-29384-1-git-send-email-sbeller@google.com>
Am 13.01.2016 um 01:10 schrieb Stefan Beller:
> Later on we want to deprecate the `git submodule init` command and make
> it implicit in other submodule commands. As these other commands are
> written in C already, we'd need the init functionality in C, too.
> The `resolve_relative_url` function is a major part of that init
> functionality, so start by porting this function to C.
Maybe tone down the word "major" to "a large and non-trivial function"?
Otherwise, the lack of proof for the claim is irritating. (As we saw,
the savings with the port to C are not breath-taking. But we have to
start somewhere.)
>
> As I was porting the functionality I noticed some odds with the inputs.
> To fully understand the situation I added some logging to the function
> temporarily to capture all calls to the function throughout the test
> suite. Duplicates have been removed and all unique testing inputs have
> been recorded into t0060.
>
> Signed-off-by: Stefan Beller <sbeller@google.com>
> ---
...
> +/*
> + * The `url` argument is the URL that navigates to the submodule origin
> + * repo. When relative, this URL is relative to the superproject origin
> + * URL repo. The `up_path` argument, if specified, is the relative
> + * path that navigates from the submodule working tree to the superproject
> + * working tree. Returns the origin URL of the submodule.
> + *
> + * Return either an absolute URL or filesystem path (if the superproject
> + * origin URL is an absolute URL or filesystem path, respectively) or a
> + * relative file system path (if the superproject origin URL is a relative
> + * file system path).
> + *
> + * When the output is a relative file system path, the path is either
> + * relative to the submodule working tree, if up_path is specified, or to
> + * the superproject working tree otherwise.
> + */
> +static char *relative_url(const char *remote_url,
> + const char *url,
> + const char *up_path)
> +{
> + int is_relative = 0;
> + int colonsep = 0;
> + char *out;
> + char *remoteurl = xstrdup(remote_url);
> + struct strbuf sb = STRBUF_INIT;
> + size_t len;
> +
> + len = strlen(remoteurl);
> + if (is_dir_sep(remoteurl[len]))
> + remoteurl[len] = '\0';
> +
> + if (!url_is_local_not_ssh(remoteurl) || is_absolute_path(remoteurl))
> + is_relative = 0;
> + else {
> + is_relative = 1;
> +
> + /* Prepend a './' to ensure all relative remoteurls start
> + * with './' or '../'. */
> + if (!starts_with_dot_slash(remoteurl) &&
> + !starts_with_dot_dot_slash(remoteurl)) {
> + strbuf_reset(&sb);
> + strbuf_addf(&sb, "./%s", remoteurl);
> + free(remoteurl);
> + remoteurl = strbuf_detach(&sb, NULL);
> + }
> + }
> + /* When the url starts with '../', remove that and the
> + * last directory in remoteurl. */
> + while (url) {
> + if (starts_with_dot_dot_slash(url)) {
> + char *rfind;
> + url += 3;
> +
> + rfind = last_dir_separator(remoteurl);
> + if (rfind)
> + *rfind = '\0';
> + else {
> + rfind = strrchr(remoteurl, ':');
> + if (rfind) {
> + *rfind = '\0';
> + colonsep = 1;
> + } else {
> + if (is_relative || !strcmp(".", remoteurl))
> + die(N_("cannot strip one component off url '%s'"), remoteurl);
This must be _("..."), not N_("..."), otherwise no translation
happens at run-time.
> + else
> + remoteurl = ".";
This must be xstrdup(".") because remoteurl is free()d below.
> + }
> + }
> + } else if (starts_with_dot_slash(url)) {
> + url += 2;
> + } else
> + break;
> + }
> + strbuf_reset(&sb);
> + strbuf_addf(&sb, "%s%s%s", remoteurl, colonsep ? ":" : "/", url);
> +
> + if (starts_with_dot_slash(sb.buf))
> + out = xstrdup(sb.buf + 2);
> + else
> + out = xstrdup(sb.buf);
> + strbuf_reset(&sb);
> +
> + free(remoteurl);
> + if (!up_path || !is_relative)
> + return out;
Nit: This conditional clearly feels like (and is) an early function
exit...
> + else {
> + strbuf_addf(&sb, "%s%s", up_path, out);
> +
> + free(out);
> + return strbuf_detach(&sb, NULL);
> + }
... hence, you don't need to place the rest of the function into the
else branch.
> +}
up_path seems to be ignored when remoteurl is absolute. Is that
combination an invalid use case?
I think that you strike a good balance between a direct rewrite
of the shell function and possible optimizations. Therefore,
further improvements should go into separate patches.
> diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
> index 627ef85..2ae1bbd 100755
> --- a/t/t0060-path-utils.sh
> +++ b/t/t0060-path-utils.sh
> @@ -19,6 +19,12 @@ relative_path() {
> "test \"\$(test-path-utils relative_path '$1' '$2')\" = '$expected'"
> }
>
> +test_submodule_relative_url() {
> + expected="$4"
> + test_expect_success "test_submodule_relative_url: $1 $2 $3 => $4" \
> + "test \"\$(git submodule--helper resolve-relative-url-test '$1' '$2' '$3')\" = '$expected'"
> +}
relative_url() has an error exit mode that is not unlikely to trigger,
but the way this test is written such a failure would be detected
only indirectly as a mismatch between actual and expected result. The
reason is that a failed command substitution does not cause the
surrounding command to fail unless it is in the last variable
assignment. Therefore, I suggest to write the helper function like this:
test_submodule_relative_url() {
test_expect_success "test_submodule_relative_url: $1 $2 $3 => $4" "
actual=\$(git submodule--helper resolve-relative-url-test '$1' '$2' '$3') &&
"test \"\$actual\" = '$4'
"
}
> +
> test_git_path() {
> test_expect_success "git-path $1 $2 => $3" "
> $1 git rev-parse --git-path $2 >actual &&
> @@ -286,4 +292,39 @@ test_git_path GIT_COMMON_DIR=bar config bar/config
> test_git_path GIT_COMMON_DIR=bar packed-refs bar/packed-refs
> test_git_path GIT_COMMON_DIR=bar shallow bar/shallow
>
> +test_submodule_relative_url "(null)" "../foo/bar" "../bar/a/b/c" "../foo/bar/a/b/c"
> +test_submodule_relative_url "../../../" "../foo/bar" "../bar/a/b/c" "../../../../foo/bar/a/b/c"
In these two cases, it is unclear whether the "bar" in the 4th
argument is copied from the 2nd or the 3rd argument. I suggest to
use a different token:
test_submodule_relative_url "(null)" "../foo/bar" "../sub/a/b/c" "../foo/sub/a/b/c"
test_submodule_relative_url "../../../" "../foo/bar" "../sub/a/b/c" "../../../../foo/sub/a/b/c"
> +test_submodule_relative_url "(null)" "../foo/bar" "../submodule" "../foo/submodule"
> +test_submodule_relative_url "../" "../foo/bar" "../submodule" "../../foo/submodule"
> +test_submodule_relative_url "(null)" "../foo/submodule" "../submodule" "../foo/submodule"
> +test_submodule_relative_url "../" "../foo/submodule" "../submodule" "../../foo/submodule"
> +test_submodule_relative_url "(null)" "../foo" "../submodule" "../submodule"
> +test_submodule_relative_url "../" "../foo" "../submodule" "../../submodule"
> +test_submodule_relative_url "(null)" "./foo/bar" "../submodule" "foo/submodule"
> +test_submodule_relative_url "../" "./foo/bar" "../submodule" "../foo/submodule"
> +test_submodule_relative_url "(null)" "./foo" "../submodule" "submodule"
> +test_submodule_relative_url "../" "./foo" "../submodule" "../submodule"
> +test_submodule_relative_url "(null)" "//somewhere else/repo" "../subrepo" "//somewhere else/subrepo"
> +test_submodule_relative_url "(null)" "/u//trash directory.t7406-submodule-update/subsuper_update_r" "../subsubsuper_update_r" "/u//trash directory.t7406-submodule-update/subsubsuper_update_r"
> +test_submodule_relative_url "(null)" "/u//trash directory.t7406-submodule-update/super_update_r2" "../subsuper_update_r" "/u//trash directory.t7406-submodule-update/subsuper_update_r"
> +test_submodule_relative_url "(null)" "/u/trash directory.t3600-rm/." "../." "/u/trash directory.t3600-rm/."
> +test_submodule_relative_url "(null)" "/u/trash directory.t3600-rm" "./." "/u/trash directory.t3600-rm/."
> +test_submodule_relative_url "(null)" "/u/trash directory.t7400-submodule-basic/addtest" "../repo" "/u/trash directory.t7400-submodule-basic/repo"
> +test_submodule_relative_url "../" "/u/trash directory.t7400-submodule-basic/addtest" "../repo" "/u/trash directory.t7400-submodule-basic/repo"
> +test_submodule_relative_url "(null)" "/u/trash directory.t7400-submodule-basic" "./å äö" "/u/trash directory.t7400-submodule-basic/å äö"
> +test_submodule_relative_url "(null)" "/u/trash directory.t7403-submodule-sync/." "../submodule" "/u/trash directory.t7403-submodule-sync/submodule"
> +test_submodule_relative_url "(null)" "/u/trash directory.t7407-submodule-foreach/submodule" "../submodule" "/u/trash directory.t7407-submodule-foreach/submodule"
> +test_submodule_relative_url "(null)" "/u/trash directory.t7409-submodule-detached-worktree/home2/../remote" "../bundle1" "/u/trash directory.t7409-submodule-detached-worktree/home2/../bundle1"
> +test_submodule_relative_url "(null)" "/u/trash directory.t7613-merge-submodule/submodule_update_repo" "./." "/u/trash directory.t7613-merge-submodule/submodule_update_repo/."
> +test_submodule_relative_url "(null)" "file:///tmp/repo" "../subrepo" "file:///tmp/subrepo"
> +test_submodule_relative_url "(null)" "foo/bar" "../submodule" "foo/submodule"
> +test_submodule_relative_url "../" "foo/bar" "../submodule" "../foo/submodule"
> +test_submodule_relative_url "(null)" "foo" "../submodule" "submodule"
> +test_submodule_relative_url "../" "foo" "../submodule" "../submodule"
> +test_submodule_relative_url "(null)" "helper:://hostname/repo" "../subrepo" "helper:://hostname/subrepo"
> +test_submodule_relative_url "(null)" "ssh://hostname/repo" "../subrepo" "ssh://hostname/subrepo"
> +test_submodule_relative_url "(null)" "ssh://hostname:22/repo" "../subrepo" "ssh://hostname:22/subrepo"
> +test_submodule_relative_url "(null)" "user@host:path/to/repo" "../subrepo" "user@host:path/to/subrepo"
> +test_submodule_relative_url "(null)" "user@host:repo" "../subrepo" "user@host:subrepo"
> +
> test_done
>
next prev parent reply other threads:[~2016-01-13 7:42 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-01-12 23:35 [PATCHv2] submodule: Port resolve_relative_url from shell to C Stefan Beller
2016-01-13 0:10 ` [PATCHv3] " Stefan Beller
2016-01-13 7:42 ` Johannes Sixt [this message]
2016-01-13 16:58 ` Junio C Hamano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=5695FFDA.9050409@kdbg.org \
--to=j6t@kdbg.org \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=jens.lehmann@web.de \
--cc=peff@peff.net \
--cc=sbeller@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.