diff options
author | Derrick Stolee <derrickstolee@github.com> | 2022-05-16 20:11:03 +0000 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2022-05-16 15:02:10 -0700 |
commit | 1d04e719e7bda885991cd4566a5bb6f6565fa106 (patch) | |
tree | 79d5445b183df056ca991b9b07f89696cbd4ebb0 /remote.c | |
parent | c1d024b84378d099f6ff9bb74e442d17587f3081 (diff) | |
download | git-1d04e719e7bda885991cd4566a5bb6f6565fa106.tar.gz |
remote: move relative_url()
This method was initially written in 63e95beb0 (submodule: port
resolve_relative_url from shell to C, 2016-05-15). As we will need
similar functionality in the bundle URI feature, extract this to be
available in remote.h.
The code is almost exactly the same, except for the following trivial
differences:
* Fix whitespace and wrapping issues with the prototype and argument
lists.
* Let's call starts_with_dot_{,dot_}slash_native() instead of the
functionally identical "starts_with_dot_{,dot_}slash()" wrappers
"builtin/submodule--helper.c".
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Derrick Stolee <derrickstolee@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'remote.c')
-rw-r--r-- | remote.c | 91 |
1 files changed, 91 insertions, 0 deletions
@@ -14,6 +14,7 @@ #include "strvec.h" #include "commit-reach.h" #include "advice.h" +#include "connect.h" enum map_direction { FROM_SRC, FROM_DST }; @@ -2727,3 +2728,93 @@ void remote_state_clear(struct remote_state *remote_state) hashmap_clear_and_free(&remote_state->remotes_hash, struct remote, ent); hashmap_clear_and_free(&remote_state->branches_hash, struct remote, ent); } + +/* + * Returns 1 if it was the last chop before ':'. + */ +static int chop_last_dir(char **remoteurl, int is_relative) +{ + char *rfind = find_last_dir_sep(*remoteurl); + if (rfind) { + *rfind = '\0'; + return 0; + } + + rfind = strrchr(*remoteurl, ':'); + if (rfind) { + *rfind = '\0'; + return 1; + } + + if (is_relative || !strcmp(".", *remoteurl)) + die(_("cannot strip one component off url '%s'"), + *remoteurl); + + free(*remoteurl); + *remoteurl = xstrdup("."); + return 0; +} + +char *relative_url(const char *remote_url, const char *url, + const char *up_path) +{ + int is_relative = 0; + int colonsep = 0; + char *out; + char *remoteurl = xstrdup(remote_url); + struct strbuf sb = STRBUF_INIT; + size_t len = strlen(remoteurl); + + if (is_dir_sep(remoteurl[len-1])) + remoteurl[len-1] = '\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_native(remoteurl) && + !starts_with_dot_dot_slash_native(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_native(url)) { + url += 3; + colonsep |= chop_last_dir(&remoteurl, is_relative); + } else if (starts_with_dot_slash_native(url)) + url += 2; + else + break; + } + strbuf_reset(&sb); + strbuf_addf(&sb, "%s%s%s", remoteurl, colonsep ? ":" : "/", url); + if (ends_with(url, "/")) + strbuf_setlen(&sb, sb.len - 1); + free(remoteurl); + + if (starts_with_dot_slash_native(sb.buf)) + out = xstrdup(sb.buf + 2); + else + out = xstrdup(sb.buf); + + if (!up_path || !is_relative) { + strbuf_release(&sb); + return out; + } + + strbuf_reset(&sb); + strbuf_addf(&sb, "%s%s", up_path, out); + free(out); + return strbuf_detach(&sb, NULL); +} |