All of lore.kernel.org
 help / color / mirror / Atom feed
* [REROLL PATCH 0/8] Remote helpers smart transport extensions
@ 2009-12-08 13:16 Ilari Liusvaara
  2009-12-08 13:16 ` [REROLL PATCH 1/8] Add remote helper debug mode Ilari Liusvaara
                   ` (7 more replies)
  0 siblings, 8 replies; 23+ messages in thread
From: Ilari Liusvaara @ 2009-12-08 13:16 UTC (permalink / raw)
  To: git

This is rerolled version of remote helpers exensions. The changes
from RFC patch v3 are:

- Enable stream buffering where possible.
- Pass the connection directly, not indirected via disown method.
- "invoke/connect" -> "connect" in commit message.
- Don't try to send disconnect command after connecting.

This series is based on same version as appiled previous version is.

Ilari Liusvaara (8):
  Add remote helper debug mode
  Support mandatory capabilities
  Pass unknown protocols to external protocol handlers
  Refactor git transport options parsing
  Support taking over transports
  Support remote helpers implementing smart transports
  Support remote archive from external protocol helpers
  Remove special casing of http, https and ftp

 .gitignore                           |    4 +
 Documentation/git-remote-helpers.txt |   30 ++++-
 Makefile                             |   24 +++-
 builtin-archive.c                    |   17 ++-
 transport-helper.c                   |  291 ++++++++++++++++++++++++++++++----
 transport.c                          |  228 +++++++++++++++++++++------
 transport.h                          |   22 +++
 7 files changed, 523 insertions(+), 93 deletions(-)

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [REROLL PATCH 1/8] Add remote helper debug mode
  2009-12-08 13:16 [REROLL PATCH 0/8] Remote helpers smart transport extensions Ilari Liusvaara
@ 2009-12-08 13:16 ` Ilari Liusvaara
  2009-12-08 23:34   ` Junio C Hamano
  2009-12-08 13:16 ` [REROLL PATCH 2/8] Support mandatory capabilities Ilari Liusvaara
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 23+ messages in thread
From: Ilari Liusvaara @ 2009-12-08 13:16 UTC (permalink / raw)
  To: git

Remote helpers deadlock easily, so support debug mode which shows the
interaction steps.

Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
---
 transport-helper.c |   94 ++++++++++++++++++++++++++++++++++++++--------------
 1 files changed, 69 insertions(+), 25 deletions(-)

diff --git a/transport-helper.c b/transport-helper.c
index 11f3d7e..a721dc2 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -8,6 +8,8 @@
 #include "quote.h"
 #include "remote.h"
 
+static int debug;
+
 struct helper_data
 {
 	const char *name;
@@ -22,6 +24,45 @@ struct helper_data
 	int refspec_nr;
 };
 
+static void sendline(struct helper_data *helper, struct strbuf *buffer)
+{
+	if (debug)
+		fprintf(stderr, "Debug: Remote helper: -> %s", buffer->buf);
+	if (write_in_full(helper->helper->in, buffer->buf, buffer->len)
+		!= buffer->len)
+		die_errno("Full write to remote helper failed");
+}
+
+static int recvline(struct helper_data *helper, struct strbuf *buffer)
+{
+	strbuf_reset(buffer);
+	if (debug)
+		fprintf(stderr, "Debug: Remote helper: Waiting...\n");
+	if (strbuf_getline(buffer, helper->out, '\n') == EOF) {
+		if (debug)
+			fprintf(stderr, "Debug: Remote helper quit.\n");
+		exit(128);
+	}
+
+	if (debug)
+		fprintf(stderr, "Debug: Remote helper: <- %s\n", buffer->buf);
+	return 0;
+}
+
+static void xchgline(struct helper_data *helper, struct strbuf *buffer)
+{
+	sendline(helper, buffer);
+	recvline(helper, buffer);
+}
+
+static void write_constant(int fd, const char *str)
+{
+	if (debug)
+		fprintf(stderr, "Debug: Remote helper: -> %s", str);
+	if (write_in_full(fd, str, strlen(str)) != strlen(str))
+		die_errno("Full write to remote helper failed");
+}
+
 static struct child_process *get_helper(struct transport *transport)
 {
 	struct helper_data *data = transport->data;
@@ -48,15 +89,16 @@ static struct child_process *get_helper(struct transport *transport)
 		die("Unable to run helper: git %s", helper->argv[0]);
 	data->helper = helper;
 
-	write_str_in_full(helper->in, "capabilities\n");
+	write_constant(helper->in, "capabilities\n");
 
 	data->out = xfdopen(helper->out, "r");
 	while (1) {
-		if (strbuf_getline(&buf, data->out, '\n') == EOF)
-			exit(128); /* child died, message supplied already */
+		recvline(data, &buf);
 
 		if (!*buf.buf)
 			break;
+		if (debug)
+			fprintf(stderr, "Debug: Got cap %s\n", buf.buf);
 		if (!strcmp(buf.buf, "fetch"))
 			data->fetch = 1;
 		if (!strcmp(buf.buf, "option"))
@@ -82,14 +124,21 @@ static struct child_process *get_helper(struct transport *transport)
 		free(refspecs);
 	}
 	strbuf_release(&buf);
+	if (debug)
+		fprintf(stderr, "Debug: Capabilities complete.\n");
 	return data->helper;
 }
 
 static int disconnect_helper(struct transport *transport)
 {
 	struct helper_data *data = transport->data;
+	struct strbuf buf = STRBUF_INIT;
+
 	if (data->helper) {
-		write_str_in_full(data->helper->in, "\n");
+		if (debug)
+			fprintf(stderr, "Debug: Disconnecting.\n");
+		strbuf_addf(&buf, "\n");
+		sendline(data, &buf);
 		close(data->helper->in);
 		fclose(data->out);
 		finish_command(data->helper);
@@ -117,10 +166,11 @@ static int set_helper_option(struct transport *transport,
 			  const char *name, const char *value)
 {
 	struct helper_data *data = transport->data;
-	struct child_process *helper = get_helper(transport);
 	struct strbuf buf = STRBUF_INIT;
 	int i, ret, is_bool = 0;
 
+	get_helper(transport);
+
 	if (!data->option)
 		return 1;
 
@@ -143,12 +193,7 @@ static int set_helper_option(struct transport *transport,
 		quote_c_style(value, &buf, NULL, 0);
 	strbuf_addch(&buf, '\n');
 
-	if (write_in_full(helper->in, buf.buf, buf.len) != buf.len)
-		die_errno("cannot send option to %s", data->name);
-
-	strbuf_reset(&buf);
-	if (strbuf_getline(&buf, data->out, '\n') == EOF)
-		exit(128); /* child died, message supplied already */
+	xchgline(data, &buf);
 
 	if (!strcmp(buf.buf, "ok"))
 		ret = 0;
@@ -208,13 +253,10 @@ static int fetch_with_fetch(struct transport *transport,
 	}
 
 	strbuf_addch(&buf, '\n');
-	if (write_in_full(data->helper->in, buf.buf, buf.len) != buf.len)
-		die_errno("cannot send fetch to %s", data->name);
+	sendline(data, &buf);
 
 	while (1) {
-		strbuf_reset(&buf);
-		if (strbuf_getline(&buf, data->out, '\n') == EOF)
-			exit(128); /* child died, message supplied already */
+		recvline(data, &buf);
 
 		if (!prefixcmp(buf.buf, "lock ")) {
 			const char *name = buf.buf + 5;
@@ -249,12 +291,13 @@ static int fetch_with_import(struct transport *transport,
 			     int nr_heads, struct ref **to_fetch)
 {
 	struct child_process fastimport;
-	struct child_process *helper = get_helper(transport);
 	struct helper_data *data = transport->data;
 	int i;
 	struct ref *posn;
 	struct strbuf buf = STRBUF_INIT;
 
+	get_helper(transport);
+
 	if (get_importer(transport, &fastimport))
 		die("Couldn't run fast-import");
 
@@ -264,7 +307,7 @@ static int fetch_with_import(struct transport *transport,
 			continue;
 
 		strbuf_addf(&buf, "import %s\n", posn->name);
-		write_in_full(helper->in, buf.buf, buf.len);
+		sendline(data, &buf);
 		strbuf_reset(&buf);
 	}
 	disconnect_helper(transport);
@@ -369,17 +412,14 @@ static int push_refs(struct transport *transport,
 	}
 
 	strbuf_addch(&buf, '\n');
-	if (write_in_full(helper->in, buf.buf, buf.len) != buf.len)
-		exit(128);
+	sendline(data, &buf);
 
 	ref = remote_refs;
 	while (1) {
 		char *refname, *msg;
 		int status;
 
-		strbuf_reset(&buf);
-		if (strbuf_getline(&buf, data->out, '\n') == EOF)
-			exit(128); /* child died, message supplied already */
+		recvline(data, &buf);
 		if (!buf.len)
 			break;
 
@@ -471,8 +511,7 @@ static struct ref *get_refs_list(struct transport *transport, int for_push)
 
 	while (1) {
 		char *eov, *eon;
-		if (strbuf_getline(&buf, data->out, '\n') == EOF)
-			exit(128); /* child died, message supplied already */
+		recvline(data, &buf);
 
 		if (!*buf.buf)
 			break;
@@ -497,6 +536,8 @@ static struct ref *get_refs_list(struct transport *transport, int for_push)
 		}
 		tail = &((*tail)->next);
 	}
+	if (debug)
+		fprintf(stderr, "Debug: Read ref listing.\n");
 	strbuf_release(&buf);
 
 	for (posn = ret; posn; posn = posn->next)
@@ -510,6 +551,9 @@ int transport_helper_init(struct transport *transport, const char *name)
 	struct helper_data *data = xcalloc(sizeof(*data), 1);
 	data->name = name;
 
+	if (getenv("GIT_TRANSPORT_HELPER_DEBUG"))
+		debug = 1;
+
 	transport->data = data;
 	transport->set_option = set_helper_option;
 	transport->get_refs_list = get_refs_list;
-- 
1.6.6.rc1.300.gfbc27

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [REROLL PATCH 2/8] Support mandatory capabilities
  2009-12-08 13:16 [REROLL PATCH 0/8] Remote helpers smart transport extensions Ilari Liusvaara
  2009-12-08 13:16 ` [REROLL PATCH 1/8] Add remote helper debug mode Ilari Liusvaara
@ 2009-12-08 13:16 ` Ilari Liusvaara
  2009-12-08 23:34   ` Junio C Hamano
  2009-12-08 13:16 ` [REROLL PATCH 3/8] Pass unknown protocols to external protocol handlers Ilari Liusvaara
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 23+ messages in thread
From: Ilari Liusvaara @ 2009-12-08 13:16 UTC (permalink / raw)
  To: git

Add support for marking capability as mandatory for hosting git version
to understand. This is useful for helpers which require various types
of assistance from main git binary.

Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
---
 Documentation/git-remote-helpers.txt |    5 ++++-
 transport-helper.c                   |   26 ++++++++++++++++++++------
 2 files changed, 24 insertions(+), 7 deletions(-)

diff --git a/Documentation/git-remote-helpers.txt b/Documentation/git-remote-helpers.txt
index 5cfdc0c..20a05fe 100644
--- a/Documentation/git-remote-helpers.txt
+++ b/Documentation/git-remote-helpers.txt
@@ -25,7 +25,10 @@ Commands are given by the caller on the helper's standard input, one per line.
 
 'capabilities'::
 	Lists the capabilities of the helper, one per line, ending
-	with a blank line.
+	with a blank line. Each capability may be preceeded with '*'.
+	This marks them mandatory for git version using the remote
+	helper to understand (unknown mandatory capability is fatal
+	error).
 
 'list'::
 	Lists the refs, one per line, in the format "<value> <name>
diff --git a/transport-helper.c b/transport-helper.c
index a721dc2..f977d28 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -93,25 +93,39 @@ static struct child_process *get_helper(struct transport *transport)
 
 	data->out = xfdopen(helper->out, "r");
 	while (1) {
+		const char *capname;
+		int mandatory = 0;
 		recvline(data, &buf);
 
 		if (!*buf.buf)
 			break;
+
+		if (*buf.buf == '*') {
+			capname = buf.buf + 1;
+			mandatory = 1;
+		} else
+			capname = buf.buf;
+
 		if (debug)
-			fprintf(stderr, "Debug: Got cap %s\n", buf.buf);
-		if (!strcmp(buf.buf, "fetch"))
+			fprintf(stderr, "Debug: Got cap %s\n", capname);
+		if (!strcmp(capname, "fetch"))
 			data->fetch = 1;
-		if (!strcmp(buf.buf, "option"))
+		else if (!strcmp(capname, "option"))
 			data->option = 1;
-		if (!strcmp(buf.buf, "push"))
+		else if (!strcmp(capname, "push"))
 			data->push = 1;
-		if (!strcmp(buf.buf, "import"))
+		else if (!strcmp(capname, "import"))
 			data->import = 1;
-		if (!data->refspecs && !prefixcmp(buf.buf, "refspec ")) {
+		else if (!data->refspecs && !prefixcmp(capname, "refspec ")) {
 			ALLOC_GROW(refspecs,
 				   refspec_nr + 1,
 				   refspec_alloc);
 			refspecs[refspec_nr++] = strdup(buf.buf + strlen("refspec "));
+		} else if (mandatory) {
+			fflush(stderr);
+			die("Unknown madatory capability %s. This remote "
+			    "helper probably needs newer version of Git.\n",
+			    capname);
 		}
 	}
 	if (refspecs) {
-- 
1.6.6.rc1.300.gfbc27

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [REROLL PATCH 3/8] Pass unknown protocols to external protocol handlers
  2009-12-08 13:16 [REROLL PATCH 0/8] Remote helpers smart transport extensions Ilari Liusvaara
  2009-12-08 13:16 ` [REROLL PATCH 1/8] Add remote helper debug mode Ilari Liusvaara
  2009-12-08 13:16 ` [REROLL PATCH 2/8] Support mandatory capabilities Ilari Liusvaara
@ 2009-12-08 13:16 ` Ilari Liusvaara
  2009-12-08 23:35   ` Junio C Hamano
  2009-12-08 13:16 ` [REROLL PATCH 4/8] Refactor git transport options parsing Ilari Liusvaara
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 23+ messages in thread
From: Ilari Liusvaara @ 2009-12-08 13:16 UTC (permalink / raw)
  To: git

Change URL handling to allow external protocol handlers to implement
new protocols without the '::' syntax if helper name does not conflict
with any built-in protocol.

foo:// now invokes git-remote-foo with foo:// URL.

Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
---
 transport-helper.c |   22 ++++++++++++-
 transport.c        |   90 +++++++++++++++++++++++++++++++++++++++++++---------
 2 files changed, 96 insertions(+), 16 deletions(-)

diff --git a/transport-helper.c b/transport-helper.c
index f977d28..0e82553 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -63,6 +63,26 @@ static void write_constant(int fd, const char *str)
 		die_errno("Full write to remote helper failed");
 }
 
+const char *remove_ext_force(const char *url)
+{
+	const char *url2 = url;
+	const char *first_colon = NULL;
+
+	if (!url)
+		return NULL;
+
+	while (*url2 && !first_colon)
+		if (*url2 == ':')
+			first_colon = url2;
+		else
+			url2++;
+
+	if (first_colon && first_colon[1] == ':')
+		return first_colon + 2;
+	else
+		return url;
+}
+
 static struct child_process *get_helper(struct transport *transport)
 {
 	struct helper_data *data = transport->data;
@@ -83,7 +103,7 @@ static struct child_process *get_helper(struct transport *transport)
 	strbuf_addf(&buf, "remote-%s", data->name);
 	helper->argv[0] = strbuf_detach(&buf, NULL);
 	helper->argv[1] = transport->remote->name;
-	helper->argv[2] = transport->url;
+	helper->argv[2] = remove_ext_force(transport->url);
 	helper->git_cmd = 1;
 	if (start_command(helper))
 		die("Unable to run helper: git %s", helper->argv[0]);
diff --git a/transport.c b/transport.c
index 3eea836..e42a82b 100644
--- a/transport.c
+++ b/transport.c
@@ -780,6 +780,58 @@ static int is_file(const char *url)
 	return S_ISREG(buf.st_mode);
 }
 
+static const char *strchrc(const char *str, int c)
+{
+	while (*str)
+		if (*str == c)
+			return str;
+		else
+			str++;
+	return NULL;
+}
+
+static int is_url(const char *url)
+{
+	const char *url2, *first_slash;
+
+	if (!url)
+		return 0;
+	url2 = url;
+	first_slash = strchrc(url, '/');
+
+	/* Input with no slash at all or slash first can't be URL. */
+	if (!first_slash || first_slash == url)
+		return 0;
+	/* Character before must be : and next must be /. */
+	if (first_slash[-1] != ':' || first_slash[1] != '/')
+		return 0;
+	/* There must be something before the :// */
+	if (first_slash == url + 1)
+		return 0;
+	/*
+	 * Check all characters up to first slash. Only alpha, num and
+	 * colon (":") are allowed. ":" must be followed by ":" or "/".
+	 */
+	url2 = url;
+	while (url2 < first_slash) {
+		if (*url2 != ':' && !isalnum((unsigned char)*url2))
+			return 0;
+		if (*url2 == ':' && url2[1] != ':' && url2[1] != '/')
+			return 0;
+		if (*url2 == ':')
+			url2++;		/* Skip second : */
+		url2++;
+	}
+
+	/* Valid enough. */
+	return 1;
+}
+
+static int external_specification_len(const char *url)
+{
+	return strchrc(url, ':') - url;
+}
+
 struct transport *transport_get(struct remote *remote, const char *url)
 {
 	struct transport *ret = xcalloc(1, sizeof(*ret));
@@ -805,30 +857,23 @@ struct transport *transport_get(struct remote *remote, const char *url)
 
 	if (remote && remote->foreign_vcs) {
 		transport_helper_init(ret, remote->foreign_vcs);
-		return ret;
-	}
-
-	if (!prefixcmp(url, "rsync:")) {
+	} else if (!prefixcmp(url, "rsync:")) {
 		ret->get_refs_list = get_refs_via_rsync;
 		ret->fetch = fetch_objs_via_rsync;
 		ret->push = rsync_transport_push;
-
-	} else if (!prefixcmp(url, "http://")
-	        || !prefixcmp(url, "https://")
-	        || !prefixcmp(url, "ftp://")) {
-		transport_helper_init(ret, "curl");
-#ifdef NO_CURL
-		error("git was compiled without libcurl support.");
-#endif
-
 	} else if (is_local(url) && is_file(url)) {
 		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;
-
-	} else {
+	} else if (!is_url(url)
+		|| !prefixcmp(url, "file://")
+		|| !prefixcmp(url, "git://")
+		|| !prefixcmp(url, "ssh://")
+		|| !prefixcmp(url, "git+ssh://")
+		|| !prefixcmp(url, "ssh+git://")) {
+		/* These are builtin smart transports. */
 		struct git_transport_data *data = xcalloc(1, sizeof(*data));
 		ret->data = data;
 		ret->set_option = set_git_option;
@@ -845,6 +890,21 @@ struct transport *transport_get(struct remote *remote, const char *url)
 		data->receivepack = "git-receive-pack";
 		if (remote->receivepack)
 			data->receivepack = remote->receivepack;
+	} else if (!prefixcmp(url, "http://")
+		|| !prefixcmp(url, "https://")
+		|| !prefixcmp(url, "ftp://")) {
+		/* These three are just plain special. */
+		transport_helper_init(ret, "curl");
+#ifdef NO_CURL
+		error("git was compiled without libcurl support.");
+#endif
+	} 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);
 	}
 
 	return ret;
-- 
1.6.6.rc1.300.gfbc27

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [REROLL PATCH 4/8] Refactor git transport options parsing
  2009-12-08 13:16 [REROLL PATCH 0/8] Remote helpers smart transport extensions Ilari Liusvaara
                   ` (2 preceding siblings ...)
  2009-12-08 13:16 ` [REROLL PATCH 3/8] Pass unknown protocols to external protocol handlers Ilari Liusvaara
@ 2009-12-08 13:16 ` Ilari Liusvaara
  2009-12-08 13:16 ` [REROLL PATCH 5/8] Support taking over transports Ilari Liusvaara
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 23+ messages in thread
From: Ilari Liusvaara @ 2009-12-08 13:16 UTC (permalink / raw)
  To: git

Refactor the transport options parsing so that protocols that aren't
directly smart transports (file://, git://, ssh:// & co) can record
the smart transport options for the case if it turns that transport
can actually be smart.

Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
---
 transport.c |   78 +++++++++++++++++++++++++++++++++++-----------------------
 transport.h |   15 +++++++++++
 2 files changed, 62 insertions(+), 31 deletions(-)

diff --git a/transport.c b/transport.c
index e42a82b..b3e22ec 100644
--- a/transport.c
+++ b/transport.c
@@ -395,41 +395,35 @@ static int close_bundle(struct transport *transport)
 }
 
 struct git_transport_data {
-	unsigned thin : 1;
-	unsigned keep : 1;
-	unsigned followtags : 1;
-	int depth;
+	struct git_transport_options options;
 	struct child_process *conn;
 	int fd[2];
-	const char *uploadpack;
-	const char *receivepack;
 	struct extra_have_objects extra_have;
 };
 
-static int set_git_option(struct transport *connection,
+static int set_git_option(struct git_transport_options *opts,
 			  const char *name, const char *value)
 {
-	struct git_transport_data *data = connection->data;
 	if (!strcmp(name, TRANS_OPT_UPLOADPACK)) {
-		data->uploadpack = value;
+		opts->uploadpack = value;
 		return 0;
 	} else if (!strcmp(name, TRANS_OPT_RECEIVEPACK)) {
-		data->receivepack = value;
+		opts->receivepack = value;
 		return 0;
 	} else if (!strcmp(name, TRANS_OPT_THIN)) {
-		data->thin = !!value;
+		opts->thin = !!value;
 		return 0;
 	} else if (!strcmp(name, TRANS_OPT_FOLLOWTAGS)) {
-		data->followtags = !!value;
+		opts->followtags = !!value;
 		return 0;
 	} else if (!strcmp(name, TRANS_OPT_KEEP)) {
-		data->keep = !!value;
+		opts->keep = !!value;
 		return 0;
 	} else if (!strcmp(name, TRANS_OPT_DEPTH)) {
 		if (!value)
-			data->depth = 0;
+			opts->depth = 0;
 		else
-			data->depth = atoi(value);
+			opts->depth = atoi(value);
 		return 0;
 	}
 	return 1;
@@ -439,7 +433,8 @@ static int connect_setup(struct transport *transport, int for_push, int verbose)
 {
 	struct git_transport_data *data = transport->data;
 	data->conn = git_connect(data->fd, transport->url,
-				 for_push ? data->receivepack : data->uploadpack,
+				 for_push ? data->options.receivepack :
+				 data->options.uploadpack,
 				 verbose ? CONNECT_VERBOSE : 0);
 	return 0;
 }
@@ -469,15 +464,15 @@ static int fetch_refs_via_pack(struct transport *transport,
 	struct ref *refs_tmp = NULL;
 
 	memset(&args, 0, sizeof(args));
-	args.uploadpack = data->uploadpack;
-	args.keep_pack = data->keep;
+	args.uploadpack = data->options.uploadpack;
+	args.keep_pack = data->options.keep;
 	args.lock_pack = 1;
-	args.use_thin_pack = data->thin;
-	args.include_tag = data->followtags;
+	args.use_thin_pack = data->options.thin;
+	args.include_tag = data->options.followtags;
 	args.verbose = (transport->verbose > 0);
 	args.quiet = (transport->verbose < 0);
 	args.no_progress = args.quiet || (!transport->progress && !isatty(1));
-	args.depth = data->depth;
+	args.depth = data->options.depth;
 
 	for (i = 0; i < nr_heads; i++)
 		origh[i] = heads[i] = xstrdup(to_fetch[i]->name);
@@ -734,7 +729,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
 	memset(&args, 0, sizeof(args));
 	args.send_mirror = !!(flags & TRANSPORT_PUSH_MIRROR);
 	args.force_update = !!(flags & TRANSPORT_PUSH_FORCE);
-	args.use_thin_pack = data->thin;
+	args.use_thin_pack = data->options.thin;
 	args.verbose = !!(flags & TRANSPORT_PUSH_VERBOSE);
 	args.quiet = !!(flags & TRANSPORT_PUSH_QUIET);
 	args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);
@@ -861,12 +856,14 @@ struct transport *transport_get(struct remote *remote, const char *url)
 		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 (is_local(url) && is_file(url)) {
 		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)
 		|| !prefixcmp(url, "file://")
 		|| !prefixcmp(url, "git://")
@@ -876,20 +873,14 @@ struct transport *transport_get(struct remote *remote, const char *url)
 		/* These are builtin smart transports. */
 		struct git_transport_data *data = xcalloc(1, sizeof(*data));
 		ret->data = data;
-		ret->set_option = set_git_option;
+		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->disconnect = disconnect_git;
+		ret->smart_options = &(data->options);
 
-		data->thin = 1;
 		data->conn = NULL;
-		data->uploadpack = "git-upload-pack";
-		if (remote->uploadpack)
-			data->uploadpack = remote->uploadpack;
-		data->receivepack = "git-receive-pack";
-		if (remote->receivepack)
-			data->receivepack = remote->receivepack;
 	} else if (!prefixcmp(url, "http://")
 		|| !prefixcmp(url, "https://")
 		|| !prefixcmp(url, "ftp://")) {
@@ -907,14 +898,39 @@ struct transport *transport_get(struct remote *remote, const char *url)
 		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;
 }
 
 int transport_set_option(struct transport *transport,
 			 const char *name, const char *value)
 {
+	int git_reports = 1, protocol_reports = 1;
+
+	if (transport->smart_options)
+		git_reports = set_git_option(transport->smart_options,
+					     name, value);
+
 	if (transport->set_option)
-		return transport->set_option(transport, name, value);
+		protocol_reports = transport->set_option(transport, name,
+							value);
+
+	/* If either report is 0, report 0 (success). */
+	if (!git_reports || !protocol_reports)
+		return 0;
+	/* If either reports -1 (invalid value), report -1. */
+	if ((git_reports == -1) || (protocol_reports == -1))
+		return -1;
+	/* Otherwise if both report unknown, report unknown. */
 	return 1;
 }
 
diff --git a/transport.h b/transport.h
index 9e74406..e90c285 100644
--- a/transport.h
+++ b/transport.h
@@ -4,6 +4,15 @@
 #include "cache.h"
 #include "remote.h"
 
+struct git_transport_options {
+	unsigned thin : 1;
+	unsigned keep : 1;
+	unsigned followtags : 1;
+	int depth;
+	const char *uploadpack;
+	const char *receivepack;
+};
+
 struct transport {
 	struct remote *remote;
 	const char *url;
@@ -65,6 +74,12 @@ struct transport {
 	signed verbose : 3;
 	/* Force progress even if the output is not a tty */
 	unsigned progress : 1;
+	/*
+	 * If transport is at least potentially smart, this points to
+	 * git_transport_options structure to use in case transport
+	 * actually turns out to be smart.
+	 */
+	struct git_transport_options *smart_options;
 };
 
 #define TRANSPORT_PUSH_ALL 1
-- 
1.6.6.rc1.300.gfbc27

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [REROLL PATCH 5/8] Support taking over transports
  2009-12-08 13:16 [REROLL PATCH 0/8] Remote helpers smart transport extensions Ilari Liusvaara
                   ` (3 preceding siblings ...)
  2009-12-08 13:16 ` [REROLL PATCH 4/8] Refactor git transport options parsing Ilari Liusvaara
@ 2009-12-08 13:16 ` Ilari Liusvaara
  2009-12-08 23:37   ` Junio C Hamano
  2009-12-08 13:16 ` [REROLL PATCH 6/8] Support remote helpers implementing smart transports Ilari Liusvaara
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 23+ messages in thread
From: Ilari Liusvaara @ 2009-12-08 13:16 UTC (permalink / raw)
  To: git

Add support for taking over transports that turn out to be smart.

Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
---
 transport-helper.c |   16 +++++++++++++++-
 transport.c        |   51 ++++++++++++++++++++++++++++++++++++++++++++++-----
 transport.h        |    2 ++
 3 files changed, 63 insertions(+), 6 deletions(-)

diff --git a/transport-helper.c b/transport-helper.c
index 0e82553..3b7340c 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -22,6 +22,7 @@ struct helper_data
 	/* These go from remote name (as in "list") to private name */
 	struct refspec *refspecs;
 	int refspec_nr;
+	struct git_transport_options gitoptions;
 };
 
 static void sendline(struct helper_data *helper, struct strbuf *buffer)
@@ -91,6 +92,7 @@ static struct child_process *get_helper(struct transport *transport)
 	const char **refspecs = NULL;
 	int refspec_nr = 0;
 	int refspec_alloc = 0;
+	int duped;
 
 	if (data->helper)
 		return data->helper;
@@ -109,9 +111,19 @@ static struct child_process *get_helper(struct transport *transport)
 		die("Unable to run helper: git %s", helper->argv[0]);
 	data->helper = helper;
 
+	/*
+	 * Open the output as FILE* so strbuf_getline() can be used.
+	 * Do this with duped fd because fclose() will close the fd,
+	 * and stuff like taking over will require the fd to remain.
+	 *
+	 */
+	duped = dup(helper->out);
+	if (duped < 0)
+		die_errno("Can't dup helper output fd");
+	data->out = xfdopen(duped, "r");
+
 	write_constant(helper->in, "capabilities\n");
 
-	data->out = xfdopen(helper->out, "r");
 	while (1) {
 		const char *capname;
 		int mandatory = 0;
@@ -174,6 +186,7 @@ static int disconnect_helper(struct transport *transport)
 		strbuf_addf(&buf, "\n");
 		sendline(data, &buf);
 		close(data->helper->in);
+		close(data->helper->out);
 		fclose(data->out);
 		finish_command(data->helper);
 		free((char *)data->helper->argv[0]);
@@ -594,5 +607,6 @@ int transport_helper_init(struct transport *transport, const char *name)
 	transport->fetch = fetch;
 	transport->push_refs = push_refs;
 	transport->disconnect = release_helper;
+	transport->smart_options = &(data->gitoptions);
 	return 0;
 }
diff --git a/transport.c b/transport.c
index b3e22ec..c4ecec4 100644
--- a/transport.c
+++ b/transport.c
@@ -398,6 +398,7 @@ struct git_transport_data {
 	struct git_transport_options options;
 	struct child_process *conn;
 	int fd[2];
+	unsigned got_remote_heads : 1;
 	struct extra_have_objects extra_have;
 };
 
@@ -432,10 +433,15 @@ static int set_git_option(struct git_transport_options *opts,
 static int connect_setup(struct transport *transport, int for_push, int verbose)
 {
 	struct git_transport_data *data = transport->data;
+
+	if (data->conn)
+		return 0;
+
 	data->conn = git_connect(data->fd, transport->url,
 				 for_push ? data->options.receivepack :
 				 data->options.uploadpack,
 				 verbose ? CONNECT_VERBOSE : 0);
+
 	return 0;
 }
 
@@ -447,6 +453,7 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus
 	connect_setup(transport, for_push, 0);
 	get_remote_heads(data->fd[0], &refs, 0, NULL,
 			 for_push ? REF_NORMAL : 0, &data->extra_have);
+	data->got_remote_heads = 1;
 
 	return refs;
 }
@@ -477,9 +484,10 @@ static int fetch_refs_via_pack(struct transport *transport,
 	for (i = 0; i < nr_heads; i++)
 		origh[i] = heads[i] = xstrdup(to_fetch[i]->name);
 
-	if (!data->conn) {
+	if (!data->got_remote_heads) {
 		connect_setup(transport, 0, 0);
 		get_remote_heads(data->fd[0], &refs_tmp, 0, NULL, 0, NULL);
+		data->got_remote_heads = 1;
 	}
 
 	refs = fetch_pack(&args, data->fd, data->conn,
@@ -490,6 +498,7 @@ static int fetch_refs_via_pack(struct transport *transport,
 	if (finish_connect(data->conn))
 		refs = NULL;
 	data->conn = NULL;
+	data->got_remote_heads = 0;
 
 	free_refs(refs_tmp);
 
@@ -718,12 +727,13 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
 	struct send_pack_args args;
 	int ret;
 
-	if (!data->conn) {
+	if (!data->got_remote_heads) {
 		struct ref *tmp_refs;
 		connect_setup(transport, 1, 0);
 
 		get_remote_heads(data->fd[0], &tmp_refs, 0, NULL, REF_NORMAL,
 				 NULL);
+		data->got_remote_heads = 1;
 	}
 
 	memset(&args, 0, sizeof(args));
@@ -741,6 +751,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
 	close(data->fd[0]);
 	ret |= finish_connect(data->conn);
 	data->conn = NULL;
+	data->got_remote_heads = 0;
 
 	return ret;
 }
@@ -749,7 +760,8 @@ static int disconnect_git(struct transport *transport)
 {
 	struct git_transport_data *data = transport->data;
 	if (data->conn) {
-		packet_flush(data->fd[1]);
+		if (data->got_remote_heads)
+			packet_flush(data->fd[1]);
 		close(data->fd[0]);
 		close(data->fd[1]);
 		finish_connect(data->conn);
@@ -759,6 +771,32 @@ static int disconnect_git(struct transport *transport)
 	return 0;
 }
 
+void transport_take_over(struct transport *transport,
+			 struct child_process *child)
+{
+	struct git_transport_data *data;
+
+	if (!transport->smart_options)
+		die("Bug detected: Taking over transport requires non-NULL "
+		    "smart_options field.");
+
+	data = xcalloc(1, sizeof(*data));
+	data->options = *transport->smart_options;
+	data->conn = child;
+	data->fd[0] = data->conn->out;
+	data->fd[1] = data->conn->in;
+	data->got_remote_heads = 0;
+	transport->data = data;
+
+	transport->set_option = NULL;
+	transport->get_refs_list = get_refs_via_connect;
+	transport->fetch = fetch_refs_via_pack;
+	transport->push = NULL;
+	transport->push_refs = git_transport_push;
+	transport->disconnect = disconnect_git;
+	transport->smart_options = &(data->options);
+}
+
 static int is_local(const char *url)
 {
 	const char *colon = strchr(url, ':');
@@ -881,6 +919,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
 		ret->smart_options = &(data->options);
 
 		data->conn = NULL;
+		data->got_remote_heads = 0;
 	} else if (!prefixcmp(url, "http://")
 		|| !prefixcmp(url, "https://")
 		|| !prefixcmp(url, "ftp://")) {
@@ -941,9 +980,9 @@ int transport_push(struct transport *transport,
 	*nonfastforward = 0;
 	verify_remote_names(refspec_nr, refspec);
 
-	if (transport->push)
+	if (transport->push) {
 		return transport->push(transport, refspec_nr, refspec, flags);
-	if (transport->push_refs) {
+	} else if (transport->push_refs) {
 		struct ref *remote_refs =
 			transport->get_refs_list(transport, 1);
 		struct ref *local_refs = get_local_heads();
@@ -987,6 +1026,7 @@ const struct ref *transport_get_remote_refs(struct transport *transport)
 {
 	if (!transport->remote_refs)
 		transport->remote_refs = transport->get_refs_list(transport, 0);
+
 	return transport->remote_refs;
 }
 
@@ -1021,6 +1061,7 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs)
 	}
 
 	rc = transport->fetch(transport, nr_heads, heads);
+
 	free(heads);
 	return rc;
 }
diff --git a/transport.h b/transport.h
index e90c285..781db2e 100644
--- a/transport.h
+++ b/transport.h
@@ -130,6 +130,8 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs);
 void transport_unlock_pack(struct transport *transport);
 int transport_disconnect(struct transport *transport);
 char *transport_anonymize_url(const char *url);
+void transport_take_over(struct transport *transport,
+			 struct child_process *child);
 
 /* Transport methods defined outside transport.c */
 int transport_helper_init(struct transport *transport, const char *name);
-- 
1.6.6.rc1.300.gfbc27

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [REROLL PATCH 6/8] Support remote helpers implementing smart transports
  2009-12-08 13:16 [REROLL PATCH 0/8] Remote helpers smart transport extensions Ilari Liusvaara
                   ` (4 preceding siblings ...)
  2009-12-08 13:16 ` [REROLL PATCH 5/8] Support taking over transports Ilari Liusvaara
@ 2009-12-08 13:16 ` Ilari Liusvaara
  2009-12-08 23:38   ` Junio C Hamano
  2009-12-08 13:16 ` [REROLL PATCH 7/8] Support remote archive from external protocol helpers Ilari Liusvaara
  2009-12-08 13:16 ` [REROLL PATCH 8/8] Remove special casing of http, https and ftp Ilari Liusvaara
  7 siblings, 1 reply; 23+ messages in thread
From: Ilari Liusvaara @ 2009-12-08 13:16 UTC (permalink / raw)
  To: git

Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
---
 Documentation/git-remote-helpers.txt |   25 +++++++-
 transport-helper.c                   |  126 ++++++++++++++++++++++++++++++++--
 2 files changed, 144 insertions(+), 7 deletions(-)

diff --git a/Documentation/git-remote-helpers.txt b/Documentation/git-remote-helpers.txt
index 20a05fe..b957813 100644
--- a/Documentation/git-remote-helpers.txt
+++ b/Documentation/git-remote-helpers.txt
@@ -93,6 +93,20 @@ Supported if the helper has the "push" capability.
 +
 Supported if the helper has the "import" capability.
 
+'connect' <service>::
+	Connects to given service. Stdin and stdout of helper are
+	connected to specified service (git prefix is included in service
+	name so e.g. fetching uses 'git-upload-pack' as service) on
+	remote side. Valid replies to this command are empty line
+	(connection established), 'fallback' (no smart transport support,
+	fall back to dumb transports) and just exiting with error message
+	printed (can't connect, don't bother trying to fall back). After
+	line feed terminating the positive (empty) response, the output
+	of service starts. After the connection ends, the remote
+	helper exits.
++
+Supported if the helper has the "connect" capability.
+
 If a fatal error occurs, the program writes the error message to
 stderr and exits. The caller should expect that a suitable error
 message has been printed if the child closes the connection without
@@ -126,6 +140,9 @@ CAPABILITIES
 	all, it must cover all refs reported by the list command; if
 	it is not used, it is effectively "*:*"
 
+'connect'::
+	This helper supports the 'connect' command.
+
 REF LIST ATTRIBUTES
 -------------------
 
@@ -168,9 +185,15 @@ OPTIONS
 	but don't actually change any repository data.	For most
 	helpers this only applies to the 'push', if supported.
 
+'option servpath <c-style-quoted-path>'::
+	Set service path (--upload-pack, --receive-pack etc.) for
+	next connect. Remote helper MAY support this option. Remote
+	helper MUST NOT rely on this option being set before
+	connect request occurs.
+
 Documentation
 -------------
-Documentation by Daniel Barkalow.
+Documentation by Daniel Barkalow and Ilari Liusvaara
 
 GIT
 ---
diff --git a/transport-helper.c b/transport-helper.c
index 3b7340c..e30c914 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -18,7 +18,9 @@ struct helper_data
 	unsigned fetch : 1,
 		import : 1,
 		option : 1,
-		push : 1;
+		push : 1,
+		connect : 1,
+		no_disconnect_req : 1;
 	/* These go from remote name (as in "list") to private name */
 	struct refspec *refspecs;
 	int refspec_nr;
@@ -34,12 +36,12 @@ static void sendline(struct helper_data *helper, struct strbuf *buffer)
 		die_errno("Full write to remote helper failed");
 }
 
-static int recvline(struct helper_data *helper, struct strbuf *buffer)
+static int _recvline(FILE *helper, struct strbuf *buffer)
 {
 	strbuf_reset(buffer);
 	if (debug)
 		fprintf(stderr, "Debug: Remote helper: Waiting...\n");
-	if (strbuf_getline(buffer, helper->out, '\n') == EOF) {
+	if (strbuf_getline(buffer, helper, '\n') == EOF) {
 		if (debug)
 			fprintf(stderr, "Debug: Remote helper quit.\n");
 		exit(128);
@@ -50,6 +52,11 @@ static int recvline(struct helper_data *helper, struct strbuf *buffer)
 	return 0;
 }
 
+static int recvline(struct helper_data *helper, struct strbuf *buffer)
+{
+	return _recvline(helper->out, buffer);
+}
+
 static void xchgline(struct helper_data *helper, struct strbuf *buffer)
 {
 	sendline(helper, buffer);
@@ -84,6 +91,15 @@ const char *remove_ext_force(const char *url)
 		return url;
 }
 
+static void do_take_over(struct transport *transport)
+{
+	struct helper_data *data;
+	data = (struct helper_data*)transport->data;
+	transport_take_over(transport, data->helper);
+	fclose(data->out);
+	free(data);
+}
+
 static struct child_process *get_helper(struct transport *transport)
 {
 	struct helper_data *data = transport->data;
@@ -110,12 +126,12 @@ static struct child_process *get_helper(struct transport *transport)
 	if (start_command(helper))
 		die("Unable to run helper: git %s", helper->argv[0]);
 	data->helper = helper;
+	data->no_disconnect_req = 0;
 
 	/*
 	 * Open the output as FILE* so strbuf_getline() can be used.
 	 * Do this with duped fd because fclose() will close the fd,
 	 * and stuff like taking over will require the fd to remain.
-	 *
 	 */
 	duped = dup(helper->out);
 	if (duped < 0)
@@ -153,6 +169,8 @@ static struct child_process *get_helper(struct transport *transport)
 				   refspec_nr + 1,
 				   refspec_alloc);
 			refspecs[refspec_nr++] = strdup(buf.buf + strlen("refspec "));
+		} else if (!strcmp(capname, "connect")) {
+			data->connect = 1;
 		} else if (mandatory) {
 			fflush(stderr);
 			die("Unknown madatory capability %s. This remote "
@@ -183,8 +201,10 @@ static int disconnect_helper(struct transport *transport)
 	if (data->helper) {
 		if (debug)
 			fprintf(stderr, "Debug: Disconnecting.\n");
-		strbuf_addf(&buf, "\n");
-		sendline(data, &buf);
+		if(!data->no_disconnect_req) {
+			strbuf_addf(&buf, "\n");
+			sendline(data, &buf);
+		}
 		close(data->helper->in);
 		close(data->helper->out);
 		fclose(data->out);
@@ -378,12 +398,96 @@ static int fetch_with_import(struct transport *transport,
 	return 0;
 }
 
+static int _process_connect(struct transport *transport,
+				      const char *name, const char *exec)
+{
+	struct helper_data *data = transport->data;
+	struct strbuf cmdbuf = STRBUF_INIT;
+	struct child_process *helper;
+	int r, duped, ret = 0;
+	FILE *input;
+
+	helper = get_helper(transport);
+
+	/*
+	 * Yes, dup the pipe another time, as we need unbuffered version
+	 * of input pipe as FILE*. fclose() closes the underlying fd and
+	 * stream buffering only can be changed before first I/O operation
+	 * on it.
+	 */
+	duped = dup(helper->out);
+	if (duped < 0)
+		die_errno("Can't dup helper output fd");
+	input = xfdopen(duped, "r");
+	setvbuf(input, NULL, _IONBF, 0);
+
+	/*
+	 * Handle --upload-pack and friends. This is fire and forget...
+	 * just warn if it fails.
+	 */
+	if (strcmp(name, exec)) {
+		r = set_helper_option(transport, "servpath", exec);
+		if (r > 0)
+			fprintf(stderr, "Warning: Setting remote service path "
+				"not supported by protocol.\n");
+		else if (r < 0)
+			fprintf(stderr, "Warning: Invalid remote service "
+				"path.\n");
+	}
+
+	if (data->connect)
+		strbuf_addf(&cmdbuf, "connect %s\n", name);
+	else
+		goto exit;
+
+	sendline(data, &cmdbuf);
+	_recvline(input, &cmdbuf);
+	if (!strcmp(cmdbuf.buf, "")) {
+		data->no_disconnect_req = 1;
+		if (debug)
+			fprintf(stderr, "Debug: Smart transport connection "
+				"ready.\n");
+		ret = 1;
+	} else if (!strcmp(cmdbuf.buf, "fallback")) {
+		if (debug)
+			fprintf(stderr, "Debug: Falling back to dumb "
+				"transport.\n");
+	} else
+		die("Unknown response to connect: %s",
+			cmdbuf.buf);
+
+exit:
+	fclose(input);
+	return ret;
+}
+
+static int process_connect(struct transport *transport,
+				     int for_push)
+{
+	struct helper_data *data = transport->data;
+	const char *name;
+	const char *exec;
+
+	name = for_push ? "git-receive-pack" : "git-upload-pack";
+	if (for_push)
+		exec = data->gitoptions.receivepack;
+	else
+		exec = data->gitoptions.uploadpack;
+
+	return _process_connect(transport, name, exec);
+}
+
 static int fetch(struct transport *transport,
 		 int nr_heads, struct ref **to_fetch)
 {
 	struct helper_data *data = transport->data;
 	int i, count;
 
+	if (process_connect(transport, 0)) {
+		do_take_over(transport);
+		return transport->fetch(transport, nr_heads, to_fetch);
+	}
+
 	count = 0;
 	for (i = 0; i < nr_heads; i++)
 		if (!(to_fetch[i]->status & REF_STATUS_UPTODATE))
@@ -411,6 +515,11 @@ static int push_refs(struct transport *transport,
 	struct child_process *helper;
 	struct ref *ref;
 
+	if (process_connect(transport, 1)) {
+		do_take_over(transport);
+		return transport->push_refs(transport, remote_refs, flags);
+	}
+
 	if (!remote_refs)
 		return 0;
 
@@ -551,6 +660,11 @@ static struct ref *get_refs_list(struct transport *transport, int for_push)
 
 	helper = get_helper(transport);
 
+	if (process_connect(transport, for_push)) {
+		do_take_over(transport);
+		return transport->get_refs_list(transport, for_push);
+	}
+
 	if (data->push && for_push)
 		write_str_in_full(helper->in, "list for-push\n");
 	else
-- 
1.6.6.rc1.300.gfbc27

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [REROLL PATCH 7/8] Support remote archive from external protocol helpers
  2009-12-08 13:16 [REROLL PATCH 0/8] Remote helpers smart transport extensions Ilari Liusvaara
                   ` (5 preceding siblings ...)
  2009-12-08 13:16 ` [REROLL PATCH 6/8] Support remote helpers implementing smart transports Ilari Liusvaara
@ 2009-12-08 13:16 ` Ilari Liusvaara
  2009-12-08 23:39   ` Junio C Hamano
  2009-12-08 13:16 ` [REROLL PATCH 8/8] Remove special casing of http, https and ftp Ilari Liusvaara
  7 siblings, 1 reply; 23+ messages in thread
From: Ilari Liusvaara @ 2009-12-08 13:16 UTC (permalink / raw)
  To: git

Helpers which support connect also should support remote archive
snapshot (or at least there's only one way to attempt it). So support
remote snapshotting for protocol helpers.

Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
---
 builtin-archive.c  |   17 ++++++++++-------
 transport-helper.c |   19 +++++++++++++++++++
 transport.c        |   21 +++++++++++++++++++++
 transport.h        |    5 +++++
 4 files changed, 55 insertions(+), 7 deletions(-)

diff --git a/builtin-archive.c b/builtin-archive.c
index 12351e9..d34b3fd 100644
--- a/builtin-archive.c
+++ b/builtin-archive.c
@@ -5,6 +5,7 @@
 #include "cache.h"
 #include "builtin.h"
 #include "archive.h"
+#include "transport.h"
 #include "parse-options.h"
 #include "pkt-line.h"
 #include "sideband.h"
@@ -25,12 +26,16 @@ static void create_output_file(const char *output_file)
 static int run_remote_archiver(int argc, const char **argv,
 			       const char *remote, const char *exec)
 {
-	char *url, buf[LARGE_PACKET_MAX];
+	char buf[LARGE_PACKET_MAX];
 	int fd[2], i, len, rv;
-	struct child_process *conn;
+	struct transport *transport;
+	struct remote *_remote;
 
-	url = xstrdup(remote);
-	conn = git_connect(fd, url, exec, 0);
+	_remote = remote_get(remote);
+	if (!_remote->url[0])
+		die("git archive: Remote with no URL");
+	transport = transport_get(_remote, _remote->url[0]);
+	transport_connect(transport, "git-upload-archive", exec, fd);
 
 	for (i = 1; i < argc; i++)
 		packet_write(fd[1], "argument %s\n", argv[i]);
@@ -53,9 +58,7 @@ static int run_remote_archiver(int argc, const char **argv,
 
 	/* Now, start reading from fd[0] and spit it out to stdout */
 	rv = recv_sideband("archive", fd[0], 1);
-	close(fd[0]);
-	close(fd[1]);
-	rv |= finish_connect(conn);
+	rv |= transport_disconnect(transport);
 
 	return !!rv;
 }
diff --git a/transport-helper.c b/transport-helper.c
index e30c914..70c798e 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -477,6 +477,24 @@ static int process_connect(struct transport *transport,
 	return _process_connect(transport, name, exec);
 }
 
+static int connect_helper(struct transport *transport, const char *name,
+		   const char *exec, int fd[2])
+{
+	struct helper_data *data = transport->data;
+
+	/* Get_helper so connect is inited. */
+	get_helper(transport);
+	if (!data->connect)
+		die("Operation not supported by protocol.");
+
+	if (!_process_connect(transport, name, exec))
+		die("Can't connect to subservice %s.", name);
+
+	fd[0] = data->helper->out;
+	fd[1] = data->helper->in;
+	return 0;
+}
+
 static int fetch(struct transport *transport,
 		 int nr_heads, struct ref **to_fetch)
 {
@@ -721,6 +739,7 @@ int transport_helper_init(struct transport *transport, const char *name)
 	transport->fetch = fetch;
 	transport->push_refs = push_refs;
 	transport->disconnect = release_helper;
+	transport->connect = connect_helper;
 	transport->smart_options = &(data->gitoptions);
 	return 0;
 }
diff --git a/transport.c b/transport.c
index c4ecec4..64938fd 100644
--- a/transport.c
+++ b/transport.c
@@ -756,6 +756,17 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
 	return ret;
 }
 
+static int connect_git(struct transport *transport, const char *name,
+		       const char *executable, int fd[2])
+{
+	struct git_transport_data *data = transport->data;
+	data->conn = git_connect(data->fd, transport->url,
+				 executable, 0);
+	fd[0] = data->fd[0];
+	fd[1] = data->fd[1];
+	return 0;
+}
+
 static int disconnect_git(struct transport *transport)
 {
 	struct git_transport_data *data = transport->data;
@@ -915,6 +926,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
 		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);
 
@@ -1075,6 +1087,15 @@ void transport_unlock_pack(struct transport *transport)
 	}
 }
 
+int transport_connect(struct transport *transport, const char *name,
+		      const char *exec, int fd[2])
+{
+	if (transport->connect)
+		return transport->connect(transport, name, exec, fd);
+	else
+		die("Operation not supported by protocol");
+}
+
 int transport_disconnect(struct transport *transport)
 {
 	int ret = 0;
diff --git a/transport.h b/transport.h
index 781db2e..97ba251 100644
--- a/transport.h
+++ b/transport.h
@@ -64,6 +64,8 @@ struct transport {
 	 **/
 	int (*push_refs)(struct transport *transport, struct ref *refs, int flags);
 	int (*push)(struct transport *connection, int refspec_nr, const char **refspec, int flags);
+	int (*connect)(struct transport *connection, const char *name,
+		       const char *executable, int fd[2]);
 
 	/** get_refs_list(), fetch(), and push_refs() can keep
 	 * resources (such as a connection) reserved for futher
@@ -133,6 +135,9 @@ char *transport_anonymize_url(const char *url);
 void transport_take_over(struct transport *transport,
 			 struct child_process *child);
 
+int transport_connect(struct transport *transport, const char *name,
+		      const char *exec, int fd[2]);
+
 /* Transport methods defined outside transport.c */
 int transport_helper_init(struct transport *transport, const char *name);
 
-- 
1.6.6.rc1.300.gfbc27

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [REROLL PATCH 8/8] Remove special casing of http, https and ftp
  2009-12-08 13:16 [REROLL PATCH 0/8] Remote helpers smart transport extensions Ilari Liusvaara
                   ` (6 preceding siblings ...)
  2009-12-08 13:16 ` [REROLL PATCH 7/8] Support remote archive from external protocol helpers Ilari Liusvaara
@ 2009-12-08 13:16 ` Ilari Liusvaara
  7 siblings, 0 replies; 23+ messages in thread
From: Ilari Liusvaara @ 2009-12-08 13:16 UTC (permalink / raw)
  To: git

HTTP, HTTPS and FTP are no longer special to transport code. Also
add support for FTPS (curl supports it so it is easy).

Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
---
 .gitignore  |    4 ++++
 Makefile    |   24 ++++++++++++++++++++++--
 transport.c |    8 --------
 3 files changed, 26 insertions(+), 10 deletions(-)

diff --git a/.gitignore b/.gitignore
index ac02a58..aa7a8ac 100644
--- a/.gitignore
+++ b/.gitignore
@@ -107,6 +107,10 @@
 /git-relink
 /git-remote
 /git-remote-curl
+/git-remote-http
+/git-remote-https
+/git-remote-ftp
+/git-remote-ftps
 /git-repack
 /git-replace
 /git-repo-config
diff --git a/Makefile b/Makefile
index 2ad7e36..546a408 100644
--- a/Makefile
+++ b/Makefile
@@ -424,6 +424,13 @@ BUILT_INS += git-stage$X
 BUILT_INS += git-status$X
 BUILT_INS += git-whatchanged$X
 
+#ifdef NO_CURL
+REMOTE_CURL_NAMES =
+#else
+# Yes, this is missing git-remote-http intentionally!
+REMOTE_CURL_NAMES = git-remote-https git-remote-ftp git-remote-ftps
+#endif
+
 # what 'all' will build and 'install' will install in gitexecdir,
 # excluding programs for built-in commands
 ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
@@ -1097,7 +1104,7 @@ else
 	else
 		CURL_LIBCURL = -lcurl
 	endif
-	PROGRAMS += git-remote-curl$X git-http-fetch$X
+	PROGRAMS += git-remote-http$X git-remote-https$X git-remote-ftp$X git-remote-ftps$X git-http-fetch$X
 	curl_check := $(shell (echo 070908; curl-config --vernum) | sort -r | sed -ne 2p)
 	ifeq "$(curl_check)" "070908"
 		ifndef NO_EXPAT
@@ -1676,7 +1683,13 @@ git-http-push$X: revision.o http.o http-push.o $(GITLIBS)
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
 		$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
 
-git-remote-curl$X: remote-curl.o http.o http-walker.o $(GITLIBS)
+$(REMOTE_CURL_NAMES): git-remote-http$X
+	$(QUIET_LNCP)$(RM) $@ && \
+	ln $< $@ 2>/dev/null || \
+	ln -s $< $@ 2>/dev/null || \
+	cp $< $@
+
+git-remote-http$X: remote-curl.o http.o http-walker.o $(GITLIBS)
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
 		$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
 
@@ -1852,6 +1865,7 @@ endif
 ifneq (,$X)
 	$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), test '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p' -ef '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p$X' || $(RM) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p';)
 endif
+
 	bindir=$$(cd '$(DESTDIR_SQ)$(bindir_SQ)' && pwd) && \
 	execdir=$$(cd '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' && pwd) && \
 	{ test "$$bindir/" = "$$execdir/" || \
@@ -1865,6 +1879,12 @@ endif
 		ln -s "git$X" "$$execdir/$$p" 2>/dev/null || \
 		cp "$$execdir/git$X" "$$execdir/$$p" || exit; \
 	  done; } && \
+	{ for p in $(REMOTE_CURL_NAMES); do \
+		$(RM) "$$execdir/$$p" && \
+		ln "$$execdir/git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
+		ln -s "git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
+		cp "$$execdir/git-remote-http$X" "$$execdir/$$p" || exit; \
+	  done; } && \
 	./check_bindir "z$$bindir" "z$$execdir" "$$bindir/git-add$X"
 
 install-doc:
diff --git a/transport.c b/transport.c
index 64938fd..211114e 100644
--- a/transport.c
+++ b/transport.c
@@ -932,14 +932,6 @@ struct transport *transport_get(struct remote *remote, const char *url)
 
 		data->conn = NULL;
 		data->got_remote_heads = 0;
-	} else if (!prefixcmp(url, "http://")
-		|| !prefixcmp(url, "https://")
-		|| !prefixcmp(url, "ftp://")) {
-		/* These three are just plain special. */
-		transport_helper_init(ret, "curl");
-#ifdef NO_CURL
-		error("git was compiled without libcurl support.");
-#endif
 	} else {
 		/* Unknown protocol in URL. Pass to external handler. */
 		int len = external_specification_len(url);
-- 
1.6.6.rc1.300.gfbc27

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: [REROLL PATCH 1/8] Add remote helper debug mode
  2009-12-08 13:16 ` [REROLL PATCH 1/8] Add remote helper debug mode Ilari Liusvaara
@ 2009-12-08 23:34   ` Junio C Hamano
  0 siblings, 0 replies; 23+ messages in thread
From: Junio C Hamano @ 2009-12-08 23:34 UTC (permalink / raw)
  To: Ilari Liusvaara; +Cc: git

Ilari Liusvaara <ilari.liusvaara@elisanet.fi> writes:

> Remote helpers deadlock easily, so support debug mode which shows the
> interaction steps.

Also new helper functions, sendline(), recvline(), write_constant() and
xchgline() introduces abstraction that makes the whole thing a lot nicer
to read ;-).

Nice.

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [REROLL PATCH 2/8] Support mandatory capabilities
  2009-12-08 13:16 ` [REROLL PATCH 2/8] Support mandatory capabilities Ilari Liusvaara
@ 2009-12-08 23:34   ` Junio C Hamano
  2009-12-09 15:12     ` Ilari Liusvaara
  0 siblings, 1 reply; 23+ messages in thread
From: Junio C Hamano @ 2009-12-08 23:34 UTC (permalink / raw)
  To: Ilari Liusvaara; +Cc: git

Ilari Liusvaara <ilari.liusvaara@elisanet.fi> writes:

> diff --git a/transport-helper.c b/transport-helper.c
> index a721dc2..f977d28 100644
> --- a/transport-helper.c
> +++ b/transport-helper.c
> @@ -93,25 +93,39 @@ static struct child_process *get_helper(struct transport *transport)
>  
>  	data->out = xfdopen(helper->out, "r");
>  	while (1) {
> +		const char *capname;
> +		int mandatory = 0;
>  		recvline(data, &buf);
> ...
> +		} else if (mandatory) {
> +			fflush(stderr);
> +			die("Unknown madatory capability %s. This remote "
> +			    "helper probably needs newer version of Git.\n",
> +			    capname);

Why fflush() here?  Is the reason for needing to flush stderr before
letting die() to write into it very specific to this codepath, or shared
among other callers of die()?  I am wondering if we should add this
fflush() to report() in usage.c instead.

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [REROLL PATCH 3/8] Pass unknown protocols to external protocol handlers
  2009-12-08 13:16 ` [REROLL PATCH 3/8] Pass unknown protocols to external protocol handlers Ilari Liusvaara
@ 2009-12-08 23:35   ` Junio C Hamano
  2009-12-09  8:55     ` Bert Wesarg
  2009-12-09 15:15     ` Ilari Liusvaara
  0 siblings, 2 replies; 23+ messages in thread
From: Junio C Hamano @ 2009-12-08 23:35 UTC (permalink / raw)
  To: Ilari Liusvaara; +Cc: git

Ilari Liusvaara <ilari.liusvaara@elisanet.fi> writes:

> +const char *remove_ext_force(const char *url)
> +{
> +	const char *url2 = url;
> +	const char *first_colon = NULL;
> +
> +	if (!url)
> +		return NULL;
> +
> +	while (*url2 && !first_colon)
> +		if (*url2 == ':')
> +			first_colon = url2;
> +		else
> +			url2++;
> +
> +	if (first_colon && first_colon[1] == ':')
> +		return first_colon + 2;
> +	else
> +		return url;
> +}

Sorry, I am slow today, but is this any different from:

	if (url) {
		const char *colon = strchr(url, ':');
	        if (colon && colon[1] == ':')
	        	return url2 + 2;
	}
	return url;

> diff --git a/transport.c b/transport.c
> index 3eea836..e42a82b 100644
> --- a/transport.c
> +++ b/transport.c
> @@ -780,6 +780,58 @@ static int is_file(const char *url)
>  	return S_ISREG(buf.st_mode);
>  }
>  
> +static const char *strchrc(const char *str, int c)
> +{
> +	while (*str)
> +		if (*str == c)
> +			return str;
> +		else
> +			str++;
> +	return NULL;
> +}

I cannot spot how this is different from strchr()...

> +static int is_url(const char *url)
> +{
> +	const char *url2, *first_slash;
> +
> +	if (!url)
> +		return 0;
> +	url2 = url;
> +	first_slash = strchrc(url, '/');
> +
> +	/* Input with no slash at all or slash first can't be URL. */
> +	if (!first_slash || first_slash == url)
> +		return 0;
> +	/* Character before must be : and next must be /. */
> +	if (first_slash[-1] != ':' || first_slash[1] != '/')
> +		return 0;
> +	/* There must be something before the :// */
> +	if (first_slash == url + 1)
> +		return 0;
> +	/*
> +	 * Check all characters up to first slash. Only alpha, num and
> +	 * colon (":") are allowed. ":" must be followed by ":" or "/".
> +	 */

Hmm, so "a::::bc:://" is ok?

Is the reason this does not to check the string up to (first_slash-1) for
a valid syntax for <scheme> (as in "<scheme>://") because this potentially
has "<helper>::" in front of it?

I cannot tell if you want to allow "<helper1>::<helper2>::<scheme>://..."
by reading this code.

> +static int external_specification_len(const char *url)
> +{
> +	return strchrc(url, ':') - url;
> +}

Does the caller guarantee url has at least one colon in it?

>  struct transport *transport_get(struct remote *remote, const char *url)
>  {
>  	struct transport *ret = xcalloc(1, sizeof(*ret));
> @@ -805,30 +857,23 @@ struct transport *transport_get(struct remote *remote, const char *url)
>  
>  	if (remote && remote->foreign_vcs) {
>  		transport_helper_init(ret, remote->foreign_vcs);
> +	} else if (!prefixcmp(url, "rsync:")) {
>  		ret->get_refs_list = get_refs_via_rsync;
>  		ret->fetch = fetch_objs_via_rsync;
>  		ret->push = rsync_transport_push;
>  	} else if (is_local(url) && is_file(url)) {
>  		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;
> +	} else if (!is_url(url)
> +		|| !prefixcmp(url, "file://")
> +		|| !prefixcmp(url, "git://")
> +		|| !prefixcmp(url, "ssh://")
> +		|| !prefixcmp(url, "git+ssh://")
> +		|| !prefixcmp(url, "ssh+git://")) {
> +		/* These are builtin smart transports. */

Hmm, what is !is_url(url) at the beginning for, if this lists "builtin"
smart transports?

> @@ -845,6 +890,21 @@ struct transport *transport_get(struct remote *remote, const char *url)
>  		data->receivepack = "git-receive-pack";
>  		if (remote->receivepack)
>  			data->receivepack = remote->receivepack;
> +	} else if (!prefixcmp(url, "http://")
> +		|| !prefixcmp(url, "https://")
> +		|| !prefixcmp(url, "ftp://")) {
> +		/* These three are just plain special. */
> +		transport_helper_init(ret, "curl");
> +#ifdef NO_CURL
> +		error("git was compiled without libcurl support.");
> +#endif
> +	} 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);

Hmph, external_specification_len() may get passed a colon-less string
after all, I think.

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [REROLL PATCH 5/8] Support taking over transports
  2009-12-08 13:16 ` [REROLL PATCH 5/8] Support taking over transports Ilari Liusvaara
@ 2009-12-08 23:37   ` Junio C Hamano
  2009-12-09 15:17     ` Ilari Liusvaara
  0 siblings, 1 reply; 23+ messages in thread
From: Junio C Hamano @ 2009-12-08 23:37 UTC (permalink / raw)
  To: Ilari Liusvaara; +Cc: git

Ilari Liusvaara <ilari.liusvaara@elisanet.fi> writes:

> diff --git a/transport-helper.c b/transport-helper.c
> index 0e82553..3b7340c 100644
> --- a/transport-helper.c
> +++ b/transport-helper.c
> @@ -22,6 +22,7 @@ struct helper_data
>  	/* These go from remote name (as in "list") to private name */
>  	struct refspec *refspecs;
>  	int refspec_nr;
> +	struct git_transport_options gitoptions;
>  };

Will we ever have another 'xxxoptions' field in this structure that is not
so git-ish?  'gitoptions' somehow doesn't feel right, unless you plan to
later add scm specific options like 'hgoptions', 'bzroptions' in this
field you need to distinguish "git" one from them.

I know you needed to name the new field to store the transport option
under a different name from the existing 'option' field, but for that
purpose, 'transport_options' might be a more appropriate name.

> @@ -109,9 +111,19 @@ static struct child_process *get_helper(struct transport *transport)
>  		die("Unable to run helper: git %s", helper->argv[0]);
>  	data->helper = helper;
>  
> +	/*
> +	 * Open the output as FILE* so strbuf_getline() can be used.
> +	 * Do this with duped fd because fclose() will close the fd,
> +	 * and stuff like taking over will require the fd to remain.
> +	 *
> +	 */
> +	duped = dup(helper->out);
> +	if (duped < 0)
> +		die_errno("Can't dup helper output fd");
> +	data->out = xfdopen(duped, "r");
> +

Neat hack (the kind I often love), but it makes something deep inside me
cringe.  This looks fragile and possibly wrong.

Who guarantees that reading from the (FILE *)data->out by strbuf_getline()
that eventually calls into fgetc() does not cause more data be read in the
buffer assiciated with the (FILE *) than we will consume?  The other FILE*
you will make out of helper->out won't be able to read what data->out has
already slurped in to its stdio buffer.

The call sequence, after [6/8] is applied as I understand it is:

    - _process_connect()
      - get_helper()
        - start_command() that gives a pipe to read from the helper in
          helper->out;
        - the above dup dance makes (FILE *)data->out out of a copied fd;
        - read from data->out, potentially reading a lot more than
          the loop consumes into the stdio buffer of data->out;
      - dup dance again to make (FILE *)input;
      - read from input, unbuffered.

But if "capabilities" exchange has read past the capability response from
the helper into helper->out inside get_helper(), wouldn't it make the dup
dance to make an extra "input" to read the rest unbuffered moot, as
"input" won't be even reading from the beginning of "the rest"?

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [REROLL PATCH 6/8] Support remote helpers implementing smart transports
  2009-12-08 13:16 ` [REROLL PATCH 6/8] Support remote helpers implementing smart transports Ilari Liusvaara
@ 2009-12-08 23:38   ` Junio C Hamano
  2009-12-09 15:16     ` Ilari Liusvaara
  0 siblings, 1 reply; 23+ messages in thread
From: Junio C Hamano @ 2009-12-08 23:38 UTC (permalink / raw)
  To: Ilari Liusvaara; +Cc: git

Ilari Liusvaara <ilari.liusvaara@elisanet.fi> writes:

> Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
> ---
>  Documentation/git-remote-helpers.txt |   25 +++++++-
>  transport-helper.c                   |  126 ++++++++++++++++++++++++++++++++--
>  2 files changed, 144 insertions(+), 7 deletions(-)
>
> diff --git a/Documentation/git-remote-helpers.txt b/Documentation/git-remote-helpers.txt
> index 20a05fe..b957813 100644
> --- a/Documentation/git-remote-helpers.txt
> +++ b/Documentation/git-remote-helpers.txt
> @@ -93,6 +93,20 @@ Supported if the helper has the "push" capability.
>  +
>  Supported if the helper has the "import" capability.
>  
> +'connect' <service>::
> +	Connects to given service. Stdin and stdout of helper are

A minor point, but in prose, unless it explains how to use "stdin" and
"stdout" as a variable, keyword, etc. in code, I'd prefer to see these
spelled out, like so:

	The standard input and output of helpers are connected to ...

> @@ -34,12 +36,12 @@ static void sendline(struct helper_data *helper, struct strbuf *buffer)
>  		die_errno("Full write to remote helper failed");
>  }
>  
> -static int recvline(struct helper_data *helper, struct strbuf *buffer)
> +static int _recvline(FILE *helper, struct strbuf *buffer)
>  {

Not a good naming for two reasons.

 - Somebody new to the code wouldn't be able to tell which of recvline()
   and _recvline() take helper_data and FILE easily.

 - A function name that starts with an underscore, even if it is file
   scoped static, is something we tend to avoid.  This applies to
   _process_connect() in the earlier patch as well.

recvline_fh() vs revline() might be better as most of the interaction in
this layer are done on helper_data, which makes the name recvline() pair
nicely with sendline that also takes helper_data; and the oddball one that
is _not_ an implementation detail (i.e. you have calls outside recvline()
implementation that call _recvline()) hints that it takes filehandle
instead.

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [REROLL PATCH 7/8] Support remote archive from external protocol helpers
  2009-12-08 13:16 ` [REROLL PATCH 7/8] Support remote archive from external protocol helpers Ilari Liusvaara
@ 2009-12-08 23:39   ` Junio C Hamano
  2009-12-09 15:16     ` Ilari Liusvaara
  0 siblings, 1 reply; 23+ messages in thread
From: Junio C Hamano @ 2009-12-08 23:39 UTC (permalink / raw)
  To: Ilari Liusvaara; +Cc: git

Ilari Liusvaara <ilari.liusvaara@elisanet.fi> writes:

> Helpers which support connect also should support remote archive
> snapshot (or at least there's only one way to attempt it). So support
> remote snapshotting for protocol helpers.

Sorry, but I cannot parse this "should", and cannot understand how the
first sentence leads to the conclusion ("So") that we must have this
patch.

"They should support it when able, because doing so gives them _this nice
property_ (that is unspecified)"???

Or "They must support it because we are changing the code not to work
without"???

Or "Because the transport layer has been restructured cleanly enough to
allow passing general payload, there is no reason not to do this change to
pass 'archive' output in addition to the 'git smart fetch/push protocol'
payload, and this allows the archive command to take advantage of the
helper based transports"???

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [REROLL PATCH 3/8] Pass unknown protocols to external protocol  handlers
  2009-12-08 23:35   ` Junio C Hamano
@ 2009-12-09  8:55     ` Bert Wesarg
  2009-12-09 15:15     ` Ilari Liusvaara
  1 sibling, 0 replies; 23+ messages in thread
From: Bert Wesarg @ 2009-12-09  8:55 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Ilari Liusvaara, git

On Wed, Dec 9, 2009 at 00:35, Junio C Hamano <gitster@pobox.com> wrote:
> Ilari Liusvaara <ilari.liusvaara@elisanet.fi> writes:
>> +     } else if (!is_url(url)
>> +             || !prefixcmp(url, "file://")
>> +             || !prefixcmp(url, "git://")
>> +             || !prefixcmp(url, "ssh://")
>> +             || !prefixcmp(url, "git+ssh://")
>> +             || !prefixcmp(url, "ssh+git://")) {
>> +             /* These are builtin smart transports. */
>
> Hmm, what is !is_url(url) at the beginning for, if this lists "builtin"
> smart transports?
I think/hope this should catch theses use cases:

       ?   [user@]host.xz:/path/to/repo.git/
       ?   [user@]host.xz:~user/path/to/repo.git/
       ?   [user@]host.xz:path/to/repo.git

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [REROLL PATCH 2/8] Support mandatory capabilities
  2009-12-08 23:34   ` Junio C Hamano
@ 2009-12-09 15:12     ` Ilari Liusvaara
  0 siblings, 0 replies; 23+ messages in thread
From: Ilari Liusvaara @ 2009-12-09 15:12 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On Tue, Dec 08, 2009 at 03:34:34PM -0800, Junio C Hamano wrote:
> Ilari Liusvaara <ilari.liusvaara@elisanet.fi> writes:

> > +			fflush(stderr);
> > +			die("Unknown madatory capability %s. This remote "
> > +			    "helper probably needs newer version of Git.\n",
> > +			    capname);
> 
> Why fflush() here?  Is the reason for needing to flush stderr before
> letting die() to write into it very specific to this codepath, or shared
> among other callers of die()?  I am wondering if we should add this
> fflush() to report() in usage.c instead.

No idea why its there (anymore). Die will flush stderr anyway via exit.
Removed.

-Ilari

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [REROLL PATCH 3/8] Pass unknown protocols to external protocol handlers
  2009-12-08 23:35   ` Junio C Hamano
  2009-12-09  8:55     ` Bert Wesarg
@ 2009-12-09 15:15     ` Ilari Liusvaara
  1 sibling, 0 replies; 23+ messages in thread
From: Ilari Liusvaara @ 2009-12-09 15:15 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On Tue, Dec 08, 2009 at 03:35:17PM -0800, Junio C Hamano wrote:
> Ilari Liusvaara <ilari.liusvaara@elisanet.fi> writes:
> 
> > +const char *remove_ext_force(const char *url)
> > +{
> > +	const char *url2 = url;
> > +	const char *first_colon = NULL;
> > +
> > +	if (!url)
> > +		return NULL;
> > +
> > +	while (*url2 && !first_colon)
> > +		if (*url2 == ':')
> > +			first_colon = url2;
> > +		else
> > +			url2++;
> > +
> > +	if (first_colon && first_colon[1] == ':')
> > +		return first_colon + 2;
> > +	else
> > +		return url;
> > +}
> 
> Sorry, I am slow today, but is this any different from:
> 
> 	if (url) {
> 		const char *colon = strchr(url, ':');
> 	        if (colon && colon[1] == ':')
> 	        	return url2 + 2;
> 	}
> 	return url;

Undefined variable url2 (you mean colon?). Changed.
 
> > diff --git a/transport.c b/transport.c
> > index 3eea836..e42a82b 100644
> > --- a/transport.c
> > +++ b/transport.c
> > @@ -780,6 +780,58 @@ static int is_file(const char *url)
> >  	return S_ISREG(buf.st_mode);
> >  }
> >  
> > +static const char *strchrc(const char *str, int c)
> > +{
> > +	while (*str)
> > +		if (*str == c)
> > +			return str;
> > +		else
> > +			str++;
> > +	return NULL;
> > +}
> 
> I cannot spot how this is different from strchr()...

Return type. Not that useful. Removed.
 
> > +static int is_url(const char *url)
> > +{
> > +	const char *url2, *first_slash;
> > +
> > +	if (!url)
> > +		return 0;
> > +	url2 = url;
> > +	first_slash = strchrc(url, '/');
> > +
> > +	/* Input with no slash at all or slash first can't be URL. */
> > +	if (!first_slash || first_slash == url)
> > +		return 0;
> > +	/* Character before must be : and next must be /. */
> > +	if (first_slash[-1] != ':' || first_slash[1] != '/')
> > +		return 0;
> > +	/* There must be something before the :// */
> > +	if (first_slash == url + 1)
> > +		return 0;
> > +	/*
> > +	 * Check all characters up to first slash. Only alpha, num and
> > +	 * colon (":") are allowed. ":" must be followed by ":" or "/".
> > +	 */

I tightened this a bit, only checking (exclusive) character before first
"/" and requiring all-alphanum. The previous rules were apparently
leftover from times the "specify remote helper by url" patch didn't
exist.

Considering context at call site, it is equivalent, since any ':' must be
followed by '/' or is_url will never be called, and first ':/' must be
exactly at last_slash - 1 (by earlier checks).
 
> Hmm, so "a::::bc:://" is ok?

is_url never gets such thing. Due to "::" it bypasses URL validation. And
yes, it is ok.

> Is the reason this does not to check the string up to (first_slash-1) for
> a valid syntax for <scheme> (as in "<scheme>://") because this potentially
> has "<helper>::" in front of it?

It doesn't have '<helper>::' in front of it.

> I cannot tell if you want to allow "<helper1>::<helper2>::<scheme>://..."
> by reading this code.

Allowing or denying that is not up to this function. And one can't chain
helpers that way. It invokes <helper1>
 
> > +static int external_specification_len(const char *url)
> > +{
> > +	return strchrc(url, ':') - url;
> > +}
> 
> Does the caller guarantee url has at least one colon in it?

Anything passing is_url() does have ':' in it.

> > +	} else if (!is_url(url)
> > +		|| !prefixcmp(url, "file://")
> > +		|| !prefixcmp(url, "git://")
> > +		|| !prefixcmp(url, "ssh://")
> > +		|| !prefixcmp(url, "git+ssh://")
> > +		|| !prefixcmp(url, "ssh+git://")) {
> > +		/* These are builtin smart transports. */
> 
> Hmm, what is !is_url(url) at the beginning for, if this lists "builtin"
> smart transports?

It catches the scp and path syntaxes as not URLs.
 
> > +	} 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);
> 
> Hmph, external_specification_len() may get passed a colon-less string
> after all, I think.

Nope, it can't. Else if above snarfs everything that doesn't pass
is_url, and string can't pass it without having ':' in it. 

-Ilari

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [REROLL PATCH 6/8] Support remote helpers implementing smart transports
  2009-12-08 23:38   ` Junio C Hamano
@ 2009-12-09 15:16     ` Ilari Liusvaara
  0 siblings, 0 replies; 23+ messages in thread
From: Ilari Liusvaara @ 2009-12-09 15:16 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On Tue, Dec 08, 2009 at 03:38:41PM -0800, Junio C Hamano wrote:
> Ilari Liusvaara <ilari.liusvaara@elisanet.fi> writes:
> >  
> > +'connect' <service>::
> > +	Connects to given service. Stdin and stdout of helper are
> 
> A minor point, but in prose, unless it explains how to use "stdin" and
> "stdout" as a variable, keyword, etc. in code, I'd prefer to see these
> spelled out, like so:
> 
> 	The standard input and output of helpers are connected to ...

Changed.

> > -static int recvline(struct helper_data *helper, struct strbuf *buffer)
> > +static int _recvline(FILE *helper, struct strbuf *buffer)
> >  {
> 
> recvline_fh() vs revline() might be better as most of the interaction in
> this layer are done on helper_data, which makes the name recvline() pair
> nicely with sendline that also takes helper_data; and the oddball one that
> is _not_ an implementation detail (i.e. you have calls outside recvline()
> implementation that call _recvline()) hints that it takes filehandle
> instead.

- _recvline -> recvline_fh
- _process_connect -> process_connect_service

-Ilari

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [REROLL PATCH 7/8] Support remote archive from external protocol helpers
  2009-12-08 23:39   ` Junio C Hamano
@ 2009-12-09 15:16     ` Ilari Liusvaara
  0 siblings, 0 replies; 23+ messages in thread
From: Ilari Liusvaara @ 2009-12-09 15:16 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On Tue, Dec 08, 2009 at 03:39:42PM -0800, Junio C Hamano wrote:
> Ilari Liusvaara <ilari.liusvaara@elisanet.fi> writes:
> 
> > Helpers which support connect also should support remote archive
> > snapshot (or at least there's only one way to attempt it). So support
> > remote snapshotting for protocol helpers.
> 
> Or "Because the transport layer has been restructured cleanly enough to
> allow passing general payload, there is no reason not to do this change to
> pass 'archive' output in addition to the 'git smart fetch/push protocol'
> payload, and this allows the archive command to take advantage of the
> helper based transports"???

This one is the correct interpretation.

Changed to:
-----------
Support remote archive from all smart transports

Previously, remote archive required internal (non remote-helper)
smart transport. Extend the remote archive to also support smart
transports implemented by remote helpers.
------------

-Ilari

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [REROLL PATCH 5/8] Support taking over transports
  2009-12-08 23:37   ` Junio C Hamano
@ 2009-12-09 15:17     ` Ilari Liusvaara
  2009-12-09 21:08       ` Junio C Hamano
  0 siblings, 1 reply; 23+ messages in thread
From: Ilari Liusvaara @ 2009-12-09 15:17 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On Tue, Dec 08, 2009 at 03:37:06PM -0800, Junio C Hamano wrote:
> Ilari Liusvaara <ilari.liusvaara@elisanet.fi> writes:
> 
> Will we ever have another 'xxxoptions' field in this structure that is not
> so git-ish?  'gitoptions' somehow doesn't feel right, unless you plan to
> later add scm specific options like 'hgoptions', 'bzroptions' in this
> field you need to distinguish "git" one from them.
> 
> I know you needed to name the new field to store the transport option
> under a different name from the existing 'option' field, but for that
> purpose, 'transport_options' might be a more appropriate name.

Changed.
 
> > @@ -109,9 +111,19 @@ static struct child_process *get_helper(struct transport *transport)
> >  		die("Unable to run helper: git %s", helper->argv[0]);
> >  	data->helper = helper;
> >  
> > +	/*
> > +	 * Open the output as FILE* so strbuf_getline() can be used.
> > +	 * Do this with duped fd because fclose() will close the fd,
> > +	 * and stuff like taking over will require the fd to remain.
> > +	 *
> > +	 */
> > +	duped = dup(helper->out);
> > +	if (duped < 0)
> > +		die_errno("Can't dup helper output fd");
> > +	data->out = xfdopen(duped, "r");
> > +
> 
> Neat hack (the kind I often love), but it makes something deep inside me
> cringe.  This looks fragile and possibly wrong.
> 
> Who guarantees that reading from the (FILE *)data->out by strbuf_getline()
> that eventually calls into fgetc() does not cause more data be read in the
> buffer assiciated with the (FILE *) than we will consume?  The other FILE*
> you will make out of helper->out won't be able to read what data->out has
> already slurped in to its stdio buffer.

Causality impiles this can happen only if buffered version gets its buffer
filled after sending connect command. And looking at stdio operations that
can occur after sending the command:

- fprintfs on stderr (debug mode only).
- fgetc()s on unbuffered version.

-Ilari

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [REROLL PATCH 5/8] Support taking over transports
  2009-12-09 15:17     ` Ilari Liusvaara
@ 2009-12-09 21:08       ` Junio C Hamano
  2009-12-09 21:42         ` Ilari Liusvaara
  0 siblings, 1 reply; 23+ messages in thread
From: Junio C Hamano @ 2009-12-09 21:08 UTC (permalink / raw)
  To: Ilari Liusvaara; +Cc: Junio C Hamano, git

Ilari Liusvaara <ilari.liusvaara@elisanet.fi> writes:

>> Who guarantees that reading from the (FILE *)data->out by strbuf_getline()
>> that eventually calls into fgetc() does not cause more data be read in the
>> buffer assiciated with the (FILE *) than we will consume?  The other FILE*
>> you will make out of helper->out won't be able to read what data->out has
>> already slurped in to its stdio buffer.
>
> Causality impiles this can happen only if buffered version gets its buffer
> filled after sending connect command. And looking at stdio operations that
> can occur after sending the command:
>
> - fprintfs on stderr (debug mode only).
> - fgetc()s on unbuffered version.

I was worried about the "capabilities" loop in get_helper() if it reads
too much from data->out (causing helper->out to lose the beginning of what
it should read), but as you said, by "Causality" it cannot happen. The
protocol during that phase works with the other end in lock-step, and the
other end wouldn't have written to the pipe what we shouldn't read in that
loop (e.g. remote-curl.c responds with "capabilities" with lines and ends
with fflush(), and after that it waits for us to talk to it --- it would
never start talking by itself without first being asked to talk).

Is data->out only used inside get_helper() to read the capabilities
response?  I was confused by it being keft open until disconnect time.

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [REROLL PATCH 5/8] Support taking over transports
  2009-12-09 21:08       ` Junio C Hamano
@ 2009-12-09 21:42         ` Ilari Liusvaara
  0 siblings, 0 replies; 23+ messages in thread
From: Ilari Liusvaara @ 2009-12-09 21:42 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On Wed, Dec 09, 2009 at 01:08:44PM -0800, Junio C Hamano wrote:
> 
> Is data->out only used inside get_helper() to read the capabilities
> response?  I was confused by it being keft open until disconnect time.
 
Its used for all incoming communications from remote helper until connect
request (and even after it if falling back to dumb transport).

- In smart transport case, that includes possible servpath option.
- In non-smart case, it includes a lot more.

In fact, recvline uses exactly helper->out as stream to read.

-Ilari

^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2009-12-09 21:42 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-12-08 13:16 [REROLL PATCH 0/8] Remote helpers smart transport extensions Ilari Liusvaara
2009-12-08 13:16 ` [REROLL PATCH 1/8] Add remote helper debug mode Ilari Liusvaara
2009-12-08 23:34   ` Junio C Hamano
2009-12-08 13:16 ` [REROLL PATCH 2/8] Support mandatory capabilities Ilari Liusvaara
2009-12-08 23:34   ` Junio C Hamano
2009-12-09 15:12     ` Ilari Liusvaara
2009-12-08 13:16 ` [REROLL PATCH 3/8] Pass unknown protocols to external protocol handlers Ilari Liusvaara
2009-12-08 23:35   ` Junio C Hamano
2009-12-09  8:55     ` Bert Wesarg
2009-12-09 15:15     ` Ilari Liusvaara
2009-12-08 13:16 ` [REROLL PATCH 4/8] Refactor git transport options parsing Ilari Liusvaara
2009-12-08 13:16 ` [REROLL PATCH 5/8] Support taking over transports Ilari Liusvaara
2009-12-08 23:37   ` Junio C Hamano
2009-12-09 15:17     ` Ilari Liusvaara
2009-12-09 21:08       ` Junio C Hamano
2009-12-09 21:42         ` Ilari Liusvaara
2009-12-08 13:16 ` [REROLL PATCH 6/8] Support remote helpers implementing smart transports Ilari Liusvaara
2009-12-08 23:38   ` Junio C Hamano
2009-12-09 15:16     ` Ilari Liusvaara
2009-12-08 13:16 ` [REROLL PATCH 7/8] Support remote archive from external protocol helpers Ilari Liusvaara
2009-12-08 23:39   ` Junio C Hamano
2009-12-09 15:16     ` Ilari Liusvaara
2009-12-08 13:16 ` [REROLL PATCH 8/8] Remove special casing of http, https and ftp Ilari Liusvaara

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.