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

[PATCH v2 3/4] archive: implement protocol v2 archive command

From
Josh Steadmon <steadmon@google.com>
Date
Sep 27, 2018, 01:24 UTC
Message-ID
<20180927012455.234876-4-steadmon@google.com>
In-Reply-To
<20180927012455.234876-1-steadmon@google.com>

This adds a new archive command for protocol v2. The command expects arguments in the form "argument X" which are passed unmodified to git-upload-archive--writer.

This command works over the file://, Git, and SSH transports. HTTP support will be added in a separate patch.

NEEDSWORK: this is not backwards-compatible with older clients that set
GIT_PROTOCOL=version=2 or configure protocol.version=2.
Signed-off-by: Josh Steadmon <steadmon@google.com>
---
 Documentation/technical/protocol-v2.txt | 21 +++++++++++-
 builtin/archive.c                       | 45 +++++++++++++++++--------
 builtin/upload-archive.c                | 27 +++++++++++++--
 serve.c                                 |  7 ++++
 t/t5000-tar-tree.sh                     |  7 ++++
 t/t5701-git-serve.sh                    |  1 +
 6 files changed, 90 insertions(+), 18 deletions(-)
diff --git a/Documentation/technical/protocol-v2.txt b/Documentation/technical/protocol-v2.txt
index 09e4e0273f..6126fc5738 100644
--- a/Documentation/technical/protocol-v2.txt
+++ b/Documentation/technical/protocol-v2.txt
@@ -77,7 +77,8 @@ A v2 server would reply:
    S: <capability-advertisement>
 
 Subsequent requests are then made directly to the service
-`$GIT_URL/git-upload-pack`. (This works the same for git-receive-pack).
+`$GIT_URL/git-upload-pack`. (This works the same for git-receive-pack or
+git-upload-archive).
 
  Capability Advertisement
 --------------------------
@@ -168,6 +169,24 @@ printable ASCII characters except space (i.e., the byte range 32 < x <
 and debugging purposes, and MUST NOT be used to programmatically assume
 the presence or absence of particular features.
 
+ archive
+~~~~~~~~~
+
+`archive` is the command to request an archive (such as a tarball) from a server
+over v2.
+
+archive takes the following optional arguments:
+
+    argument <arg>
+       This specifies that <arg> should be passed as an argument to the
+       underlying archive writer.  This option can be repeated to pass
+       additional arguments to the archive writer.
+
+See linkgit:git-archive[1] for allowed values of `<arg>`.
+
+The output of archive is a packet-line stream of the resulting archive, with
+error messages transferred over the sideband channel.
+
  ls-refs
 ~~~~~~~~~
 
diff --git a/builtin/archive.c b/builtin/archive.c
index 4eb547c5b7..f91d222677 100644
--- a/builtin/archive.c
+++ b/builtin/archive.c
@@ -5,9 +5,11 @@
 #include "cache.h"
 #include "builtin.h"
 #include "archive.h"
+#include "connect.h"
 #include "transport.h"
 #include "parse-options.h"
 #include "pkt-line.h"
+#include "protocol.h"
 #include "sideband.h"
 
 static void create_output_file(const char *output_file)
@@ -23,6 +25,13 @@ static void create_output_file(const char *output_file)
 	}
 }
 
+static void do_v2_command_and_cap(int out)
+{
+	packet_write_fmt(out, "command=archive\n");
+	/* Capability list would go here, if we wanted to request any. */
+	packet_delim(out);
+}
+
 static int run_remote_archiver(int argc, const char **argv,
 			       const char *remote, const char *exec,
 			       const char *name_hint)
@@ -32,6 +41,7 @@ static int run_remote_archiver(int argc, const char **argv,
 	struct remote *_remote;
 	struct packet_reader reader;
 	enum packet_read_status status;
+	enum protocol_version version;
 
 	_remote = remote_get(remote);
 	if (!_remote->url[0])
@@ -41,6 +51,11 @@ static int run_remote_archiver(int argc, const char **argv,
 
 	packet_reader_init(&reader, fd[0], NULL, 0, PACKET_READ_CHOMP_NEWLINE);
 
+	version = discover_version(&reader);
+
+	if (version == protocol_v2 && server_supports_v2("archive", 0))
+		do_v2_command_and_cap(fd[1]);
+
 	/*
 	 * Inject a fake --format field at the beginning of the
 	 * arguments, with the format inferred from our output
@@ -56,22 +71,24 @@ static int run_remote_archiver(int argc, const char **argv,
 		packet_write_fmt(fd[1], "argument %s\n", argv[i]);
 	packet_flush(fd[1]);
 
-	status = packet_reader_read(&reader);
-
-	if (status != PACKET_READ_NORMAL || reader.pktlen <= 0)
-		die(_("git archive: expected ACK/NAK, got a flush packet"));
-	if (strcmp(reader.line, "ACK")) {
-		if (starts_with(reader.line, "NACK "))
-			die(_("git archive: NACK %s"), reader.line + 5);
-		if (starts_with(reader.line, "ERR "))
-			die(_("remote error: %s"), reader.line + 4);
-		die(_("git archive: protocol error"));
+	if (version == protocol_v0) {
+		status = packet_reader_read(&reader);
+
+		if (status != PACKET_READ_NORMAL || reader.pktlen <= 0)
+			die(_("git archive: expected ACK/NAK, got a flush packet"));
+		if (strcmp(reader.line, "ACK")) {
+			if (starts_with(reader.line, "NACK "))
+				die(_("git archive: NACK %s"), reader.line + 5);
+			if (starts_with(reader.line, "ERR "))
+				die(_("remote error: %s"), reader.line + 4);
+			die(_("git archive: protocol error"));
+		}
+
+		status = packet_reader_read(&reader);
+		if (status == PACKET_READ_NORMAL && reader.pktlen > 0)
+			die(_("git archive: expected a flush"));
 	}
 
-	status = packet_reader_read(&reader);
-	if (status == PACKET_READ_NORMAL && reader.pktlen > 0)
-		die(_("git archive: expected a flush"));
-
 	/* Now, start reading from fd[0] and spit it out to stdout */
 	rv = recv_sideband("archive", fd[0], 1);
 	rv |= transport_disconnect(transport);
diff --git a/builtin/upload-archive.c b/builtin/upload-archive.c
index 25d9116356..b8a8fb256f 100644
--- a/builtin/upload-archive.c
+++ b/builtin/upload-archive.c
@@ -5,6 +5,8 @@
 #include "builtin.h"
 #include "archive.h"
 #include "pkt-line.h"
+#include "protocol.h"
+#include "serve.h"
 #include "sideband.h"
 #include "run-command.h"
 #include "argv-array.h"
@@ -76,10 +78,24 @@ static ssize_t process_input(int child_fd, int band)
 int cmd_upload_archive(int argc, const char **argv, const char *prefix)
 {
 	struct child_process writer = { argv };
+	enum protocol_version version = determine_protocol_version_server();
 
 	if (argc == 2 && !strcmp(argv[1], "-h"))
 		usage(upload_archive_usage);
 
+	if (version == protocol_v2) {
+		struct serve_options options = SERVE_OPTIONS_INIT;
+
+		/* Send "version 2" message and capabilities. */
+		options.advertise_capabilities = 1;
+		serve(&options);
+
+		/* Process command request and validate client capabilities. */
+		options.advertise_capabilities = 0;
+		options.stateless_rpc = 1;
+		serve(&options);
+	}
+
 	/*
 	 * Set up sideband subprocess.
 	 *
@@ -92,12 +108,17 @@ int cmd_upload_archive(int argc, const char **argv, const char *prefix)
 	writer.git_cmd = 1;
 	if (start_command(&writer)) {
 		int err = errno;
-		packet_write_fmt(1, "NACK unable to spawn subprocess\n");
+		if (version == protocol_v0 || version == protocol_v1)
+			packet_write_fmt(1, "NACK unable to spawn subprocess\n");
+		else if (version == protocol_v2)
+			error_clnt("unable to spawn subprocess\n");
 		die("upload-archive: %s", strerror(err));
 	}
 
-	packet_write_fmt(1, "ACK\n");
-	packet_flush(1);
+	if (version == protocol_v0 || version == protocol_v1) {
+		packet_write_fmt(1, "ACK\n");
+		packet_flush(1);
+	}
 
 	while (1) {
 		struct pollfd pfd[2];
diff --git a/serve.c b/serve.c
index bda085f09c..cef575086f 100644
--- a/serve.c
+++ b/serve.c
@@ -52,8 +52,15 @@ struct protocol_capability {
 		       struct packet_reader *request);
 };
 
+static int noop(struct repository *r, struct argv_array *keys,
+		struct packet_reader *request)
+{
+	return 0;
+}
+
 static struct protocol_capability capabilities[] = {
 	{ "agent", agent_advertise, NULL },
+	{ "archive", always_advertise, noop },
 	{ "ls-refs", always_advertise, ls_refs },
 	{ "fetch", upload_pack_advertise, upload_pack_v2 },
 	{ "server-option", always_advertise, NULL },
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index c408e3a23d..4a76354021 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -138,6 +138,13 @@ test_expect_success \
 
 test_expect_success 'extract archive' 'check_tar b'
 
+test_expect_success 'protocol v2 for remote' '
+	GIT_PROTOCOL="version=2" GIT_TRACE_PACKET="$(pwd)/log" git archive \
+		--remote=. HEAD >v2_remote.tar &&
+	grep "version 2" log
+'
+test_expect_success 'extract v2 archive' 'check_tar v2_remote'
+
 test_expect_success 'git archive --prefix=prefix/' '
 	git archive --prefix=prefix/ HEAD >with_prefix.tar
 '
diff --git a/t/t5701-git-serve.sh b/t/t5701-git-serve.sh
index 75ec79e6cb..f47a14660b 100755
--- a/t/t5701-git-serve.sh
+++ b/t/t5701-git-serve.sh
@@ -8,6 +8,7 @@ test_expect_success 'test capability advertisement' '
 	cat >expect <<-EOF &&
 	version 2
 	agent=git/$(git version | cut -d" " -f3)
+	archive
 	ls-refs
 	fetch=shallow
 	server-option
-- 
2.19.0.605.g01d371f741-goog
Previous: Stefan BellerNext: Josh Steadmon
Message 26 of 33 in “Add proto v2 archive command with HTTP support”
  1. Josh SteadmonSep 12, 2018
  2. 1/3 archive: use packet_reader for communicationsJosh Steadmon, Sep 12, 2018
  3. Stefan BellerSep 12, 2018
  4. Junio C HamanoSep 13, 2018
  5. Junio C HamanoSep 13, 2018
  6. 2/3 archive: implement protocol v2 archive commandJosh Steadmon, Sep 12, 2018
  7. Stefan BellerSep 12, 2018
  8. Ævar Arnfjörð BjarmasonSep 13, 2018
  9. Jonathan NiederSep 14, 2018
  10. Ævar Arnfjörð BjarmasonSep 14, 2018
  11. Junio C HamanoSep 14, 2018
  12. Jonathan NiederSep 14, 2018
  13. Junio C HamanoSep 13, 2018
  14. Jonathan NiederSep 14, 2018
  15. 3/3 archive: allow archive over HTTP(S) with proto v2Josh Steadmon, Sep 12, 2018
  16. Stefan BellerSep 12, 2018
  17. Junio C HamanoSep 13, 2018
  18. Josh SteadmonSep 27, 2018
  19. Jonathan NiederSep 14, 2018
  20. Jonathan NiederSep 14, 2018
  21. 0/4 Add proto v2 archive command with HTTP supportJosh Steadmon, Sep 27, 2018
  22. 1/4 archive: follow test standards around assertionsJosh Steadmon, Sep 27, 2018
  23. Stefan BellerSep 27, 2018
  24. 2/4 archive: use packet_reader for communicationsJosh Steadmon, Sep 27, 2018
  25. Stefan BellerSep 27, 2018
  26. 3/4 archive: implement protocol v2 archive commandJosh Steadmon, Sep 27, 2018
  27. 4/4 archive: allow archive over HTTP(S) with proto v2Josh Steadmon, Sep 27, 2018
  28. Stefan BellerSep 27, 2018
  29. Jonathan NiederSep 27, 2018
  30. Junio C HamanoSep 27, 2018
  31. Josh SteadmonSep 27, 2018
  32. Junio C HamanoSep 28, 2018
  33. Josh SteadmonSep 27, 2018

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.