connect.c: refactor url parsing

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 <tboegi@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Torsten Bögershausen committed Nov 28, 2013 at 20:50 UTC c59ab2e52a64abd7fded97e0983a9b7f3d0508a0
5 files changed +48 -37
connect.c
+30 -27
@@ -232,14 +232,24 @@ int server_supports(const char *feature)
232
233 enum protocol {
234 PROTO_LOCAL = 1,
235 + PROTO_FILE,
236 PROTO_SSH,
237 PROTO_GIT
238 };
239
240 +int url_is_local_not_ssh(const char *url)
241 +{
242 + const char *colon = strchr(url, ':');
243 + const char *slash = strchr(url, '/');
244 + return !colon || (slash && slash < colon) ||
245 + has_dos_drive_prefix(url);
246 +}
247 +
248 static const char *prot_name(enum protocol protocol)
249 {
250 switch (protocol) {
251 case PROTO_LOCAL:
252 + case PROTO_FILE:
253 return "file";
254 case PROTO_SSH:
255 return "ssh";
@@ -261,7 +271,7 @@ static enum protocol get_protocol(const char *name)
271 if (!strcmp(name, "ssh+git"))
272 return PROTO_SSH;
273 if (!strcmp(name, "file"))
264 - return PROTO_LOCAL;
274 + return PROTO_FILE;
275 die("I don't handle protocol '%s'", name);
276 }
277
@@ -564,9 +574,8 @@ static enum protocol parse_connect_url(const char *url_orig, char **ret_host,
574 char *url;
575 char *host, *path;
576 char *end;
567 - int separator;
577 + int separator = '/';
578 enum protocol protocol = PROTO_LOCAL;
569 - int free_path = 0;
579
580 if (is_url(url_orig))
581 url = url_decode(url_orig);
@@ -578,10 +587,12 @@ static enum protocol parse_connect_url(const char *url_orig, char **ret_host,
587 *host = '\0';
588 protocol = get_protocol(url);
589 host += 3;
581 - separator = '/';
590 } else {
591 host = url;
584 - separator = ':';
592 + if (!url_is_local_not_ssh(url)) {
593 + protocol = PROTO_SSH;
594 + separator = ':';
595 + }
596 }
597
598 /*
@@ -597,17 +608,12 @@ static enum protocol parse_connect_url(const char *url_orig, char **ret_host,
608 } else
609 end = host;
610
600 - path = strchr(end, separator);
601 - if (path && !has_dos_drive_prefix(end)) {
602 - if (separator == ':') {
603 - if (host != url || path < strchrnul(host, '/')) {
604 - protocol = PROTO_SSH;
605 - *path++ = '\0';
606 - } else /* '/' in the host part, assume local path */
607 - path = end;
608 - }
609 - } else
611 + if (protocol == PROTO_LOCAL)
612 path = end;
613 + else if (protocol == PROTO_FILE && has_dos_drive_prefix(end))
614 + path = end; /* "file://$(pwd)" may be "file://C:/projects/repo" */
615 + else
616 + path = strchr(end, separator);
617
618 if (!path || !*path)
619 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,
622 * null-terminate hostname and point path to ~ for URL's like this:
623 * ssh://host.xz/~user/repo
624 */
619 - if (protocol != PROTO_LOCAL) {
620 - char *ptr = path;
625 +
626 + end = path; /* Need to \0 terminate host here */
627 + if (separator == ':')
628 + path++; /* path starts after ':' */
629 + if (protocol == PROTO_GIT || protocol == PROTO_SSH) {
630 if (path[1] == '~')
631 path++;
623 - else {
624 - path = xstrdup(ptr);
625 - free_path = 1;
626 - }
627 -
628 - *ptr = '\0';
632 }
633
634 + path = xstrdup(path);
635 + *end = '\0';
636 +
637 *ret_host = xstrdup(host);
632 - if (free_path)
633 - *ret_path = path;
634 - else
635 - *ret_path = xstrdup(path);
638 + *ret_path = path;
639 free(url);
640 return protocol;
641 }
connect.h
+1
@@ -9,5 +9,6 @@ extern int git_connection_is_socket(struct child_process *conn);
9 extern int server_supports(const char *feature);
10 extern int parse_feature_request(const char *features, const char *feature);
11 extern const char *server_feature_value(const char *feature, int *len_ret);
12 +extern int url_is_local_not_ssh(const char *url);
13
14 #endif
t/t5500-fetch-pack.sh
+7
@@ -612,4 +612,11 @@ do
612 done
613 done
614
615 +test_expect_success MINGW 'fetch-pack --diag-url file://c:/repo' '
616 + check_prot_path file://c:/repo file c:/repo
617 +'
618 +test_expect_success MINGW 'fetch-pack --diag-url c:repo' '
619 + check_prot_path c:repo file c:repo
620 +'
621 +
622 test_done
t/t5601-clone.sh
+8
@@ -362,6 +362,14 @@ do
362 test_clone_url [::1]:$repo ::1 $repo
363 '
364 done
365 +#home directory
366 +test_expect_success "clone host:/~repo" '
367 + test_clone_url host:/~repo host "~repo"
368 +'
369 +
370 +test_expect_success "clone [::1]:/~repo" '
371 + test_clone_url [::1]:/~repo ::1 "~repo"
372 +'
373
374 # Corner cases
375 for url in foo/bar:baz [foo]bar/baz:qux [foo/bar]:baz
transport.c
+2 -10
@@ -885,14 +885,6 @@ void transport_take_over(struct transport *transport,
885 transport->cannot_reuse = 1;
886 }
887
888 -static int is_local(const char *url)
889 -{
890 - const char *colon = strchr(url, ':');
891 - const char *slash = strchr(url, '/');
892 - return !colon || (slash && slash < colon) ||
893 - has_dos_drive_prefix(url);
894 -}
895 -
888 static int is_file(const char *url)
889 {
890 struct stat buf;
@@ -941,7 +933,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
933 ret->fetch = fetch_objs_via_rsync;
934 ret->push = rsync_transport_push;
935 ret->smart_options = NULL;
944 - } else if (is_local(url) && is_file(url) && is_bundle(url, 1)) {
936 + } else if (url_is_local_not_ssh(url) && is_file(url) && is_bundle(url, 1)) {
937 struct bundle_transport_data *data = xcalloc(1, sizeof(*data));
938 ret->data = data;
939 ret->get_refs_list = get_refs_from_bundle;
@@ -1297,7 +1289,7 @@ char *transport_anonymize_url(const char *url)
1289 size_t anon_len, prefix_len = 0;
1290
1291 anon_part = strchr(url, '@');
1300 - if (is_local(url) || !anon_part)
1292 + if (url_is_local_not_ssh(url) || !anon_part)
1293 goto literal_copy;
1294
1295 anon_len = strlen(++anon_part);