* [PATCH/RFC] git clone: is an URL local or ssh
@ 2013-10-26 19:03 Torsten Bögershausen
2013-10-27 18:31 ` Eric Sunshine
0 siblings, 1 reply; 2+ messages in thread
From: Torsten Bögershausen @ 2013-10-26 19:03 UTC (permalink / raw)
To: git, tboegi; +Cc: peff, pclouds
Commit 8d3d28f5 added test cases for URLs which should be ssh.
Add more tests testing all the combinations:
-IPv4 or IPv6
-path starting with "/" or with "/~"
-with and without the ssh:// scheme
Add tests for ssh:// with port number.
When a git repository "foo:bar" exist, git clone will call
absolute_path() and git_connect() will be called with
something like "/home/user/projects/foo:bar"
Tighten the test and use "foo:bar" instead of "./foo:bar",
refactor clear_ssh() and expect_ssh() into test_clone_url().
"git clone foo/bar:baz" should not be ssh:
Make the rule
"if there is a slash before the first colon, it is not ssh"
more strict by using the same is_local() in both connect.c
and transport.c. Add a test case.
Bug fixes for corner cases:
- Handle clone of [::1]:/~repo correctly (2e7766655):
Git should call "ssh ::1 ~repo", not ssh ::1 /~repo
This was caused by looking at (host != url), which never
worked for host names with literal IPv6 adresses, embedded by []
Support for the [] URLs was introduced in 356bece0a.
- Do not tamper local URLs starting with '[' which have a ']'
Signed-off-by: Torsten Bögershausen <tboegi@web.de>
---
(This does apply on pu, not on master.
I'm almost sure there are more corner cases, but the
most important things should be covered)
connect.c | 47 +++++++++++++++++----------
connect.h | 1 +
t/t5601-clone.sh | 96 +++++++++++++++++++++++++++++++++++++++++++-------------
transport.c | 8 -----
4 files changed, 106 insertions(+), 46 deletions(-)
diff --git a/connect.c b/connect.c
index 06e88b0..903063e 100644
--- a/connect.c
+++ b/connect.c
@@ -231,6 +231,7 @@ int server_supports(const char *feature)
}
enum protocol {
+ PROTO_LOCAL_OR_SSH = 0,
PROTO_LOCAL = 1,
PROTO_SSH,
PROTO_GIT
@@ -547,6 +548,15 @@ static char *get_port(char *host)
static struct child_process no_fork;
+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);
+}
+
+
/*
* This returns a dummy child_process if the transport protocol does not
* need fork(2), or a struct child_process object if it does. Once done,
@@ -564,9 +574,9 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
char *url;
char *host, *path;
char *end;
- int c;
+ int seperator;
struct child_process *conn = &no_fork;
- enum protocol protocol = PROTO_LOCAL;
+ enum protocol protocol = PROTO_LOCAL_OR_SSH;
int free_path = 0;
char *port = NULL;
const char **arg;
@@ -587,20 +597,23 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
*host = '\0';
protocol = get_protocol(url);
host += 3;
- c = '/';
+ seperator = '/';
} else {
host = url;
- c = ':';
+ seperator = ':';
+ if (is_local(url))
+ protocol = PROTO_LOCAL;
}
/*
* Don't do destructive transforms with git:// as that
* protocol code does '[]' unwrapping of its own.
+ * Don't change local URLs
*/
if (host[0] == '[') {
end = strchr(host + 1, ']');
if (end) {
- if (protocol != PROTO_GIT) {
+ if (protocol != PROTO_GIT && protocol != PROTO_LOCAL) {
*end = 0;
host++;
}
@@ -610,17 +623,17 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
} else
end = host;
- path = strchr(end, c);
- if (path && !has_dos_drive_prefix(end)) {
- if (c == ':') {
- if (host != url || path < strchrnul(host, '/')) {
- protocol = PROTO_SSH;
- *path++ = '\0';
- } else /* '/' in the host part, assume local path */
- path = end;
+ path = strchr(end, seperator);
+ if (seperator == ':') {
+ if (path && protocol == PROTO_LOCAL_OR_SSH) {
+ /* We have a ':' */
+ protocol = PROTO_SSH;
+ *path++ = '\0';
+ } else {/* assume local path */
+ protocol = PROTO_LOCAL;
+ path = end;
}
- } else
- path = end;
+ }
if (!path || !*path)
die("No path specified. See 'man git-pull' for valid url syntax");
@@ -629,7 +642,7 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
* null-terminate hostname and point path to ~ for URL's like this:
* ssh://host.xz/~user/repo
*/
- if (protocol != PROTO_LOCAL && host != url) {
+ if (protocol != PROTO_LOCAL && seperator == '/') {
char *ptr = path;
if (path[1] == '~')
path++;
@@ -644,7 +657,7 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
/*
* Add support for ssh port: ssh://host.xy:<port>/...
*/
- if (protocol == PROTO_SSH && host != url)
+ if (protocol == PROTO_SSH && seperator == '/')
port = get_port(end);
if (protocol == PROTO_GIT) {
diff --git a/connect.h b/connect.h
index 64fb7db..fb2de9b 100644
--- a/connect.h
+++ b/connect.h
@@ -5,6 +5,7 @@
extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
extern int finish_connect(struct child_process *conn);
extern int git_connection_is_socket(struct child_process *conn);
+extern int is_local(const char *url);
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);
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index 1d1c875..69af007 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -294,39 +294,93 @@ test_expect_success 'setup ssh wrapper' '
export TRASH_DIRECTORY
'
-clear_ssh () {
- >"$TRASH_DIRECTORY/ssh-output"
-}
-
-expect_ssh () {
+i6501=0
+# $1 url
+# $2 none|host
+# $3 path
+test_clone_url () {
+ i6501=$(($i6501 + 1))
+ >"$TRASH_DIRECTORY/ssh-output" &&
+ test_might_fail git clone "$1" tmp$i6501 &&
{
- case "$1" in
+ case "$2" in
none)
;;
*)
- echo "ssh: $1 git-upload-pack '$2'"
+ echo "ssh: $2 git-upload-pack '$3'"
esac
} >"$TRASH_DIRECTORY/ssh-expect" &&
- (cd "$TRASH_DIRECTORY" && test_cmp ssh-expect ssh-output)
+ (cd "$TRASH_DIRECTORY" && test_cmp ssh-expect ssh-output) && {
+ rm -rf ssh-expect ssh-output
+ }
}
-test_expect_success 'cloning myhost:src uses ssh' '
- clear_ssh &&
- git clone myhost:src ssh-clone &&
- expect_ssh myhost src
-'
-
+# url looks ssh like, and is on disc: should be local
test_expect_success NOT_MINGW,NOT_CYGWIN 'clone local path foo:bar' '
- clear_ssh &&
cp -R src "foo:bar" &&
- git clone "./foo:bar" foobar &&
- expect_ssh none
+ test_clone_url "foo:bar" none &&
+ ( cd tmp$i6501 && git log)
+'
+#ip v4
+for repo in rep rep/home/project /~proj 123
+do
+ test_expect_success "cloning host:$repo" '
+ test_clone_url host:$repo host $repo
+ '
+done
+
+#ipv6
+for repo in rep rep/home/project /~proj 123
+do
+ test_expect_success "cloning [::1]:$repo" '
+ test_clone_url [::1]:$repo ::1 $repo
+ '
+done
+
+# Corner cases
+for url in [foo]bar/baz:qux [foo/bar]:baz foo/bar:baz
+do
+ test_expect_success "cloning $url is not ssh" '
+ test_clone_url $url none
+ '
+done
+
+#with ssh:// scheme
+test_expect_success 'ssh://host.xz/home/user/repo' '
+ test_clone_url "ssh://host.xz/home/user/repo" host.xz "/home/user/repo"
+'
+
+# from home directory
+test_expect_success 'ssh://host.xz/~repo' '
+ test_clone_url "ssh://host.xz/~repo" host.xz "~repo"
+'
+# with port number
+test_expect_success 'ssh://host.xz:22/home/user/repo' '
+ test_clone_url "ssh://host.xz:22/home/user/repo" "-p 22 host.xz" "/home/user/repo"
'
-test_expect_success 'bracketed hostnames are still ssh' '
- clear_ssh &&
- git clone "[myhost:123]:src" ssh-bracket-clone &&
- expect_ssh myhost:123 src
+# from home directory with port number
+test_expect_success 'ssh://host.xz:22/~repo' '
+ test_clone_url "ssh://host.xz:22/~repo" "-p 22 host.xz" "~repo"
+'
+
+#IPv6
+test_expect_success 'ssh://[::1]/home/user/repo' '
+ test_clone_url "ssh://[::1]/home/user/repo" "::1" "/home/user/repo"
+'
+
+#IPv6 from home directory
+test_expect_success 'ssh://[::1]/~repo' '
+ test_clone_url "ssh://[::1]/~repo" "::1" "~repo"
+'
+
+#IPv6 with port number
+test_expect_success 'ssh://[::1]:22/home/user/repo' '
+ test_clone_url "ssh://[::1]:22/home/user/repo" "-p 22 ::1" "/home/user/repo"
+'
+#IPv6 from home directory with port number
+test_expect_success 'ssh://[::1]:22/~repo' '
+ test_clone_url "ssh://[::1]:22/~repo" "-p 22 ::1" "~repo"
'
test_expect_success 'clone from a repository with two identical branches' '
diff --git a/transport.c b/transport.c
index 7202b77..a09ba95 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;
--
1.8.4.457.g424cb08
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH/RFC] git clone: is an URL local or ssh
2013-10-26 19:03 [PATCH/RFC] git clone: is an URL local or ssh Torsten Bögershausen
@ 2013-10-27 18:31 ` Eric Sunshine
0 siblings, 0 replies; 2+ messages in thread
From: Eric Sunshine @ 2013-10-27 18:31 UTC (permalink / raw)
To: Torsten Bögershausen
Cc: git@vger.kernel.org, peff@peff.net, pclouds@gmail.com
On Saturday, October 26, 2013, Torsten Bögershausen wrote:
> diff --git a/connect.c b/connect.c
> index 06e88b0..903063e 100644
> --- a/connect.c
> +++ b/connect.c
> @@ -564,9 +574,9 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
> char *url;
> char *host, *path;
> char *end;
> - int c;
> + int seperator;
s/seperator/separator/g
> struct child_process *conn = &no_fork;
> - enum protocol protocol = PROTO_LOCAL;
> + enum protocol protocol = PROTO_LOCAL_OR_SSH;
> int free_path = 0;
> char *port = NULL;
> const char **arg;
> diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
> index 1d1c875..69af007 100755
> --- a/t/t5601-clone.sh
> +++ b/t/t5601-clone.sh
> @@ -294,39 +294,93 @@ test_expect_success 'setup ssh wrapper' '
> export TRASH_DIRECTORY
> '
>
> -clear_ssh () {
> - >"$TRASH_DIRECTORY/ssh-output"
> -}
> -
> -expect_ssh () {
> +i6501=0
Is this variable meant to be named after the test script t5601? If so:
s/i6501/i5601/g
> +# $1 url
> +# $2 none|host
> +# $3 path
> +test_clone_url () {
> + i6501=$(($i6501 + 1))
> + >"$TRASH_DIRECTORY/ssh-output" &&
> + test_might_fail git clone "$1" tmp$i6501 &&
> {
> - case "$1" in
> + case "$2" in
> none)
> ;;
> *)
> - echo "ssh: $1 git-upload-pack '$2'"
> + echo "ssh: $2 git-upload-pack '$3'"
> esac
> } >"$TRASH_DIRECTORY/ssh-expect" &&
> - (cd "$TRASH_DIRECTORY" && test_cmp ssh-expect ssh-output)
> + (cd "$TRASH_DIRECTORY" && test_cmp ssh-expect ssh-output) && {
> + rm -rf ssh-expect ssh-output
> + }
Should the 'rm' be inside the (cd...) subshell? If not, are the braces
wrapping 'rm' needed, and wouldn't you want to prefix the paths with
$TRASH_DIRECTORY/?
> }
>
> -test_expect_success 'cloning myhost:src uses ssh' '
> - clear_ssh &&
> - git clone myhost:src ssh-clone &&
> - expect_ssh myhost src
> -'
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2013-10-27 18:31 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-10-26 19:03 [PATCH/RFC] git clone: is an URL local or ssh Torsten Bögershausen
2013-10-27 18:31 ` Eric Sunshine
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).