/* * 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. * * NEEDSWORK: This works incorrectly on the domain and protocol part. * remote_url url outcome expectation * http://a.com/b ../c http://a.com/c as is * http://a.com/b ../../c http://c error out * http://a.com/b ../../../c http:/c error out * http://a.com/b ../../../../c http:c error out * http://a.com/b ../../../../../c .:c error out * NEEDSWORK: Given how chop_last_dir() works, this function is broken * when a local part has a colon in its path component, too. */ 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 = 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)) { url += 3; colonsep |= chop_last_dir(&remoteurl, is_relative); } else if (starts_with_dot_slash(url)) url += 2; else break; } strbuf_reset(&sb); strbuf_addf(&sb, "%s%s%s", remoteurl, colonsep ? ":" : "/", url); free(remoteurl); if (starts_with_dot_slash(sb.buf)) out = xstrdup(sb.buf + 2); else out = xstrdup(sb.buf); strbuf_reset(&sb); if (!up_path || !is_relative) return out; strbuf_addf(&sb, "%s%s", up_path, out); free(out); return strbuf_detach(&sb, NULL); }
/* * Strip username (and password) from a URL and return * it in a newly allocated string. */ char *transport_anonymize_url(const char *url) { char *anon_url, *scheme_prefix, *anon_part; size_t anon_len, prefix_len = 0; anon_part = strchr(url, '@'); if (url_is_local_not_ssh(url) || !anon_part) goto literal_copy; anon_len = strlen(++anon_part); scheme_prefix = strstr(url, "://"); if (!scheme_prefix) { if (!strchr(anon_part, ':')) /* cannot be "me@there:/path/name" */ goto literal_copy; } else { const char *cp; /* make sure scheme is reasonable */ for (cp = url; cp < scheme_prefix; cp++) { switch (*cp) { /* RFC 1738 2.1 */ case '+': case '.': case '-': break; /* ok */ default: if (isalnum(*cp)) break; /* it isn't */ goto literal_copy; } } /* @ past the first slash does not count */ cp = strchr(scheme_prefix + 3, '/'); if (cp && cp < anon_part) goto literal_copy; prefix_len = scheme_prefix - url + 3; } anon_url = xcalloc(1, 1 + prefix_len + anon_len); memcpy(anon_url, url, prefix_len); memcpy(anon_url + prefix_len, anon_part, anon_len); return anon_url; literal_copy: return xstrdup(url); }
struct transport *transport_get(struct remote *remote, const char *url) { const char *helper; struct transport *ret = xcalloc(1, sizeof(*ret)); ret->progress = isatty(2); if (!remote) BUG("No remote provided to transport_get()"); ret->got_remote_refs = 0; ret->remote = remote; helper = remote->foreign_vcs; if (!url && remote->url) url = remote->url[0]; ret->url = url; /* maybe it is a foreign URL? */ if (url) { const char *p = url; while (is_urlschemechar(p == url, *p)) p++; if (starts_with(p, "::")) helper = xstrndup(url, p - url); } if (helper) { transport_helper_init(ret, helper); } else if (starts_with(url, "rsync:")) { die(_("git-over-rsync is no longer supported")); } else if (url_is_local_not_ssh(url) && is_file(url) && is_bundle(url, 1)) { struct bundle_transport_data *data = xcalloc(1, sizeof(*data)); transport_check_allowed("file"); ret->data = data; ret->vtable = &bundle_vtable; ret->smart_options = NULL; } else if (!is_url(url) || starts_with(url, "file://") || starts_with(url, "git://") || starts_with(url, "ssh://") || starts_with(url, "git+ssh://") /* deprecated - do not use */ || starts_with(url, "ssh+git://") /* deprecated - do not use */ ) { /* * These are builtin smart transports; "allowed" transports * will be checked individually in git_connect. */ struct git_transport_data *data = xcalloc(1, sizeof(*data)); ret->data = data; ret->vtable = &builtin_smart_vtable; ret->smart_options = &(data->options); data->conn = NULL; data->got_remote_heads = 0; } else { /* Unknown protocol in URL. Pass to external handler. */ int len = external_specification_len(url); char *handler = xmemdupz(url, len); transport_helper_init(ret, handler); } if (ret->smart_options) { ret->smart_options->thin = 1; ret->smart_options->uploadpack = "git-upload-pack"; if (remote->uploadpack) ret->smart_options->uploadpack = remote->uploadpack; ret->smart_options->receivepack = "git-receive-pack"; if (remote->receivepack) ret->smart_options->receivepack = remote->receivepack; } return ret; }
struct transport *transport_get(struct remote *remote, const char *url) { const char *helper; struct transport *ret = xcalloc(1, sizeof(*ret)); ret->progress = isatty(2); if (!remote) die("No remote provided to transport_get()"); ret->got_remote_refs = 0; ret->remote = remote; helper = remote->foreign_vcs; if (!url && remote->url) url = remote->url[0]; ret->url = url; /* maybe it is a foreign URL? */ if (url) { const char *p = url; while (is_urlschemechar(p == url, *p)) p++; if (starts_with(p, "::")) helper = xstrndup(url, p - url); } if (helper) { transport_helper_init(ret, helper); } else if (starts_with(url, "rsync:")) { ret->get_refs_list = get_refs_via_rsync; ret->fetch = fetch_objs_via_rsync; ret->push = rsync_transport_push; ret->smart_options = NULL; } else if (url_is_local_not_ssh(url) && is_file(url) && is_bundle(url, 1)) { struct bundle_transport_data *data = xcalloc(1, sizeof(*data)); ret->data = data; ret->get_refs_list = get_refs_from_bundle; ret->fetch = fetch_refs_from_bundle; ret->disconnect = close_bundle; ret->smart_options = NULL; } else if (!is_url(url) || starts_with(url, "file://") || starts_with(url, "git://") || starts_with(url, "ssh://") || starts_with(url, "git+ssh://") || starts_with(url, "ssh+git://")) { /* These are builtin smart transports. */ struct git_transport_data *data = xcalloc(1, sizeof(*data)); ret->data = data; ret->set_option = NULL; ret->get_refs_list = get_refs_via_connect; ret->fetch = fetch_refs_via_pack; ret->push_refs = git_transport_push; ret->connect = connect_git; ret->disconnect = disconnect_git; ret->smart_options = &(data->options); data->conn = NULL; data->got_remote_heads = 0; } else { /* Unknown protocol in URL. Pass to external handler. */ int len = external_specification_len(url); char *handler = xmalloc(len + 1); handler[len] = 0; strncpy(handler, url, len); transport_helper_init(ret, handler); } if (ret->smart_options) { ret->smart_options->thin = 1; ret->smart_options->uploadpack = "git-upload-pack"; if (remote->uploadpack) ret->smart_options->uploadpack = remote->uploadpack; ret->smart_options->receivepack = "git-receive-pack"; if (remote->receivepack) ret->smart_options->receivepack = remote->receivepack; } return ret; }
/* * Extract protocol and relevant parts from the specified connection URL. * The caller must free() the returned strings. */ static enum protocol parse_connect_url(const char *url_orig, char **ret_host, char **ret_path) { char *url; char *host, *path; char *end; int separator = '/'; enum protocol protocol = PROTO_LOCAL; if (is_url(url_orig)) url = url_decode(url_orig); else url = xstrdup(url_orig); host = strstr(url, "://"); if (host) { *host = '\0'; protocol = get_protocol(url); host += 3; } else { host = url; if (!url_is_local_not_ssh(url)) { protocol = PROTO_SSH; separator = ':'; } } /* * Don't do destructive transforms as protocol code does * '[]' unwrapping in get_host_and_port() */ end = host_end(&host, 0); if (protocol == PROTO_LOCAL) path = end; else if (protocol == PROTO_FILE && has_dos_drive_prefix(end)) path = end; /* "file://$(pwd)" may be "file://C:/projects/repo" */ else path = strchr(end, separator); if (!path || !*path) die("No path specified. See 'man git-pull' for valid url syntax"); /* * null-terminate hostname and point path to ~ for URL's like this: * ssh://host.xz/~user/repo */ end = path; /* Need to \0 terminate host here */ if (separator == ':') path++; /* path starts after ':' */ if (protocol == PROTO_GIT || protocol == PROTO_SSH) { if (path[1] == '~') path++; } path = xstrdup(path); *end = '\0'; *ret_host = xstrdup(host); *ret_path = path; free(url); return protocol; }