about summary refs log tree commit diff
path: root/third_party/git/serve.c
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/git/serve.c')
-rw-r--r--third_party/git/serve.c47
1 files changed, 38 insertions, 9 deletions
diff --git a/third_party/git/serve.c b/third_party/git/serve.c
index 317256c1a493..f6341206c4e3 100644
--- a/third_party/git/serve.c
+++ b/third_party/git/serve.c
@@ -3,7 +3,7 @@
 #include "config.h"
 #include "pkt-line.h"
 #include "version.h"
-#include "argv-array.h"
+#include "strvec.h"
 #include "ls-refs.h"
 #include "serve.h"
 #include "upload-pack.h"
@@ -22,6 +22,14 @@ static int agent_advertise(struct repository *r,
 	return 1;
 }
 
+static int object_format_advertise(struct repository *r,
+				   struct strbuf *value)
+{
+	if (value)
+		strbuf_addstr(value, r->hash_algo->name);
+	return 1;
+}
+
 struct protocol_capability {
 	/*
 	 * The name of the capability.  The server uses this name when
@@ -48,7 +56,7 @@ struct protocol_capability {
 	 * This field should be NULL for capabilities which are not commands.
 	 */
 	int (*command)(struct repository *r,
-		       struct argv_array *keys,
+		       struct strvec *keys,
 		       struct packet_reader *request);
 };
 
@@ -57,6 +65,7 @@ static struct protocol_capability capabilities[] = {
 	{ "ls-refs", always_advertise, ls_refs },
 	{ "fetch", upload_pack_advertise, upload_pack_v2 },
 	{ "server-option", always_advertise, NULL },
+	{ "object-format", object_format_advertise, NULL },
 };
 
 static void advertise_capabilities(void)
@@ -133,13 +142,13 @@ static int is_command(const char *key, struct protocol_capability **command)
 	return 0;
 }
 
-int has_capability(const struct argv_array *keys, const char *capability,
+int has_capability(const struct strvec *keys, const char *capability,
 		   const char **value)
 {
 	int i;
-	for (i = 0; i < keys->argc; i++) {
+	for (i = 0; i < keys->nr; i++) {
 		const char *out;
-		if (skip_prefix(keys->argv[i], capability, &out) &&
+		if (skip_prefix(keys->v[i], capability, &out) &&
 		    (!*out || *out == '=')) {
 			if (value) {
 				if (*out == '=')
@@ -153,6 +162,22 @@ int has_capability(const struct argv_array *keys, const char *capability,
 	return 0;
 }
 
+static void check_algorithm(struct repository *r, struct strvec *keys)
+{
+	int client = GIT_HASH_SHA1, server = hash_algo_by_ptr(r->hash_algo);
+	const char *algo_name;
+
+	if (has_capability(keys, "object-format", &algo_name)) {
+		client = hash_algo_by_name(algo_name);
+		if (client == GIT_HASH_UNKNOWN)
+			die("unknown object format '%s'", algo_name);
+	}
+
+	if (client != server)
+		die("mismatched object format: server %s; client %s\n",
+		    r->hash_algo->name, hash_algos[client].name);
+}
+
 enum request_state {
 	PROCESS_REQUEST_KEYS,
 	PROCESS_REQUEST_DONE,
@@ -162,7 +187,7 @@ static int process_request(void)
 {
 	enum request_state state = PROCESS_REQUEST_KEYS;
 	struct packet_reader reader;
-	struct argv_array keys = ARGV_ARRAY_INIT;
+	struct strvec keys = STRVEC_INIT;
 	struct protocol_capability *command = NULL;
 
 	packet_reader_init(&reader, 0, NULL, 0,
@@ -186,7 +211,7 @@ static int process_request(void)
 			/* collect request; a sequence of keys and values */
 			if (is_command(reader.line, &command) ||
 			    is_valid_capability(reader.line))
-				argv_array_push(&keys, reader.line);
+				strvec_push(&keys, reader.line);
 			else
 				die("unknown capability '%s'", reader.line);
 
@@ -198,7 +223,7 @@ static int process_request(void)
 			 * If no command and no keys were given then the client
 			 * wanted to terminate the connection.
 			 */
-			if (!keys.argc)
+			if (!keys.nr)
 				return 1;
 
 			/*
@@ -217,15 +242,19 @@ static int process_request(void)
 
 			state = PROCESS_REQUEST_DONE;
 			break;
+		case PACKET_READ_RESPONSE_END:
+			BUG("unexpected stateless separator packet");
 		}
 	}
 
 	if (!command)
 		die("no command requested");
 
+	check_algorithm(the_repository, &keys);
+
 	command->command(the_repository, &keys, &reader);
 
-	argv_array_clear(&keys);
+	strvec_clear(&keys);
 	return 0;
 }