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

[WIP 14/15] upload_pack: introduce fetch server command

From
BWBrandon Williams <bmwill@google.com>
Date
Dec 4, 2017, 23:58 UTC
Message-ID
<20171204235823.63299-15-bmwill@google.com>
In-Reply-To
<20171204235823.63299-1-bmwill@google.com>
Introduce the 'fetch' server command.
Signed-off-by: Brandon Williams <bmwill@google.com>
---
 serve.c       |   2 +
 upload-pack.c | 223 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 upload-pack.h |   9 +++
 3 files changed, 234 insertions(+)
 create mode 100644 upload-pack.h
diff --git a/serve.c b/serve.c
index 36f77c365..7823ef0d9 100644
--- a/serve.c
+++ b/serve.c
@@ -6,6 +6,7 @@
 #include "argv-array.h"
 #include "ls-refs.h"
 #include "serve.h"
+#include "upload-pack.h"
 
 static int always_advertise(struct repository *r,
 			    struct strbuf *value)
@@ -34,6 +35,7 @@ struct protocol_capability {
 static struct protocol_capability capabilities[] = {
 	{ "agent", 0, agent_advertise, NULL },
 	{ "ls-refs", 0, always_advertise, ls_refs },
+	{ "fetch", 0, always_advertise, upload_pack_v2 },
 };
 
 static void advertise_capabilities(void)
diff --git a/upload-pack.c b/upload-pack.c
index 3296831f8..dc4421ab3 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -20,6 +20,7 @@
 #include "prio-queue.h"
 #include "protocol.h"
 #include "serve.h"
+#include "upload-pack.h"
 
 static const char * const upload_pack_usage[] = {
 	N_("git upload-pack [<options>] <dir>"),
@@ -1041,6 +1042,228 @@ static void upload_pack(void)
 	}
 }
 
+static int process_want(const char *line)
+{
+	const char *arg;
+	if (skip_prefix(line, "want ", &arg)) {
+		struct object_id oid;
+		struct object *o;
+
+		if (get_oid_hex(arg, &oid))
+			die("git upload-pack: protocol error, "
+			    "expected to get oid, not '%s'", line);
+
+		o = parse_object(&oid);
+		if (!o) {
+			packet_write_fmt(1,
+					 "ERR upload-pack: not our ref %s",
+					 oid_to_hex(&oid));
+			die("git upload-pack: not our ref %s",
+			    oid_to_hex(&oid));
+		}
+
+		if (!(o->flags & WANTED)) {
+			o->flags |= WANTED;
+			add_object_array(o, NULL, &want_obj);
+		}
+
+		return 1;
+	}
+
+	return 0;
+}
+
+static int parse_have(const char *line, struct oid_array *haves)
+{
+	const char *arg;
+	if (skip_prefix(line, "have ", &arg)) {
+		struct object_id oid;
+
+		if (get_oid_hex(arg, &oid))
+			die("git upload-pack: expected SHA1 object, got '%s'", arg);
+		oid_array_append(haves, &oid);
+		return 1;
+	}
+
+	return 0;
+}
+
+static void process_args(struct argv_array *args, struct oid_array *haves_oid)
+{
+	int i;
+
+	for (i = 0; i < args->argc; i++) {
+		const char *arg = args->argv[i];
+
+		/* process want */
+		if (process_want(arg))
+			continue;
+		/* process have line */
+		if (parse_have(arg, haves_oid))
+			continue;
+
+		/* process args like thin-pack */
+		if (!strcmp(arg, "thin-pack")) {
+			use_thin_pack = 1;
+			continue;
+		}
+		if (!strcmp(arg, "ofs-delta")) {
+			use_ofs_delta = 1;
+			continue;
+		}
+		if (!strcmp(arg, "no-progress")) {
+			no_progress = 1;
+			continue;
+		}
+		if (!strcmp(arg, "include-tag")) {
+			use_include_tag = 1;
+			continue;
+		}
+
+		/* ignore unknown lines maybe? */
+		die("unexpect line: '%s'", arg);
+	}
+}
+
+static int process_haves(struct oid_array *haves, struct oid_array *common)
+{
+	int i;
+
+	/* Process haves */
+	for (i = 0; i < haves->nr; i++) {
+		const struct object_id *oid = &haves->oid[i];
+		struct object *o;
+		int we_knew_they_have = 0;
+
+		if (!has_object_file(oid))
+			continue;
+
+		oid_array_append(common, oid);
+
+		o = parse_object(oid);
+		if (!o)
+			die("oops (%s)", oid_to_hex(oid));
+		if (o->type == OBJ_COMMIT) {
+			struct commit_list *parents;
+			struct commit *commit = (struct commit *)o;
+			if (o->flags & THEY_HAVE)
+				we_knew_they_have = 1;
+			else
+				o->flags |= THEY_HAVE;
+			if (!oldest_have || (commit->date < oldest_have))
+				oldest_have = commit->date;
+			for (parents = commit->parents;
+			     parents;
+			     parents = parents->next)
+				parents->item->object.flags |= THEY_HAVE;
+		}
+		if (!we_knew_they_have)
+			add_object_array(o, NULL, &have_obj);
+	}
+
+	return 0;
+}
+
+static int send_acks(struct oid_array *acks, struct strbuf *response)
+{
+	int i;
+	/* Send Acks */
+	if (!acks->nr)
+		packet_buf_write(response, "NAK\n");
+
+	for (i = 0; i < acks->nr; i++) {
+		packet_buf_write(response, "ACK %s common\n",
+				 oid_to_hex(&acks->oid[i]));
+	}
+
+	if (ok_to_give_up()) {
+		/* Send Ready */
+		packet_buf_write(response, "ACK %s ready\n",
+				 oid_to_hex(&acks->oid[i-1]));
+		return 1;
+	}
+
+	return 0;
+}
+
+#define COMMON_START 0
+#define COMMON_DONE 1
+#define COMMON_READ_HAVES 2
+#define COMMON_PROCESS_HAVES 3
+
+static int get_common_commits_v2(struct oid_array *haves_oid)
+{
+	struct packet_reader reader;
+	int done = 0;
+	int state = haves_oid->nr ? COMMON_PROCESS_HAVES : COMMON_DONE;
+	struct oid_array common = OID_ARRAY_INIT;
+	struct strbuf response = STRBUF_INIT;
+	packet_reader_init(&reader, 0, NULL, 0);
+
+	while (state != COMMON_DONE) {
+		switch (state) {
+		case COMMON_START:
+			break;
+		case COMMON_READ_HAVES:
+			while (packet_reader_read(&reader) == PACKET_READ_NORMAL) {
+
+				if (parse_have(reader.line, haves_oid))
+					continue;
+				if (!strcmp(reader.line, "done")) {
+					done = 1;
+					continue;
+				}
+			}
+			if (reader.status != PACKET_READ_FLUSH)
+				die("ERROR");
+
+			state = COMMON_PROCESS_HAVES;
+			break;
+		case COMMON_PROCESS_HAVES:
+			process_haves(haves_oid, &common);
+			if (done) {
+				state = COMMON_DONE;
+			} else if (send_acks(&common, &response)) {
+				packet_buf_delim(&response);
+				state = COMMON_DONE;
+			} else {
+				/* Add Flush */
+				packet_buf_flush(&response);
+				state = COMMON_READ_HAVES;
+			}
+
+			/* Send response */
+			write_or_die(1, response.buf, response.len);
+			strbuf_reset(&response);
+			oid_array_clear(haves_oid);
+			oid_array_clear(&common);
+			break;
+		case COMMON_DONE:
+			break;
+		default:
+			BUG("invalid state");
+		}
+	}
+	return 0;
+}
+
+int upload_pack_v2(struct repository *r, struct argv_array *keys,
+		   struct argv_array *args)
+{
+	struct oid_array haves_oid = OID_ARRAY_INIT;
+	use_sideband = LARGE_PACKET_MAX;
+	process_args(args, &haves_oid);
+
+	if (want_obj.nr) {
+		/* Determine Common Commits */
+		get_common_commits_v2(&haves_oid);
+		/* create packfile */
+		create_pack_file();
+	}
+
+	return 0;
+}
+
 static int upload_pack_config(const char *var, const char *value, void *unused)
 {
 	if (!strcmp("uploadpack.allowtipsha1inwant", var)) {
diff --git a/upload-pack.h b/upload-pack.h
new file mode 100644
index 000000000..54c429563
--- /dev/null
+++ b/upload-pack.h
@@ -0,0 +1,9 @@
+#ifndef UPLOAD_PACK_H
+#define UPLOAD_PACK_H
+
+struct repository;
+struct argv_array;
+extern int upload_pack_v2(struct repository *r, struct argv_array *keys,
+			  struct argv_array *args);
+
+#endif /* UPLOAD_PACK_H */
-- 
2.15.1.424.g9478a66081-goog
Previous: Brandon WilliamsNext: Brandon Williams
Message 45 of 47 in “[RFC] protocol version 2”
  1. Brandon WilliamsOct 20, 2017
  2. Junio C HamanoOct 24, 2017
  3. Brandon WilliamsOct 24, 2017
  4. Junio C HamanoOct 25, 2017
  5. Junio C HamanoOct 26, 2017
  6. Derrick StoleeOct 25, 2017
  7. Brandon WilliamsOct 25, 2017
  8. Philip OakleyOct 28, 2017
  9. Brandon WilliamsOct 31, 2017
  10. Jonathan TanNov 10, 2017
  11. 00/15 protocol version 2Brandon Williams, Dec 4, 2017
  12. 01/15 pkt-line: introduce packet_read_with_statusBrandon Williams, Dec 4, 2017
  13. Stefan BellerDec 7, 2017
  14. Brandon WilliamsDec 8, 2017
  15. 02/15 pkt-line: introduce struct packet_readerBrandon Williams, Dec 4, 2017
  16. Stefan BellerDec 7, 2017
  17. Brandon WilliamsDec 8, 2017
  18. 03/15 pkt-line: add delim packet supportBrandon Williams, Dec 4, 2017
  19. Stefan BellerDec 7, 2017
  20. Brandon WilliamsDec 8, 2017
  21. 04/15 upload-pack: convert to a builtinBrandon Williams, Dec 4, 2017
  22. Junio C HamanoDec 6, 2017
  23. Johannes SchindelinDec 7, 2017
  24. Junio C HamanoDec 8, 2017
  25. Brandon WilliamsDec 8, 2017
  26. 07/15 connect: convert get_remote_heads to use struct packet_readerBrandon Williams, Dec 4, 2017
  27. Junio C HamanoDec 6, 2017
  28. Brandon WilliamsDec 8, 2017
  29. 06/15 transport: use get_refs_via_connect to get refsBrandon Williams, Dec 4, 2017
  30. Junio C HamanoDec 6, 2017
  31. Brandon WilliamsDec 7, 2017
  32. 08/15 connect: discover protocol version outside of get_remote_headsBrandon Williams, Dec 4, 2017
  33. Junio C HamanoDec 7, 2017
  34. Brandon WilliamsDec 7, 2017
  35. Junio C HamanoDec 7, 2017
  36. Brandon WilliamsDec 8, 2017
  37. 13/15 connect: request remote refs using v2Brandon Williams, Dec 4, 2017
  38. 09/15 transport: store protocol versionBrandon Williams, Dec 4, 2017
  39. 11/15 serve: introduce git-serveBrandon Williams, Dec 4, 2017
  40. Junio C HamanoDec 7, 2017
  41. Brandon WilliamsDec 8, 2017
  42. 12/15 ls-refs: introduce ls-refs server commandBrandon Williams, Dec 4, 2017
  43. Philip OakleyDec 13, 2017
  44. 15/15 fetch-pack: perform a fetch using v2Brandon Williams, Dec 4, 2017
  45. 14/15 upload_pack: introduce fetch server commandBrandon Williams, Dec 4, 2017
  46. 10/15 protocol: introduce enum protocol_version value protocol_v2Brandon Williams, Dec 4, 2017
  47. 05/15 upload-pack: factor out processing linesBrandon Williams, Dec 4, 2017

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.