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);