From c59ab2e52a64abd7fded97e0983a9b7f3d0508a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torsten=20B=C3=B6gershausen?= Date: Thu, 28 Nov 2013 20:50:03 +0100 Subject: connect.c: refactor url parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the function is_local() in transport.c public, rename it into url_is_local_not_ssh() and use it in both transport.c and connect.c Use a protocol "local" for URLs for the local file system. One note about using file:// under Windows: The (absolute) path on Unix like system typically starts with "/". When the host is empty, it can be omitted, so that a shell scriptlet url=file://$pwd will give a URL like "file:///home/user/repo". Windows does not have the same concept of a root directory located in "/". When parsing the URL allow "file://C:/user/repo" (even if RFC1738 indicates that "file:///C:/user/repo" should be used). Signed-off-by: Torsten Bögershausen Signed-off-by: Junio C Hamano diff --git a/connect.c b/connect.c index 7874530..04093c4 100644 --- a/connect.c +++ b/connect.c @@ -232,14 +232,24 @@ int server_supports(const char *feature) enum protocol { PROTO_LOCAL = 1, + PROTO_FILE, PROTO_SSH, PROTO_GIT }; +int url_is_local_not_ssh(const char *url) +{ + const char *colon = strchr(url, ':'); + const char *slash = strchr(url, '/'); + return !colon || (slash && slash < colon) || + has_dos_drive_prefix(url); +} + static const char *prot_name(enum protocol protocol) { switch (protocol) { case PROTO_LOCAL: + case PROTO_FILE: return "file"; case PROTO_SSH: return "ssh"; @@ -261,7 +271,7 @@ static enum protocol get_protocol(const char *name) if (!strcmp(name, "ssh+git")) return PROTO_SSH; if (!strcmp(name, "file")) - return PROTO_LOCAL; + return PROTO_FILE; die("I don't handle protocol '%s'", name); } @@ -564,9 +574,8 @@ static enum protocol parse_connect_url(const char *url_orig, char **ret_host, char *url; char *host, *path; char *end; - int separator; + int separator = '/'; enum protocol protocol = PROTO_LOCAL; - int free_path = 0; if (is_url(url_orig)) url = url_decode(url_orig); @@ -578,10 +587,12 @@ static enum protocol parse_connect_url(const char *url_orig, char **ret_host, *host = '\0'; protocol = get_protocol(url); host += 3; - separator = '/'; } else { host = url; - separator = ':'; + if (!url_is_local_not_ssh(url)) { + protocol = PROTO_SSH; + separator = ':'; + } } /* @@ -597,17 +608,12 @@ static enum protocol parse_connect_url(const char *url_orig, char **ret_host, } else end = host; - path = strchr(end, separator); - if (path && !has_dos_drive_prefix(end)) { - if (separator == ':') { - if (host != url || path < strchrnul(host, '/')) { - protocol = PROTO_SSH; - *path++ = '\0'; - } else /* '/' in the host part, assume local path */ - path = end; - } - } else + 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"); @@ -616,23 +622,20 @@ static enum protocol parse_connect_url(const char *url_orig, char **ret_host, * null-terminate hostname and point path to ~ for URL's like this: * ssh://host.xz/~user/repo */ - if (protocol != PROTO_LOCAL) { - char *ptr = path; + + 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++; - else { - path = xstrdup(ptr); - free_path = 1; - } - - *ptr = '\0'; } + path = xstrdup(path); + *end = '\0'; + *ret_host = xstrdup(host); - if (free_path) - *ret_path = path; - else - *ret_path = xstrdup(path); + *ret_path = path; free(url); return protocol; } diff --git a/connect.h b/connect.h index 527d58a..c41a685 100644 --- a/connect.h +++ b/connect.h @@ -9,5 +9,6 @@ extern int git_connection_is_socket(struct child_process *conn); extern int server_supports(const char *feature); extern int parse_feature_request(const char *features, const char *feature); extern const char *server_feature_value(const char *feature, int *len_ret); +extern int url_is_local_not_ssh(const char *url); #endif diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index b4866ea..5b2b1c2 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -612,4 +612,11 @@ do done done +test_expect_success MINGW 'fetch-pack --diag-url file://c:/repo' ' + check_prot_path file://c:/repo file c:/repo +' +test_expect_success MINGW 'fetch-pack --diag-url c:repo' ' + check_prot_path c:repo file c:repo +' + test_done diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh index 53a1de9..62fbd7e 100755 --- a/t/t5601-clone.sh +++ b/t/t5601-clone.sh @@ -362,6 +362,14 @@ do test_clone_url [::1]:$repo ::1 $repo ' done +#home directory +test_expect_success "clone host:/~repo" ' + test_clone_url host:/~repo host "~repo" +' + +test_expect_success "clone [::1]:/~repo" ' + test_clone_url [::1]:/~repo ::1 "~repo" +' # Corner cases for url in foo/bar:baz [foo]bar/baz:qux [foo/bar]:baz diff --git a/transport.c b/transport.c index 7202b77..5485e2a 100644 --- a/transport.c +++ b/transport.c @@ -885,14 +885,6 @@ void transport_take_over(struct transport *transport, transport->cannot_reuse = 1; } -static int is_local(const char *url) -{ - const char *colon = strchr(url, ':'); - const char *slash = strchr(url, '/'); - return !colon || (slash && slash < colon) || - has_dos_drive_prefix(url); -} - static int is_file(const char *url) { struct stat buf; @@ -941,7 +933,7 @@ struct transport *transport_get(struct remote *remote, const char *url) ret->fetch = fetch_objs_via_rsync; ret->push = rsync_transport_push; ret->smart_options = NULL; - } else if (is_local(url) && is_file(url) && is_bundle(url, 1)) { + } 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; @@ -1297,7 +1289,7 @@ char *transport_anonymize_url(const char *url) size_t anon_len, prefix_len = 0; anon_part = strchr(url, '@'); - if (is_local(url) || !anon_part) + if (url_is_local_not_ssh(url) || !anon_part) goto literal_copy; anon_len = strlen(++anon_part); -- cgit v0.10.2-6-g49f6