All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jonathan Tan <jonathantanmy@google.com>
To: git@vger.kernel.org
Cc: Jonathan Tan <jonathantanmy@google.com>
Subject: [RFC 11/14] fetch-pack: support want-ref
Date: Wed, 25 Jan 2017 14:03:04 -0800	[thread overview]
Message-ID: <7b01ee955db8b2d9c6c7e641f7f09436938b2af4.1485381677.git.jonathantanmy@google.com> (raw)
In-Reply-To: <cover.1485381677.git.jonathantanmy@google.com>
In-Reply-To: <cover.1485381677.git.jonathantanmy@google.com>

Teach fetch-pack to use the want-ref mechanism whenever the server
advertises it.

Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
---
 builtin/fetch-pack.c  |   5 +-
 fetch-pack.c          | 173 ++++++++++++++++++++++++++++++++++++--------------
 fetch-pack.h          |   2 +
 t/t5500-fetch-pack.sh |  42 ++++++++++++
 transport.c           |   2 +-
 5 files changed, 175 insertions(+), 49 deletions(-)

diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index 5f14242ae..ae073ab24 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -179,8 +179,9 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
 	get_remote_heads(fd[0], NULL, 0, &ref, 0, NULL, &shallow);
 	get_ref_array(&sought_refs, &nr_sought_refs, ref, sought, nr_sought);
 
-	ref = fetch_pack(&args, fd, conn, ref, dest, sought_refs, nr_sought_refs,
-			 &shallow, pack_lockfile_ptr);
+	ref = fetch_pack(&args, fd, conn, ref, dest, sought, nr_sought,
+			 sought_refs, nr_sought_refs, &shallow,
+			 pack_lockfile_ptr);
 	if (pack_lockfile) {
 		printf("lock %s\n", pack_lockfile);
 		fflush(stdout);
diff --git a/fetch-pack.c b/fetch-pack.c
index 8cc85c19f..02149c930 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -219,11 +219,19 @@ static void consume_shallow_list(struct fetch_pack_args *args, int fd)
 	}
 }
 
-static enum ack_type get_ack(int fd, unsigned char *result_sha1)
+/*
+ * Reads an ACK or NAK from fd. If wanted_ref_tail is not NULL, also accepts
+ * any "wanted-ref" lines before that ACK or NAK, writing them to
+ * wanted_ref_tail.
+ */
+static enum ack_type get_ack(int fd, unsigned char *result_sha1,
+			     struct ref ***wanted_ref_tail)
 {
 	int len;
-	char *line = packet_read_line(fd, &len);
+	char *line;
 	const char *arg;
+start:
+	line = packet_read_line(fd, &len);
 
 	if (!len)
 		die(_("git fetch-pack: expected ACK/NAK, got EOF"));
@@ -244,7 +252,19 @@ static enum ack_type get_ack(int fd, unsigned char *result_sha1)
 			return ACK;
 		}
 	}
-	die(_("git fetch-pack: expected ACK/NAK, got '%s'"), line);
+	if (wanted_ref_tail) {
+		struct object_id oid;
+		if (skip_prefix(line, "wanted-ref ", &arg) &&
+		    !get_sha1_hex(arg, oid.hash) && arg[40] == ' ' && arg[41]) {
+			struct ref *ref = alloc_ref(arg + 41);
+			oidcpy(&ref->old_oid, &oid);
+			**wanted_ref_tail = ref;
+			*wanted_ref_tail = &ref->next;
+			goto start;
+		}
+		die(_("git fetch_pack: expected ACK/NAK or wanted-ref, got '%s'"), line);
+	}
+	die(_("git fetch_pack: expected ACK/NAK, got '%s'"), line);
 }
 
 static void send_request(struct fetch_pack_args *args,
@@ -282,29 +302,55 @@ static int next_flush(struct fetch_pack_args *args, int count)
 	return count;
 }
 
-static int find_common(struct fetch_pack_args *args,
-		       int fd[2], unsigned char *result_sha1,
-		       struct ref *refs)
+static void write_capabilities(struct strbuf *sb,
+			       const struct fetch_pack_args *args)
 {
-	int fetching;
-	int count = 0, flushes = 0, flush_at = INITIAL_FLUSH, retval;
-	const unsigned char *sha1;
-	unsigned in_vain = 0;
-	int got_continue = 0;
-	int got_ready = 0;
-	struct strbuf req_buf = STRBUF_INIT;
-	size_t state_len = 0;
+	if (multi_ack == 2)     strbuf_addstr(sb, " multi_ack_detailed");
+	if (multi_ack == 1)     strbuf_addstr(sb, " multi_ack");
+	if (no_done)            strbuf_addstr(sb, " no-done");
+	if (use_sideband == 2)  strbuf_addstr(sb, " side-band-64k");
+	if (use_sideband == 1)  strbuf_addstr(sb, " side-band");
+	if (args->deepen_relative) strbuf_addstr(sb, " deepen-relative");
+	if (args->use_thin_pack) strbuf_addstr(sb, " thin-pack");
+	if (args->no_progress)   strbuf_addstr(sb, " no-progress");
+	if (args->include_tag)   strbuf_addstr(sb, " include-tag");
+	if (prefer_ofs_delta)   strbuf_addstr(sb, " ofs-delta");
+	if (deepen_since_ok)    strbuf_addstr(sb, " deepen-since");
+	if (deepen_not_ok)      strbuf_addstr(sb, " deepen-not");
+	if (agent_supported)    strbuf_addf(sb, " agent=%s",
+					    git_user_agent_sanitized());
+}
 
-	if (args->stateless_rpc && multi_ack == 1)
-		die(_("--stateless-rpc requires multi_ack_detailed"));
-	if (marked)
-		for_each_ref(clear_marks, NULL);
-	marked = 1;
+static void write_wants(struct strbuf *sb, const struct fetch_pack_args *args,
+			const struct refspec *refspecs, int nr_refspec,
+			struct ref *refs)
+{
+	int capabilities_written = 0;
 
-	for_each_ref(rev_list_insert_ref_oid, NULL);
-	for_each_alternate_ref(insert_one_alternate_ref, NULL);
+	if (refspecs) {
+		int i;
+		for (i = 0; i < nr_refspec; i++) {
+			const char *to_send = (refspecs[i].src && refspecs[i].src[0])
+				? refspecs[i].src : "HEAD";
+			if (i == 0) {
+				struct strbuf c = STRBUF_INIT;
+				write_capabilities(&c, args);
+				packet_buf_write(sb, "want-ref %s%s\n",
+						 to_send, c.buf);
+				strbuf_release(&c);
+			} else
+				packet_buf_write(sb, "want-ref %s\n", to_send);
+
+			/* write everything that refname_match supports */
+			packet_buf_write(sb, "want-ref refs/%s\n", to_send);
+			packet_buf_write(sb, "want-ref refs/tags/%s\n", to_send);
+			packet_buf_write(sb, "want-ref refs/heads/%s\n", to_send);
+			packet_buf_write(sb, "want-ref refs/remotes/%s\n", to_send);
+			packet_buf_write(sb, "want-ref refs/remotes/%s/HEAD\n", to_send);
+		}
+		return;
+	}
 
-	fetching = 0;
 	for ( ; refs ; refs = refs->next) {
 		unsigned char *remote = refs->old_oid.hash;
 		const char *remote_hex;
@@ -326,30 +372,41 @@ static int find_common(struct fetch_pack_args *args,
 		}
 
 		remote_hex = sha1_to_hex(remote);
-		if (!fetching) {
+		if (!capabilities_written) {
 			struct strbuf c = STRBUF_INIT;
-			if (multi_ack == 2)     strbuf_addstr(&c, " multi_ack_detailed");
-			if (multi_ack == 1)     strbuf_addstr(&c, " multi_ack");
-			if (no_done)            strbuf_addstr(&c, " no-done");
-			if (use_sideband == 2)  strbuf_addstr(&c, " side-band-64k");
-			if (use_sideband == 1)  strbuf_addstr(&c, " side-band");
-			if (args->deepen_relative) strbuf_addstr(&c, " deepen-relative");
-			if (args->use_thin_pack) strbuf_addstr(&c, " thin-pack");
-			if (args->no_progress)   strbuf_addstr(&c, " no-progress");
-			if (args->include_tag)   strbuf_addstr(&c, " include-tag");
-			if (prefer_ofs_delta)   strbuf_addstr(&c, " ofs-delta");
-			if (deepen_since_ok)    strbuf_addstr(&c, " deepen-since");
-			if (deepen_not_ok)      strbuf_addstr(&c, " deepen-not");
-			if (agent_supported)    strbuf_addf(&c, " agent=%s",
-							    git_user_agent_sanitized());
-			packet_buf_write(&req_buf, "want %s%s\n", remote_hex, c.buf);
+			write_capabilities(&c, args);
+			packet_buf_write(sb, "want %s%s\n", remote_hex, c.buf);
 			strbuf_release(&c);
+			capabilities_written = 1;
 		} else
-			packet_buf_write(&req_buf, "want %s\n", remote_hex);
-		fetching++;
+			packet_buf_write(sb, "want %s\n", remote_hex);
 	}
+}
+
+static int find_common(struct fetch_pack_args *args,
+		       int fd[2], unsigned char *result_sha1,
+		       struct strbuf *wants, struct ref **wanted_refs)
+{
+	int count = 0, flushes = 0, flush_at = INITIAL_FLUSH, retval;
+	const unsigned char *sha1;
+	unsigned in_vain = 0;
+	int got_continue = 0;
+	int got_ready = 0;
+	struct strbuf req_buf = STRBUF_INIT;
+	size_t state_len = 0;
+
+	if (args->stateless_rpc && multi_ack == 1)
+		die(_("--stateless-rpc requires multi_ack_detailed"));
+	if (marked)
+		for_each_ref(clear_marks, NULL);
+	marked = 1;
 
-	if (!fetching) {
+	for_each_ref(rev_list_insert_ref_oid, NULL);
+	for_each_alternate_ref(insert_one_alternate_ref, NULL);
+
+	strbuf_swap(&req_buf, wants);
+
+	if (!req_buf.len) {
 		strbuf_release(&req_buf);
 		packet_flush(fd[1]);
 		return 1;
@@ -435,7 +492,7 @@ static int find_common(struct fetch_pack_args *args,
 
 			consume_shallow_list(args, fd[0]);
 			do {
-				ack = get_ack(fd[0], result_sha1);
+				ack = get_ack(fd[0], result_sha1, NULL);
 				if (ack)
 					print_verbose(args, _("got %s %d %s"), "ack",
 						      ack, sha1_to_hex(result_sha1));
@@ -504,7 +561,9 @@ static int find_common(struct fetch_pack_args *args,
 	if (!got_ready || !no_done)
 		consume_shallow_list(args, fd[0]);
 	while (flushes || multi_ack) {
-		int ack = get_ack(fd[0], result_sha1);
+		struct ref *wr = NULL, **wr_tail = &wr;
+		int ack = get_ack(fd[0], result_sha1, &wr_tail);
+		*wanted_refs = wr;
 		if (ack) {
 			print_verbose(args, _("got %s (%d) %s"), "ack",
 				      ack, sha1_to_hex(result_sha1));
@@ -835,6 +894,7 @@ static int cmp_ref_by_name(const void *a_, const void *b_)
 static struct ref *do_fetch_pack(struct fetch_pack_args *args,
 				 int fd[2],
 				 const struct ref *orig_ref,
+				 const struct refspec *refspecs, int nr_refspec,
 				 const struct ref **sought, int nr_sought,
 				 struct shallow_info *si,
 				 char **pack_lockfile)
@@ -843,6 +903,10 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
 	unsigned char sha1[20];
 	const char *agent_feature;
 	int agent_len;
+	int ref_in_want = 0;
+	struct strbuf wants = STRBUF_INIT;
+	struct ref *wanted_refs = NULL;
+	int want_ref_used = 0;
 
 	sort_ref_list(&ref, ref_compare_name);
 	QSORT(sought, nr_sought, cmp_ref_by_name);
@@ -907,17 +971,26 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
 		die(_("Server does not support --shallow-exclude"));
 	if (!server_supports("deepen-relative") && args->deepen_relative)
 		die(_("Server does not support --deepen"));
+	if (server_supports("ref-in-want"))
+		ref_in_want = 1;
 
 	if (everything_local(args, &ref, sought, nr_sought)) {
 		packet_flush(fd[1]);
 		goto all_done;
 	}
-	if (find_common(args, fd, sha1, ref) < 0)
+
+	if (ref_in_want && refspecs) {
+		write_wants(&wants, args, refspecs, nr_refspec, NULL);
+		want_ref_used = 1;
+	} else
+		write_wants(&wants, args, NULL, 0, ref);
+	if (find_common(args, fd, sha1, &wants, &wanted_refs) < 0)
 		if (!args->keep_pack)
 			/* When cloning, it is not unusual to have
 			 * no common commit.
 			 */
 			warning(_("no common commits"));
+	strbuf_release(&wants);
 
 	if (args->stateless_rpc)
 		packet_flush(fd[1]);
@@ -932,6 +1005,13 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
 		die(_("git fetch-pack: fetch failed."));
 
  all_done:
+	if (want_ref_used) {
+		free_refs(ref);
+		return wanted_refs;
+	}
+
+	if (wanted_refs)
+		die("Protocol error: we are not using ref-in-want but server still sends wanted-ref");
 	return ref;
 }
 
@@ -1082,6 +1162,7 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
 		       int fd[], struct child_process *conn,
 		       const struct ref *ref,
 		       const char *dest,
+		       const struct refspec *refspecs, int nr_refspec,
 		       const struct ref **sought, int nr_sought,
 		       struct sha1_array *shallow,
 		       char **pack_lockfile)
@@ -1098,8 +1179,8 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
 		die(_("no matching remote head"));
 	}
 	prepare_shallow_info(&si, shallow);
-	ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought,
-				&si, pack_lockfile);
+	ref_cpy = do_fetch_pack(args, fd, ref, refspecs, nr_refspec,
+				sought, nr_sought, &si, pack_lockfile);
 	reprepare_packed_git();
 	update_shallow(args, ref_cpy, &si);
 	clear_shallow_info(&si);
diff --git a/fetch-pack.h b/fetch-pack.h
index 6e4fdbb68..06eb0fb28 100644
--- a/fetch-pack.h
+++ b/fetch-pack.h
@@ -5,6 +5,7 @@
 #include "run-command.h"
 
 struct sha1_array;
+struct refspec;
 
 struct fetch_pack_args {
 	const char *uploadpack;
@@ -38,6 +39,7 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
 		       int fd[], struct child_process *conn,
 		       const struct ref *ref,
 		       const char *dest,
+		       const struct refspec *refspecs, int nr_refspec,
 		       const struct ref **sought,
 		       int nr_sought,
 		       struct sha1_array *shallow,
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index cb1b7d949..18fe23c97 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -563,6 +563,25 @@ test_expect_success 'fetch-pack can fetch refs using a partial name' '
 	grep "$(printf "%s refs/heads/one" $(git -C server rev-parse --verify one))" actual
 '
 
+test_expect_success 'fetch-pack can fetch refs using a partial name using want-ref' '
+	rm -rf server &&
+	git init server &&
+	(
+		cd server &&
+		git config uploadpack.advertiseRefInWant true
+		test_commit 1 &&
+		test_commit 2 &&
+		git checkout -b one
+	) &&
+	rm -f trace &&
+	GIT_TRACE_PACKET="$(pwd)/trace" git fetch-pack server one >actual &&
+	echo here &&
+	grep " want-ref " trace &&
+	! grep " want " trace &&
+
+	grep "$(printf "%s refs/heads/one" $(git -C server rev-parse --verify one))" actual
+'
+
 test_expect_success 'fetch-pack can fetch refs using a glob' '
 	rm -rf server &&
 	git init server &&
@@ -585,6 +604,29 @@ test_expect_success 'fetch-pack can fetch refs using a glob' '
 	grep "$(printf "%s refs/heads/onc" $(git -C server rev-parse --verify onc))" actual
 '
 
+test_expect_success 'fetch-pack can fetch refs using a glob using want-ref' '
+	rm -rf server &&
+	git init server &&
+	(
+		cd server &&
+		git config uploadpack.advertiseRefInWant true
+		test_commit 1 &&
+		test_commit 2 &&
+		git checkout -b ona &&
+		git checkout -b onb &&
+		test_commit 3 &&
+		git checkout -b onc
+	) &&
+	rm -f trace &&
+	GIT_TRACE_PACKET="$(pwd)/trace" git fetch-pack server "refs/heads/on*" >actual &&
+	grep " want-ref " trace &&
+	! grep " want " trace &&
+
+	grep "$(printf "%s refs/heads/ona" $(git -C server rev-parse --verify ona))" actual &&
+	grep "$(printf "%s refs/heads/onb" $(git -C server rev-parse --verify onb))" actual &&
+	grep "$(printf "%s refs/heads/onc" $(git -C server rev-parse --verify onc))" actual
+'
+
 check_prot_path () {
 	cat >expected <<-EOF &&
 	Diag: url=$1
diff --git a/transport.c b/transport.c
index 5ed3fc68e..85a4c5369 100644
--- a/transport.c
+++ b/transport.c
@@ -239,7 +239,7 @@ static int fetch_refs_via_pack(struct transport *transport,
 
 	refs = fetch_pack(&args, data->fd, data->conn,
 			  refs_tmp ? refs_tmp : transport->remote_refs,
-			  dest, to_fetch, nr_heads, &data->shallow,
+			  dest, NULL, 0, to_fetch, nr_heads, &data->shallow,
 			  &transport->pack_lockfile);
 	close(data->fd[0]);
 	close(data->fd[1]);
-- 
2.11.0.483.g087da7b7c-goog


  parent reply	other threads:[~2017-01-25 22:03 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-01-25 22:02 [RFC 00/14] Allow fetch-pack to send ref names (globs allowed) Jonathan Tan
2017-01-25 22:02 ` [RFC 01/14] upload-pack: move parsing of "want" line Jonathan Tan
2017-01-25 22:02 ` [RFC 02/14] upload-pack: allow ref name and glob requests Jonathan Tan
2017-01-26 22:23   ` Junio C Hamano
2017-01-27  0:35     ` Jonathan Tan
2017-01-27  1:54       ` Junio C Hamano
2017-01-25 22:02 ` [RFC 03/14] upload-pack: test negotiation with changing repo Jonathan Tan
2017-01-26 22:33   ` Junio C Hamano
2017-01-27  0:44     ` Jonathan Tan
2017-02-22 23:36       ` Junio C Hamano
2017-02-23 18:43         ` [PATCH] upload-pack: report "not our ref" to client Jonathan Tan
2017-02-23 20:14           ` Junio C Hamano
2017-01-25 22:02 ` [RFC 04/14] fetch: refactor the population of hashes Jonathan Tan
2017-01-25 22:02 ` [RFC 05/14] fetch: refactor fetch_refs into two functions Jonathan Tan
2017-01-25 22:02 ` [RFC 06/14] fetch: refactor to make function args narrower Jonathan Tan
2017-01-25 22:03 ` [RFC 07/14] fetch-pack: put shallow info in out param Jonathan Tan
2017-01-25 22:03 ` [RFC 08/14] fetch-pack: check returned refs for matches Jonathan Tan
2017-01-25 22:03 ` [RFC 09/14] transport: put ref oid in out param Jonathan Tan
2017-01-25 22:03 ` [RFC 10/14] fetch-pack: support partial names and globs Jonathan Tan
2017-01-25 22:03 ` Jonathan Tan [this message]
2017-01-25 22:03 ` [RFC 12/14] fetch-pack: do not printf after closing stdout Jonathan Tan
2017-01-26  0:50   ` Stefan Beller
2017-01-26 18:18     ` Jonathan Tan
2017-01-25 22:03 ` [RFC 13/14] fetch: send want-ref and receive fetched refs Jonathan Tan
2017-01-25 22:03 ` [RFC 14/14] DONT USE advertise_ref_in_want=1 Jonathan Tan
2017-01-26 22:15 ` [RFC 00/14] Allow fetch-pack to send ref names (globs allowed) Stefan Beller
2017-01-26 23:00 ` Jeff King
2017-01-27  0:26   ` Jonathan Tan
2017-02-07 23:53 ` Jonathan Tan
2017-02-09  0:26   ` Junio C Hamano

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=7b01ee955db8b2d9c6c7e641f7f09436938b2af4.1485381677.git.jonathantanmy@google.com \
    --to=jonathantanmy@google.com \
    --cc=git@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.