git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH v2 0/8] Remote helpers smart transport extensions
@ 2009-12-04 15:55 Ilari Liusvaara
  2009-12-04 15:55 ` Ilari Liusvaara
                   ` (9 more replies)
  0 siblings, 10 replies; 20+ messages in thread
From: Ilari Liusvaara @ 2009-12-04 15:55 UTC (permalink / raw)
  To: git

This series is reroll of previous version of smart transport extensions,
with various technical nits addressed and some errors fixed. Also rebased
on top of latest next.

Major changes:
- Service names now have 'git-' prefix.
- Successful response to <connect> is "", not "OK".
- Removed <connect> "ERROR" response.
- <connect-r> renamed to <connect>.
- <invoke-r> removed. Pass service executable as option "servpath" instead.
- HTTP helpers are now hardlinked if possible (copied if not).
- Revert the changes to way helpers are invoked.[1]
- Actually test the intermediate states for gross errors.

[1] The current way seems to have all sorts of funky failure cases, but
I'm not touching it without fixing it properly.

Known issues (not caused by this code and does not need this code to be
visible):
- Segfaults when closing transports, caused by double-frees
- Funky error message if trying to use not-present helper.

Ilari Liusvaara (8):
  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
  Add remote helper debug mode
  Support mandatory capabilities

 .gitignore                           |    4 +
 Documentation/git-remote-helpers.txt |   33 ++++-
 Makefile                             |   24 +++-
 builtin-archive.c                    |   17 ++-
 transport-helper.c                   |  258 +++++++++++++++++++++++++++++-----
 transport.c                          |  258 +++++++++++++++++++++++++++------
 transport.h                          |   32 ++++
 7 files changed, 534 insertions(+), 92 deletions(-)

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

* [RFC PATCH v2 0/8] Remote helpers smart transport extensions
  2009-12-04 15:55 [RFC PATCH v2 0/8] Remote helpers smart transport extensions Ilari Liusvaara
@ 2009-12-04 15:55 ` Ilari Liusvaara
  2009-12-04 15:56 ` Ilari Liusvaara
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: Ilari Liusvaara @ 2009-12-04 15:55 UTC (permalink / raw)
  To: git

This series is reroll of previous version of smart transport extensions,
with various technical nits addressed and some errors fixed. Also rebased
on top of latest next.

Major changes:
- Service names now have 'git-' prefix.
- Successful response to <connect> is "", not "OK".
- Removed <connect> "ERROR" response.
- <connect-r> renamed to <connect>.
- <invoke-r> removed. Pass service executable as option "servpath" instead.
- HTTP helpers are now hardlinked if possible (copied if not).
- Revert the changes to way helpers are invoked.[1]
- Actually test that all intermediate states compile.

[1] The current way seems to have all sorts of funky failure cases, but
I'm not touching it without fixing it properly.

Known issues (not caused by this code and does not need this code to be
visible):
- Segfaults when closing transports, caused by double-frees
- Funky error message if trying to use not-present helper.

Ilari Liusvaara (8):
  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
  Add remote helper debug mode
  Support mandatory capabilities

 .gitignore                           |    4 +
 Documentation/git-remote-helpers.txt |   33 ++++-
 Makefile                             |   24 +++-
 builtin-archive.c                    |   17 ++-
 transport-helper.c                   |  257 +++++++++++++++++++++++++++++-----
 transport.c                          |  258 +++++++++++++++++++++++++++------
 transport.h                          |   32 ++++
 7 files changed, 533 insertions(+), 92 deletions(-)

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

* [RFC PATCH v2 0/8] Remote helpers smart transport extensions
  2009-12-04 15:55 [RFC PATCH v2 0/8] Remote helpers smart transport extensions Ilari Liusvaara
  2009-12-04 15:55 ` Ilari Liusvaara
@ 2009-12-04 15:56 ` Ilari Liusvaara
  2009-12-04 15:56 ` [RFC PATCH v2 1/8] Pass unknown protocols to external protocol handlers Ilari Liusvaara
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: Ilari Liusvaara @ 2009-12-04 15:56 UTC (permalink / raw)
  To: git

This series is reroll of previous version of smart transport extensions,
with various technical nits addressed and some errors fixed. Also rebased
on top of latest next.

Major changes:
- Service names now have 'git-' prefix.
- Successful response to <connect> is "", not "OK".
- Removed <connect> "ERROR" response.
- <connect-r> renamed to <connect>.
- <invoke-r> removed. Pass service executable as option "servpath" instead.
- HTTP helpers are now hardlinked if possible (copied if not).
- Revert the changes to invoking helpers (see known issues)


Known issues (not caused by this code and does not need this code to be
visible):
- Segfaults when closing transports, caused by double-frees
- Funky error message if trying to use not-present helper.


Ilari Liusvaara (8):
  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
  Add remote helper debug mode
  Support mandatory capabilities

 .gitignore                           |    4 +
 Documentation/git-remote-helpers.txt |   36 +++++-
 Makefile                             |   24 +++-
 builtin-archive.c                    |   17 ++-
 transport-helper.c                   |  261 +++++++++++++++++++++++++++++-----
 transport.c                          |  258 +++++++++++++++++++++++++++------
 transport.h                          |   32 ++++
 7 files changed, 540 insertions(+), 92 deletions(-)

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

* [RFC PATCH v2 1/8] Pass unknown protocols to external protocol handlers
  2009-12-04 15:55 [RFC PATCH v2 0/8] Remote helpers smart transport extensions Ilari Liusvaara
  2009-12-04 15:55 ` Ilari Liusvaara
  2009-12-04 15:56 ` Ilari Liusvaara
@ 2009-12-04 15:56 ` Ilari Liusvaara
  2009-12-04 16:01   ` Sverre Rabbelier
  2009-12-04 17:55   ` Shawn O. Pearce
  2009-12-04 15:56 ` [RFC PATCH v2 2/8] Refactor git transport options parsing Ilari Liusvaara
                   ` (6 subsequent siblings)
  9 siblings, 2 replies; 20+ messages in thread
From: Ilari Liusvaara @ 2009-12-04 15:56 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>
---
 transport-helper.c |   37 ++++++++++++++++++++++-
 transport.c        |   82 +++++++++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 106 insertions(+), 13 deletions(-)

diff --git a/transport-helper.c b/transport-helper.c
index 11f3d7e..d3aad5c 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -22,6 +22,26 @@ struct helper_data
 	int refspec_nr;
 };
 
+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;
@@ -30,6 +50,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;
@@ -42,15 +63,26 @@ 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]);
 	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 disowning will require the fd to remain.
+
+	   Set the stream to unbuffered because some reads are critical
+	   in sense that any overreading will cause deadlocks.
+	*/
+	if((duped = dup(helper->out)) < 0)
+		die_errno("Can't dup helper output fd");
+	data->out = xfdopen(duped, "r");
+	setvbuf(data->out, NULL, _IONBF, 0);
+
 	write_str_in_full(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 */
@@ -91,6 +123,7 @@ static int disconnect_helper(struct transport *transport)
 	if (data->helper) {
 		write_str_in_full(data->helper->in, "\n");
 		close(data->helper->in);
+		close(data->helper->out);
 		fclose(data->out);
 		finish_command(data->helper);
 		free((char *)data->helper->argv[0]);
diff --git a/transport.c b/transport.c
index 3eea836..162d022 100644
--- a/transport.c
+++ b/transport.c
@@ -780,6 +780,55 @@ 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)
+{
+	if(!url)
+		return 0;
+
+	const char* url2 = url;
+	const char* 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
+	   : 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));
@@ -812,23 +861,19 @@ 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;
-
-	} 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.288.g40e67

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

* [RFC PATCH v2 2/8] Refactor git transport options parsing
  2009-12-04 15:55 [RFC PATCH v2 0/8] Remote helpers smart transport extensions Ilari Liusvaara
                   ` (2 preceding siblings ...)
  2009-12-04 15:56 ` [RFC PATCH v2 1/8] Pass unknown protocols to external protocol handlers Ilari Liusvaara
@ 2009-12-04 15:56 ` Ilari Liusvaara
  2009-12-04 15:56 ` [RFC PATCH v2 3/8] Support taking over transports Ilari Liusvaara
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: Ilari Liusvaara @ 2009-12-04 15:56 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>
---
 transport.c |   78 +++++++++++++++++++++++++++++++++++-----------------------
 transport.h |   12 +++++++++
 2 files changed, 59 insertions(+), 31 deletions(-)

diff --git a/transport.c b/transport.c
index 162d022..21310c5 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..5949132 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,9 @@ 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.288.g40e67

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

* [RFC PATCH v2 3/8] Support taking over transports
  2009-12-04 15:55 [RFC PATCH v2 0/8] Remote helpers smart transport extensions Ilari Liusvaara
                   ` (3 preceding siblings ...)
  2009-12-04 15:56 ` [RFC PATCH v2 2/8] Refactor git transport options parsing Ilari Liusvaara
@ 2009-12-04 15:56 ` Ilari Liusvaara
  2009-12-04 18:27   ` Shawn O. Pearce
  2009-12-04 15:56 ` [RFC PATCH v2 4/8] Support remote helpers implementing smart transports Ilari Liusvaara
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 20+ messages in thread
From: Ilari Liusvaara @ 2009-12-04 15:56 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>
---
 transport-helper.c |   12 +++++++
 transport.c        |   89 +++++++++++++++++++++++++++++++++++++++++++++++----
 transport.h        |   15 +++++++++
 3 files changed, 109 insertions(+), 7 deletions(-)

diff --git a/transport-helper.c b/transport-helper.c
index d3aad5c..5d17fb5 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -20,8 +20,18 @@ 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 struct child_process* helper_disown(struct transport *transport)
+{
+	struct helper_data *data = transport->data;
+	struct child_process *child = data->helper;
+	fclose(data->out);
+	free(data);
+	return child;
+}
+
 const char* remove_ext_force(const char* url)
 {
 	const char* url2 = url;
@@ -549,5 +559,7 @@ int transport_helper_init(struct transport *transport, const char *name)
 	transport->fetch = fetch;
 	transport->push_refs = push_refs;
 	transport->disconnect = release_helper;
+	transport->disown = helper_disown;
+	transport->smart_options = &(data->gitoptions);
 	return 0;
 }
diff --git a/transport.c b/transport.c
index 21310c5..7e6ef2b 100644
--- a/transport.c
+++ b/transport.c
@@ -9,6 +9,8 @@
 #include "dir.h"
 #include "refs.h"
 
+struct ref special_transport_layer6_ready;
+
 /* rsync support */
 
 /*
@@ -398,6 +400,8 @@ struct git_transport_data {
 	struct git_transport_options options;
 	struct child_process *conn;
 	int fd[2];
+	/* Connection is fully up. */
+	unsigned virtual_connected : 1;
 	struct extra_have_objects extra_have;
 };
 
@@ -432,10 +436,21 @@ 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->virtual_connected && data->conn) {
+		/* Just mark it connected. */
+		data->virtual_connected = 1;
+		return 0;
+	}
+
 	data->conn = git_connect(data->fd, transport->url,
 				 for_push ? data->options.receivepack :
 				 data->options.uploadpack,
 				 verbose ? CONNECT_VERBOSE : 0);
+
+	if(data->conn)
+		data->virtual_connected = 1;
+
 	return 0;
 }
 
@@ -477,7 +492,7 @@ 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->virtual_connected) {
 		connect_setup(transport, 0, 0);
 		get_remote_heads(data->fd[0], &refs_tmp, 0, NULL, 0, NULL);
 	}
@@ -490,6 +505,7 @@ static int fetch_refs_via_pack(struct transport *transport,
 	if (finish_connect(data->conn))
 		refs = NULL;
 	data->conn = NULL;
+	data->virtual_connected = 0;
 
 	free_refs(refs_tmp);
 
@@ -718,7 +734,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
 	struct send_pack_args args;
 	int ret;
 
-	if (!data->conn) {
+	if (!data->virtual_connected) {
 		struct ref *tmp_refs;
 		connect_setup(transport, 1, 0);
 
@@ -741,6 +757,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->virtual_connected = 0;
 
 	return ret;
 }
@@ -749,7 +766,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->virtual_connected)
+			packet_flush(data->fd[1]);
 		close(data->fd[0]);
 		close(data->fd[1]);
 		finish_connect(data->conn);
@@ -759,6 +777,35 @@ static int disconnect_git(struct transport *transport)
 	return 0;
 }
 
+static void git_take_over_transport(struct transport *transport)
+{
+	struct git_transport_data *data;
+
+	if(!transport->disown)
+		die("Bug detected: Taking over transport requires non-NULL "
+		    "disown method.");
+	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 = transport->disown(transport);
+	data->fd[0] = data->conn->out;
+	data->fd[1] = data->conn->in;
+	data->virtual_connected = 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);
+	transport->disown = NULL;
+}
+
 static int is_local(const char *url)
 {
 	const char *colon = strchr(url, ':');
@@ -857,6 +904,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
 		ret->fetch = fetch_objs_via_rsync;
 		ret->push = rsync_transport_push;
 		ret->smart_options = NULL;
+		ret->disown = NULL;
 	} else if (is_local(url) && is_file(url)) {
 		struct bundle_transport_data *data = xcalloc(1, sizeof(*data));
 		ret->data = data;
@@ -864,6 +912,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
 		ret->fetch = fetch_refs_from_bundle;
 		ret->disconnect = close_bundle;
 		ret->smart_options = NULL;
+		ret->disown = NULL;
 	} else if(!is_url(url)
 		|| !prefixcmp(url, "file://")
 		|| !prefixcmp(url, "git://")
@@ -879,8 +928,10 @@ struct transport *transport_get(struct remote *remote, const char *url)
 		ret->push_refs = git_transport_push;
 		ret->disconnect = disconnect_git;
 		ret->smart_options = &(data->options);
+		ret->disown = NULL;
 
 		data->conn = NULL;
+		data->virtual_connected = 0;
 	} else if (!prefixcmp(url, "http://")
 		|| !prefixcmp(url, "https://")
 		|| !prefixcmp(url, "ftp://")) {
@@ -938,14 +989,25 @@ int transport_push(struct transport *transport,
 		   int refspec_nr, const char **refspec, int flags,
 		   int *nonfastforward)
 {
+	int rc = 0;
 	*nonfastforward = 0;
 	verify_remote_names(refspec_nr, refspec);
 
-	if (transport->push)
-		return transport->push(transport, refspec_nr, refspec, flags);
-	if (transport->push_refs) {
+retry:
+	if (transport->push) {
+		rc = transport->push(transport, refspec_nr, refspec, flags);
+		if(rc == TRANSPORT_LAYER6_READY) {
+			git_take_over_transport(transport);
+			goto retry;
+		}
+		return rc;
+	} else if (transport->push_refs) {
 		struct ref *remote_refs =
 			transport->get_refs_list(transport, 1);
+		if(remote_refs == &special_transport_layer6_ready) {
+			git_take_over_transport(transport);
+			goto retry;
+		}
 		struct ref *local_refs = get_local_heads();
 		int match_flags = MATCH_REFS_NONE;
 		int verbose = flags & TRANSPORT_PUSH_VERBOSE;
@@ -985,8 +1047,15 @@ int transport_push(struct transport *transport,
 
 const struct ref *transport_get_remote_refs(struct transport *transport)
 {
-	if (!transport->remote_refs)
+	if (!transport->remote_refs) {
+retry:
 		transport->remote_refs = transport->get_refs_list(transport, 0);
+		if(transport->remote_refs == &special_transport_layer6_ready) {
+			git_take_over_transport(transport);
+			goto retry;
+		}
+	}
+
 	return transport->remote_refs;
 }
 
@@ -1020,7 +1089,13 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs)
 			heads[nr_heads++] = rm;
 	}
 
+retry:
 	rc = transport->fetch(transport, nr_heads, heads);
+	if(rc == TRANSPORT_LAYER6_READY) {
+		git_take_over_transport(transport);
+		goto retry;
+	}
+
 	free(heads);
 	return rc;
 }
diff --git a/transport.h b/transport.h
index 5949132..f3ee890 100644
--- a/transport.h
+++ b/transport.h
@@ -65,6 +65,15 @@ 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);
 
+	/**
+	 * Disown the transport helper. Releases all resources used
+	 * by field pointed by member data, except that the child
+	 * process is not released but returned and whatever is pointed
+	 * by smart transport options structure is not freed (but the
+	 * smart transport options structure itself is).
+	 **/
+	struct child_process* (*disown)(struct transport* connection);
+
 	/** get_refs_list(), fetch(), and push_refs() can keep
 	 * resources (such as a connection) reserved for futher
 	 * use. disconnect() releases these resources.
@@ -79,6 +88,12 @@ struct transport {
 	struct git_transport_options* smart_options;
 };
 
+/* Returned by get_refs_list, fetch or push methods of struct transport: Layer 6 is ready,
+   take over the transport and retry operation. */
+#define TRANSPORT_LAYER6_READY -42
+extern struct ref special_transport_layer6_ready;
+
+
 #define TRANSPORT_PUSH_ALL 1
 #define TRANSPORT_PUSH_FORCE 2
 #define TRANSPORT_PUSH_DRY_RUN 4
-- 
1.6.6.rc1.288.g40e67

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

* [RFC PATCH v2 4/8] Support remote helpers implementing smart transports
  2009-12-04 15:55 [RFC PATCH v2 0/8] Remote helpers smart transport extensions Ilari Liusvaara
                   ` (4 preceding siblings ...)
  2009-12-04 15:56 ` [RFC PATCH v2 3/8] Support taking over transports Ilari Liusvaara
@ 2009-12-04 15:56 ` Ilari Liusvaara
  2009-12-04 18:37   ` Shawn O. Pearce
  2009-12-04 15:56 ` [RFC PATCH v2 5/8] Support remote archive from external protocol helpers Ilari Liusvaara
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 20+ messages in thread
From: Ilari Liusvaara @ 2009-12-04 15:56 UTC (permalink / raw)
  To: git

Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
---
 Documentation/git-remote-helpers.txt |   28 ++++++++++-
 transport-helper.c                   |   96 +++++++++++++++++++++++++++++++++-
 transport.c                          |   21 +++++++
 transport.h                          |    5 ++
 4 files changed, 147 insertions(+), 3 deletions(-)

diff --git a/Documentation/git-remote-helpers.txt b/Documentation/git-remote-helpers.txt
index 5cfdc0c..91cd9eb 100644
--- a/Documentation/git-remote-helpers.txt
+++ b/Documentation/git-remote-helpers.txt
@@ -90,6 +90,23 @@ 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. Note that to prevent deadlocking, all read data
+	should be immediately flushed to outgoing connection (excepting
+	remote initial advertisments, which should be flushed on first
+	flush packet (0000 as length) encountered.
++
+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
@@ -123,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
 -------------------
 
@@ -165,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 5d17fb5..21aa4ab 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -16,7 +16,8 @@ struct helper_data
 	unsigned fetch : 1,
 		import : 1,
 		option : 1,
-		push : 1;
+		push : 1,
+		connect : 1;
 	/* These go from remote name (as in "list") to private name */
 	struct refspec *refspecs;
 	int refspec_nr;
@@ -72,7 +73,10 @@ static struct child_process *get_helper(struct transport *transport)
 	helper->argv = xcalloc(4, sizeof(*helper->argv));
 	strbuf_addf(&buf, "remote-%s", data->name);
 	helper->argv[0] = strbuf_detach(&buf, NULL);
-	helper->argv[1] = transport->remote->name;
+	if(transport->remote)
+		helper->argv[1] = transport->remote->name;
+	else
+		helper->argv[1] = "";
 	helper->argv[2] = remove_ext_force(transport->url);
 	helper->git_cmd = 1;
 	if (start_command(helper))
@@ -113,6 +117,8 @@ static struct child_process *get_helper(struct transport *transport)
 				   refspec_alloc);
 			refspecs[refspec_nr++] = strdup(buf.buf + strlen("refspec "));
 		}
+		if (!strcmp(buf.buf, "connect"))
+			data->connect = 1;
 	}
 	if (refspecs) {
 		int i;
@@ -331,12 +337,91 @@ 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;
+
+	helper = get_helper(transport);
+
+	/* Handle --upload-pack and friends. This is fire and forget...
+	   just warn if it fails. */
+	if(exec && 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
+		return 0;
+
+	write_in_full(helper->in, cmdbuf.buf, cmdbuf.len);
+	strbuf_reset(&cmdbuf);
+	if (strbuf_getline(&cmdbuf, data->out, '\n') == EOF)
+		exit(128); /* child died, message supplied already */
+	if(!strcmp(cmdbuf.buf, ""))
+		return 1;
+	else if(!strcmp(cmdbuf.buf, "FALLBACK"))
+		return 0;
+	else
+		die("Unknown response to connect: %s",
+			cmdbuf.buf);
+
+	return 0;	/* Shouldn't be here. */
+}
+
+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 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)
 {
 	struct helper_data *data = transport->data;
 	int i, count;
 
+	if(process_connect(transport, 0))
+		return TRANSPORT_LAYER6_READY;
+
 	count = 0;
 	for (i = 0; i < nr_heads; i++)
 		if (!(to_fetch[i]->status & REF_STATUS_UPTODATE))
@@ -364,6 +449,9 @@ static int push_refs(struct transport *transport,
 	struct child_process *helper;
 	struct ref *ref;
 
+	if(process_connect(transport, 1))
+		return TRANSPORT_LAYER6_READY;
+
 	if (!remote_refs)
 		return 0;
 
@@ -507,6 +595,9 @@ static struct ref *get_refs_list(struct transport *transport, int for_push)
 
 	helper = get_helper(transport);
 
+	if(process_connect(transport, for_push))
+		return &special_transport_layer6_ready;
+
 	if (data->push && for_push)
 		write_str_in_full(helper->in, "list for-push\n");
 	else
@@ -559,6 +650,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->disown = helper_disown;
 	transport->smart_options = &(data->gitoptions);
 	return 0;
diff --git a/transport.c b/transport.c
index 7e6ef2b..1488cfe 100644
--- a/transport.c
+++ b/transport.c
@@ -762,6 +762,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;
@@ -926,6 +937,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);
 		ret->disown = NULL;
@@ -1109,6 +1121,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 f3ee890..c86329a 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]);
 
 	/**
 	 * Disown the transport helper. Releases all resources used
@@ -143,6 +145,9 @@ void transport_unlock_pack(struct transport *transport);
 int transport_disconnect(struct transport *transport);
 char *transport_anonymize_url(const char *url);
 
+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.288.g40e67

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

* [RFC PATCH v2 5/8] Support remote archive from external protocol helpers
  2009-12-04 15:55 [RFC PATCH v2 0/8] Remote helpers smart transport extensions Ilari Liusvaara
                   ` (5 preceding siblings ...)
  2009-12-04 15:56 ` [RFC PATCH v2 4/8] Support remote helpers implementing smart transports Ilari Liusvaara
@ 2009-12-04 15:56 ` Ilari Liusvaara
  2009-12-04 15:56 ` [RFC PATCH v2 6/8] Remove special casing of http, https and ftp Ilari Liusvaara
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: Ilari Liusvaara @ 2009-12-04 15:56 UTC (permalink / raw)
  To: git

Helpers which support invoke/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>
---
 builtin-archive.c |   17 ++++++++++-------
 1 files changed, 10 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;
 }
-- 
1.6.6.rc1.288.g40e67

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

* [RFC PATCH v2 6/8] Remove special casing of http, https and ftp
  2009-12-04 15:55 [RFC PATCH v2 0/8] Remote helpers smart transport extensions Ilari Liusvaara
                   ` (6 preceding siblings ...)
  2009-12-04 15:56 ` [RFC PATCH v2 5/8] Support remote archive from external protocol helpers Ilari Liusvaara
@ 2009-12-04 15:56 ` Ilari Liusvaara
  2009-12-04 16:05   ` Sverre Rabbelier
  2009-12-04 15:56 ` [RFC PATCH v2 7/8] Add remote helper debug mode Ilari Liusvaara
  2009-12-04 15:56 ` [RFC PATCH v2 8/8] Support mandatory capabilities Ilari Liusvaara
  9 siblings, 1 reply; 20+ messages in thread
From: Ilari Liusvaara @ 2009-12-04 15:56 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>
---
 .gitignore  |    4 ++++
 Makefile    |   24 ++++++++++++++++++++++--
 transport.c |    8 --------
 3 files changed, 26 insertions(+), 10 deletions(-)

diff --git a/.gitignore b/.gitignore
index 7cc54b4..7c79f97 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 42744a4..1332225 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)
 
@@ -1853,6 +1866,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/" || \
@@ -1866,6 +1880,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 1488cfe..872cc30 100644
--- a/transport.c
+++ b/transport.c
@@ -944,14 +944,6 @@ struct transport *transport_get(struct remote *remote, const char *url)
 
 		data->conn = NULL;
 		data->virtual_connected = 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.288.g40e67

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

* [RFC PATCH v2 7/8] Add remote helper debug mode
  2009-12-04 15:55 [RFC PATCH v2 0/8] Remote helpers smart transport extensions Ilari Liusvaara
                   ` (7 preceding siblings ...)
  2009-12-04 15:56 ` [RFC PATCH v2 6/8] Remove special casing of http, https and ftp Ilari Liusvaara
@ 2009-12-04 15:56 ` Ilari Liusvaara
  2009-12-04 16:03   ` Sverre Rabbelier
  2009-12-04 15:56 ` [RFC PATCH v2 8/8] Support mandatory capabilities Ilari Liusvaara
  9 siblings, 1 reply; 20+ messages in thread
From: Ilari Liusvaara @ 2009-12-04 15:56 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>
---
 transport-helper.c |   96 ++++++++++++++++++++++++++++++++++++----------------
 1 files changed, 67 insertions(+), 29 deletions(-)

diff --git a/transport-helper.c b/transport-helper.c
index 21aa4ab..03920bb 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -8,6 +8,8 @@
 #include "quote.h"
 #include "remote.h"
 
+static int debug = 0;
+
 struct helper_data
 {
 	const char *name;
@@ -24,6 +26,46 @@ struct helper_data
 	struct git_transport_options gitoptions;
 };
 
+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* helper_disown(struct transport *transport)
 {
 	struct helper_data *data = transport->data;
@@ -95,14 +137,14 @@ static struct child_process *get_helper(struct transport *transport)
 	data->out = xfdopen(duped, "r");
 	setvbuf(data->out, NULL, _IONBF, 0);
 
-	write_str_in_full(helper->in, "capabilities\n");
+	write_constant(helper->in, "capabilities\n");
 
 	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"))
@@ -130,14 +172,19 @@ 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);
 		close(data->helper->out);
 		fclose(data->out);
@@ -166,10 +213,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;
 
@@ -192,12 +240,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;
@@ -257,13 +300,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;
@@ -298,12 +338,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");
 
@@ -313,7 +354,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);
@@ -364,10 +405,7 @@ static int _process_connect(struct transport *transport,
 	} else
 		return 0;
 
-	write_in_full(helper->in, cmdbuf.buf, cmdbuf.len);
-	strbuf_reset(&cmdbuf);
-	if (strbuf_getline(&cmdbuf, data->out, '\n') == EOF)
-		exit(128); /* child died, message supplied already */
+	xchgline(data, &cmdbuf);
 	if(!strcmp(cmdbuf.buf, ""))
 		return 1;
 	else if(!strcmp(cmdbuf.buf, "FALLBACK"))
@@ -500,17 +538,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;
 
@@ -605,8 +640,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;
@@ -631,6 +665,7 @@ 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)
@@ -644,6 +679,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.288.g40e67

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

* [RFC PATCH v2 8/8] Support mandatory capabilities
  2009-12-04 15:55 [RFC PATCH v2 0/8] Remote helpers smart transport extensions Ilari Liusvaara
                   ` (8 preceding siblings ...)
  2009-12-04 15:56 ` [RFC PATCH v2 7/8] Add remote helper debug mode Ilari Liusvaara
@ 2009-12-04 15:56 ` Ilari Liusvaara
  9 siblings, 0 replies; 20+ messages in thread
From: Ilari Liusvaara @ 2009-12-04 15:56 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>
---
 Documentation/git-remote-helpers.txt |    5 ++++-
 transport-helper.c                   |   29 ++++++++++++++++++++++-------
 2 files changed, 26 insertions(+), 8 deletions(-)

diff --git a/Documentation/git-remote-helpers.txt b/Documentation/git-remote-helpers.txt
index 91cd9eb..6af6824 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 03920bb..9ddcdc3 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -140,27 +140,42 @@ static struct child_process *get_helper(struct transport *transport)
 	write_constant(helper->in, "capabilities\n");
 
 	while (1) {
+		const char* capname;
+		int mandatory = 0;
 		recvline(data, &buf);
 
 		if (!*buf.buf)
 			break;
-		if(debug) fprintf(stderr, "Debug: Got cap %s\n", buf.buf);
-		if (!strcmp(buf.buf, "fetch"))
+
+		if(*buf.buf == '*') {
+			capname = buf.buf + 1;
+			mandatory = 1;
+		} else
+			capname = buf.buf;
+
+		if(debug) 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 "));
 		}
-		if (!strcmp(buf.buf, "connect"))
+		else if (!strcmp(capname, "connect"))
 			data->connect = 1;
+		else if (mandatory) {
+			fflush(stderr);
+			die("Unknown madatory capability %s. This remote "
+			    "helper probably needs newer version of Git.\n",
+			    capname);
+		}
 	}
 	if (refspecs) {
 		int i;
-- 
1.6.6.rc1.288.g40e67

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

* Re: [RFC PATCH v2 1/8] Pass unknown protocols to external protocol  handlers
  2009-12-04 15:56 ` [RFC PATCH v2 1/8] Pass unknown protocols to external protocol handlers Ilari Liusvaara
@ 2009-12-04 16:01   ` Sverre Rabbelier
  2009-12-04 17:55   ` Shawn O. Pearce
  1 sibling, 0 replies; 20+ messages in thread
From: Sverre Rabbelier @ 2009-12-04 16:01 UTC (permalink / raw)
  To: Ilari Liusvaara; +Cc: git

Heya,

On Fri, Dec 4, 2009 at 16:56, Ilari Liusvaara
<ilari.liusvaara@elisanet.fi> wrote:
> 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.

Haven't looked at the patch itself, but I like the idea, a lot.

-- 
Cheers,

Sverre Rabbelier

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

* Re: [RFC PATCH v2 7/8] Add remote helper debug mode
  2009-12-04 15:56 ` [RFC PATCH v2 7/8] Add remote helper debug mode Ilari Liusvaara
@ 2009-12-04 16:03   ` Sverre Rabbelier
  0 siblings, 0 replies; 20+ messages in thread
From: Sverre Rabbelier @ 2009-12-04 16:03 UTC (permalink / raw)
  To: Ilari Liusvaara; +Cc: git

Heya,

On Fri, Dec 4, 2009 at 16:56, Ilari Liusvaara
<ilari.liusvaara@elisanet.fi> wrote:
> Remote helpers deadlock easily, so support debug mode which shows the
> interaction steps.

You should move this to the beginning of the series, so that it can be
merged even if your other patches are not :).

-- 
Cheers,

Sverre Rabbelier

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

* Re: [RFC PATCH v2 6/8] Remove special casing of http, https and ftp
  2009-12-04 15:56 ` [RFC PATCH v2 6/8] Remove special casing of http, https and ftp Ilari Liusvaara
@ 2009-12-04 16:05   ` Sverre Rabbelier
  0 siblings, 0 replies; 20+ messages in thread
From: Sverre Rabbelier @ 2009-12-04 16:05 UTC (permalink / raw)
  To: Ilari Liusvaara; +Cc: git

Heya,

On Fri, Dec 4, 2009 at 16:56, Ilari Liusvaara
<ilari.liusvaara@elisanet.fi> wrote:
> HTTP, HTTPS and FTP are no longer special to transport code. Also
> add support for FTPS (curl supports it so it is easy).

As I said earlier, I like this idea, again, without having looked at
the patch itself so I cannot comment on the implementation :).

-- 
Cheers,

Sverre Rabbelier

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

* Re: [RFC PATCH v2 1/8] Pass unknown protocols to external protocol handlers
  2009-12-04 15:56 ` [RFC PATCH v2 1/8] Pass unknown protocols to external protocol handlers Ilari Liusvaara
  2009-12-04 16:01   ` Sverre Rabbelier
@ 2009-12-04 17:55   ` Shawn O. Pearce
  2009-12-05 13:17     ` Ilari Liusvaara
  1 sibling, 1 reply; 20+ messages in thread
From: Shawn O. Pearce @ 2009-12-04 17:55 UTC (permalink / raw)
  To: Ilari Liusvaara; +Cc: git

Ilari Liusvaara <ilari.liusvaara@elisanet.fi> wrote:
> 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.
...

Uh, great, but...

> @@ -30,6 +50,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;
...
> +	/* 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 disowning will require the fd to remain.
> +
> +	   Set the stream to unbuffered because some reads are critical
> +	   in sense that any overreading will cause deadlocks.
> +	*/
> +	if((duped = dup(helper->out)) < 0)
> +		die_errno("Can't dup helper output fd");
> +	data->out = xfdopen(duped, "r");
> +	setvbuf(data->out, NULL, _IONBF, 0);
> +

This is an entirely unrelated change.  Please split it into its
own commit so its easier to review, test, blah blah blah.

-- 
Shawn.

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

* Re: [RFC PATCH v2 3/8] Support taking over transports
  2009-12-04 15:56 ` [RFC PATCH v2 3/8] Support taking over transports Ilari Liusvaara
@ 2009-12-04 18:27   ` Shawn O. Pearce
  2009-12-05 13:18     ` Ilari Liusvaara
  0 siblings, 1 reply; 20+ messages in thread
From: Shawn O. Pearce @ 2009-12-04 18:27 UTC (permalink / raw)
  To: Ilari Liusvaara; +Cc: git

Ilari Liusvaara <ilari.liusvaara@elisanet.fi> wrote:
> Add support for taking over transports that turn out to be smart.

I really don't like this disown strategy and its magic ref return
value from fetch.
 
> @@ -1020,7 +1089,13 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs)
>  			heads[nr_heads++] = rm;
>  	}
>  
> +retry:
>  	rc = transport->fetch(transport, nr_heads, heads);
> +	if(rc == TRANSPORT_LAYER6_READY) {
> +		git_take_over_transport(transport);
> +		goto retry;
> +	}

Why can't you expose git_take_over_transport as a public function
and then the transport-helper.c code can instead do:

	... setup connect with helper ...
	transport_takeover(transport, child);
	return transport->fetch(....);

Would this make the code simpler?

-- 
Shawn.

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

* Re: [RFC PATCH v2 4/8] Support remote helpers implementing smart transports
  2009-12-04 15:56 ` [RFC PATCH v2 4/8] Support remote helpers implementing smart transports Ilari Liusvaara
@ 2009-12-04 18:37   ` Shawn O. Pearce
  2009-12-05 13:16     ` Ilari Liusvaara
  0 siblings, 1 reply; 20+ messages in thread
From: Shawn O. Pearce @ 2009-12-04 18:37 UTC (permalink / raw)
  To: Ilari Liusvaara; +Cc: git

Ilari Liusvaara <ilari.liusvaara@elisanet.fi> wrote:
>  
> +'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,

Why not 'fallback' to remain consistent with this protocol and many
others in git where we stick to lowercase ASCII?

> +	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. Note that to prevent deadlocking, all read data
> +	should be immediately flushed to outgoing connection (excepting
> +	remote initial advertisments, which should be flushed on first
> +	flush packet (0000 as length) encountered.

Why is the initial advertisement special?  If the helper always
flushes both sides, it shouldn't ever deadlock the protocol.  Also,
note that a helper should be able to implement a tiny delay like
Nagle's algorithm does in TCP.  It just can't sit on a byte forever.

> @@ -72,7 +73,10 @@ static struct child_process *get_helper(struct transport *transport)
>  	helper->argv = xcalloc(4, sizeof(*helper->argv));
>  	strbuf_addf(&buf, "remote-%s", data->name);
>  	helper->argv[0] = strbuf_detach(&buf, NULL);
> -	helper->argv[1] = transport->remote->name;
> +	if(transport->remote)
> +		helper->argv[1] = transport->remote->name;
> +	else
> +		helper->argv[1] = "";

This hunk appears to be unrelated.  And actually, if transport has
no remote, shouldn't the arg here be NULL so the helper gets only
1 argument and not 2 arguments?

> +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;
> +
> +	helper = get_helper(transport);
> +
> +	/* Handle --upload-pack and friends. This is fire and forget...
> +	   just warn if it fails. */
> +	if(exec && 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");
> +	}

I think exec winds up defaulting to name if --upload-pack was not
used on the command line, and remote.$name.uploadpack was not set.
See transport.c where you initialize the git options struct, these
fields were defaulted in.

My point is, we shouldn't send option servpath to the helper if
name is equal to servpath, because the helper might not support
servpath and the option command will issue a warning above for no
reason at all.

-- 
Shawn.

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

* Re: [RFC PATCH v2 4/8] Support remote helpers implementing smart transports
  2009-12-04 18:37   ` Shawn O. Pearce
@ 2009-12-05 13:16     ` Ilari Liusvaara
  0 siblings, 0 replies; 20+ messages in thread
From: Ilari Liusvaara @ 2009-12-05 13:16 UTC (permalink / raw)
  To: Shawn O. Pearce; +Cc: git

On Fri, Dec 04, 2009 at 10:37:13AM -0800, Shawn O. Pearce wrote:
> Ilari Liusvaara <ilari.liusvaara@elisanet.fi> wrote:
> 
> Why not 'fallback' to remain consistent with this protocol and many
> others in git where we stick to lowercase ASCII?

Changed.
 
> Why is the initial advertisement special?  If the helper always
> flushes both sides, it shouldn't ever deadlock the protocol.  Also,
> note that a helper should be able to implement a tiny delay like
> Nagle's algorithm does in TCP.  It just can't sit on a byte forever.

That's just extremely badly worded. The point is that only place where
buffering has even seemed useful is initial adverts. Those can
really trigger large numbers of small transfers if unbuffered.

Granted, small delay should work quite well.

I'll reword it.

> This hunk appears to be unrelated.  And actually, if transport has
> no remote, shouldn't the arg here be NULL so the helper gets only
> 1 argument and not 2 arguments?

Actually, it can't be NULL (the code would have already die()'d in
that case). Fixed.

> I think exec winds up defaulting to name if --upload-pack was not
> used on the command line, and remote.$name.uploadpack was not set.
> See transport.c where you initialize the git options struct, these
> fields were defaulted in.

Actually, there was bug in initializing git options struct. It was
properly initialized if one used foo://, but not with foo::bar://.
Tripping it caused NULL exec to be passed if you didn't override.
I fixed that bug and now the servpath code has just strcmp.

> My point is, we shouldn't send option servpath to the helper if
> name is equal to servpath, because the helper might not support
> servpath and the option command will issue a warning above for no
> reason at all.

This is exactly what happens (and yes, I tested). No --receive-pack
doesn't trip warning, with --receive-pack it does.

-Ilari

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

* Re: [RFC PATCH v2 1/8] Pass unknown protocols to external protocol handlers
  2009-12-04 17:55   ` Shawn O. Pearce
@ 2009-12-05 13:17     ` Ilari Liusvaara
  0 siblings, 0 replies; 20+ messages in thread
From: Ilari Liusvaara @ 2009-12-05 13:17 UTC (permalink / raw)
  To: Shawn O. Pearce; +Cc: git

On Fri, Dec 04, 2009 at 09:55:45AM -0800, Shawn O. Pearce wrote:

<fd dupping code>
 
> This is an entirely unrelated change.  Please split it into its
> own commit so its easier to review, test, blah blah blah.

Moved to 3/8, since that's where it actually will be needed (latter
rearrangement renumbered that patch to 5/8).

-Ilari

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

* Re: [RFC PATCH v2 3/8] Support taking over transports
  2009-12-04 18:27   ` Shawn O. Pearce
@ 2009-12-05 13:18     ` Ilari Liusvaara
  0 siblings, 0 replies; 20+ messages in thread
From: Ilari Liusvaara @ 2009-12-05 13:18 UTC (permalink / raw)
  To: Shawn O. Pearce; +Cc: git

On Fri, Dec 04, 2009 at 10:27:53AM -0800, Shawn O. Pearce wrote:
> Ilari Liusvaara <ilari.liusvaara@elisanet.fi> wrote:
> > Add support for taking over transports that turn out to be smart.
> 
> Why can't you expose git_take_over_transport as a public function
> and then the transport-helper.c code can instead do:
> 
> 	... setup connect with helper ...
> 	transport_takeover(transport, child);
> 	return transport->fetch(....);
> 
> Would this make the code simpler?

The code complexity difference is not big. The magic return was for
historic reasons that no longer apply (and didn't get fixed when
corresponding upstream changes happned).

Namely, the push/push_refs impedance mismatch. Helpers used push,
smart transports used push_refs. But now that both use push_refs,
the handler can be invoked directly from push_refs of helper.

Fixed.

-Ilari

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

end of thread, other threads:[~2009-12-05 13:18 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-12-04 15:55 [RFC PATCH v2 0/8] Remote helpers smart transport extensions Ilari Liusvaara
2009-12-04 15:55 ` Ilari Liusvaara
2009-12-04 15:56 ` Ilari Liusvaara
2009-12-04 15:56 ` [RFC PATCH v2 1/8] Pass unknown protocols to external protocol handlers Ilari Liusvaara
2009-12-04 16:01   ` Sverre Rabbelier
2009-12-04 17:55   ` Shawn O. Pearce
2009-12-05 13:17     ` Ilari Liusvaara
2009-12-04 15:56 ` [RFC PATCH v2 2/8] Refactor git transport options parsing Ilari Liusvaara
2009-12-04 15:56 ` [RFC PATCH v2 3/8] Support taking over transports Ilari Liusvaara
2009-12-04 18:27   ` Shawn O. Pearce
2009-12-05 13:18     ` Ilari Liusvaara
2009-12-04 15:56 ` [RFC PATCH v2 4/8] Support remote helpers implementing smart transports Ilari Liusvaara
2009-12-04 18:37   ` Shawn O. Pearce
2009-12-05 13:16     ` Ilari Liusvaara
2009-12-04 15:56 ` [RFC PATCH v2 5/8] Support remote archive from external protocol helpers Ilari Liusvaara
2009-12-04 15:56 ` [RFC PATCH v2 6/8] Remove special casing of http, https and ftp Ilari Liusvaara
2009-12-04 16:05   ` Sverre Rabbelier
2009-12-04 15:56 ` [RFC PATCH v2 7/8] Add remote helper debug mode Ilari Liusvaara
2009-12-04 16:03   ` Sverre Rabbelier
2009-12-04 15:56 ` [RFC PATCH v2 8/8] Support mandatory capabilities Ilari Liusvaara

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