git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[RFC PATCH 4/8] Support remote helpers implementing smart transports

From
ILIlari Liusvaara <ilari.liusvaara@elisanet.fi>
Date
Dec 1, 2009, 13:57 UTC
Message-ID
<1259675838-14692-5-git-send-email-ilari.liusvaara@elisanet.fi>
In-Reply-To
<1259675838-14692-1-git-send-email-ilari.liusvaara@elisanet.fi>
Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
---
 Documentation/git-remote-helpers.txt |   30 ++++++++++-
 transport-helper.c                   |   94 +++++++++++++++++++++++++++++++--
 transport.c                          |   21 ++++++++
 transport.h                          |    5 ++
 4 files changed, 143 insertions(+), 7 deletions(-)
diff --git a/Documentation/git-remote-helpers.txt b/Documentation/git-remote-helpers.txt
index 5cfdc0c..adf815c 100644
--- a/Documentation/git-remote-helpers.txt
+++ b/Documentation/git-remote-helpers.txt
@@ -90,6 +90,28 @@ Supported if the helper has the "push" capability.
 +
 Supported if the helper has the "import" capability.
 
+'connect-r' <service>::
+	Connects to given service. Stdin and stdout of helper are
+	connected to specified service (no git or git- prefixes are used,
+	so e.g. fetching uses 'upload-pack' as service) on remote side.
+	Valid replies to this command are 'OK' (connection established),
+	'FALLBACK' (no smart transport support, fall back to dumb
+	transports) and 'ERROR' (can't connect, don't bother trying to
+	fall back). After line feed terminating the OK 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.
++
+Supported if the helper has the "connect-r" capability. Not used if
+helper has the "invoke-r" capability, as invoke is preferred to connect.
+
+'invoke-r' <cmdlength> <cmd>::
+	Like connect-r command, but instead of service name, command
+	line is given. The length of command field is given in command
+	length field.
++
+Supported if the helper has the "invoke-r" 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 +145,12 @@ CAPABILITIES
 	all, it must cover all refs reported by the list command; if
 	it is not used, it is effectively "*:*"
 
+'connect-r'::
+	This helper supports the 'connect-r' command.
+
+'invoke-r'::
+	This helper supports the 'invoke-r' command.
+
 REF LIST ATTRIBUTES
 -------------------
 
@@ -167,7 +195,7 @@ OPTIONS
 
 Documentation
 -------------
-Documentation by Daniel Barkalow.
+Documentation by Daniel Barkalow and Ilari Liusvaara
 
 GIT
 ---
diff --git a/transport-helper.c b/transport-helper.c
index 777ecbb..0e4da79 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -16,7 +16,9 @@ struct helper_data
 	unsigned fetch : 1,
 		import : 1,
 		option : 1,
-		push : 1;
+		push : 1,
+		connect_r : 1,
+		invoke_r : 1;
 	/* These go from remote name (as in "list") to private name */
 	struct refspec *refspecs;
 	int refspec_nr;
@@ -75,7 +77,10 @@ static struct child_process *get_helper(struct transport *transport)
 	   would run and print totally inapporiate error message. */
 	strbuf_addf(&buf, "git-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 = 0;
 	if (start_command(helper))
@@ -125,6 +130,10 @@ static struct child_process *get_helper(struct transport *transport)
 				   refspec_alloc);
 			refspecs[refspec_nr++] = strdup(buf.buf + strlen("refspec "));
 		}
+		if (!strcmp(buf.buf, "connect-r"))
+			data->connect_r = 1;
+		if (!strcmp(buf.buf, "invoke-r"))
+			data->invoke_r = 1;
 	}
 	if (refspecs) {
 		int i;
@@ -344,12 +353,83 @@ static int fetch_with_import(struct transport *transport,
 	return 0;
 }
 
+static int _process_connect_or_invoke(struct transport *transport,
+				      const char *name, const char *exec)
+{
+	struct helper_data *data = transport->data;
+	struct strbuf cmdbuf = STRBUF_INIT;
+	struct child_process *helper;
+
+	helper = get_helper(transport);
+
+	if(data->invoke_r) {
+		strbuf_addf(&cmdbuf, "invoke-r %i %s\n",
+			    strlen(exec), exec);
+	} else if(data->connect_r) {
+		strbuf_addf(&cmdbuf, "connect-r %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, "OK"))
+		return 1;
+	else if(!strcmp(cmdbuf.buf, "FALLBACK"))
+		return 0;
+	else if(!strcmp(cmdbuf.buf, "ERROR"))
+		exit(128); /* Error already suppiled. */
+	else
+		die("Unknown response to invoke/connect: %s",
+			cmdbuf.buf);
+
+	return 0;	/* Shouldn't be here. */
+}
+
+static int process_connect_or_invoke(struct transport* transport,
+				     int for_push)
+{
+	struct helper_data *data = transport->data;
+	const char *name;
+	const char *exec;
+
+	name = for_push ? "receive-pack" : "upload-pack";
+	if(for_push)
+		exec = data->gitoptions.receivepack;
+	else
+		exec = data->gitoptions.uploadpack;
+
+	return _process_connect_or_invoke(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 invoke_r and connect_r are inited. */
+	get_helper(transport);
+	if(!data->invoke_r && !data->connect_r)
+		die("Operation not supported by protocol.");
+
+	if(!_process_connect_or_invoke(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_or_invoke(transport, 0))
+		return TRANSPORT_LAYER6_READY;
+
 	count = 0;
 	for (i = 0; i < nr_heads; i++)
 		if (!(to_fetch[i]->status & REF_STATUS_UPTODATE))
@@ -377,6 +457,9 @@ static int push_refs(struct transport *transport,
 	struct child_process *helper;
 	struct ref *ref;
 
+	if(process_connect_or_invoke(transport, 1))
+		return TRANSPORT_LAYER6_READY;
+
 	if (!remote_refs)
 		return 0;
 
@@ -520,10 +603,8 @@ static struct ref *get_refs_list(struct transport *transport, int for_push)
 
 	helper = get_helper(transport);
 
-	if (data->push && for_push)
-		write_str_in_full(helper->in, "list for-push\n");
-	else
-		write_str_in_full(helper->in, "list\n");
+	if(process_connect_or_invoke(transport, for_push))
+		return &special_transport_layer6_ready;
 
 	while (1) {
 		char *eov, *eon;
@@ -572,6 +653,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 09e4c97..a32f405 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.rc0.64.g5593e
Previous: Ilari LiusvaaraNext: Shawn O. Pearce
Message 5 of 42 in “Git remote helpers to implement smart transports.”
  1. 0/8 Git remote helpers to implement smart transports.Ilari Liusvaara, Dec 1, 2009
  2. 1/8 Pass unknown protocols to external protocol handlersIlari Liusvaara, Dec 1, 2009
  3. 2/8 Refactor git transport options parsingIlari Liusvaara, Dec 1, 2009
  4. 3/8 Support taking over transportsIlari Liusvaara, Dec 1, 2009
  5. 4/8 Support remote helpers implementing smart transportsIlari Liusvaara, Dec 1, 2009
  6. Shawn O. PearceDec 1, 2009
  7. Ilari LiusvaaraDec 2, 2009
  8. Shawn O. PearceDec 2, 2009
  9. Ilari LiusvaaraDec 2, 2009
  10. Shawn O. PearceDec 3, 2009
  11. Shawn O. PearceDec 2, 2009
  12. 5/8 Support remote archive from external protocol helpersIlari Liusvaara, Dec 1, 2009
  13. 6/8 Remove special casing of http, https and ftpIlari Liusvaara, Dec 1, 2009
  14. Shawn O. PearceDec 1, 2009
  15. Ilari LiusvaaraDec 1, 2009
  16. Daniel BarkalowDec 1, 2009
  17. Ilari LiusvaaraDec 2, 2009
  18. 7/8 Add remote helper debug modeIlari Liusvaara, Dec 1, 2009
  19. 8/8 Support mandatory capabilitiesIlari Liusvaara, Dec 1, 2009
  20. Sverre RabbelierDec 1, 2009
  21. Shawn O. PearceDec 1, 2009
  22. Ilari LiusvaaraDec 1, 2009
  23. Shawn O. PearceDec 1, 2009
  24. Junio C HamanoDec 1, 2009
  25. Shawn O. PearceDec 1, 2009
  26. Ilari LiusvaaraDec 2, 2009
  27. Junio C HamanoDec 2, 2009
  28. Ilari LiusvaaraDec 2, 2009
  29. Junio C HamanoDec 2, 2009
  30. Johannes SchindelinDec 2, 2009
  31. Sverre RabbelierDec 2, 2009
  32. Junio C HamanoDec 2, 2009
  33. Sverre RabbelierDec 2, 2009
  34. Junio C HamanoDec 2, 2009
  35. Sverre RabbelierDec 2, 2009
  36. Junio C HamanoDec 2, 2009
  37. Jeff KingDec 2, 2009
  38. Ilari LiusvaaraDec 2, 2009
  39. Junio C HamanoDec 2, 2009
  40. Ilari LiusvaaraDec 2, 2009
  41. Ilari LiusvaaraDec 2, 2009
  42. Ilari LiusvaaraDec 2, 2009

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.