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 02/14] upload-pack: allow ref name and glob requests
Date: Wed, 25 Jan 2017 14:02:55 -0800	[thread overview]
Message-ID: <d0d42b3bb4cf755f122591e191354c53848f197d.1485381677.git.jonathantanmy@google.com> (raw)
In-Reply-To: <cover.1485381677.git.jonathantanmy@google.com>
In-Reply-To: <cover.1485381677.git.jonathantanmy@google.com>

Currently, while performing packfile negotiation [1], upload-pack allows
clients to specify their desired objects only as SHA-1s. This causes:
(a) vulnerability to failure when an object turns non-existent during
    negotiation, which may happen if, for example, upload-pack is
    provided by multiple Git servers in a load-balancing arrangement,
    and
(b) dependence on the server first publishing a list of refs with
    associated objects.

To eliminate (a) and take a step towards eliminating (b), teach
upload-pack to support requests in the form of ref names and globs (in
addition to the existing support for SHA-1s) through a new line of the
form "want-ref <ref>" where ref is the full name of a ref, a glob
pattern, or a SHA-1. At the conclusion of negotiation, the server will
write "wanted-ref <SHA-1> <name>" for all requests that have been
specified this way.

The server indicates that it supports this feature by advertising the
capability "ref-in-want". Advertisement of this capability is by default
disabled, but can be enabled through a configuration option.

To be flexible with respect to client needs, the server does not
indicate an error if a "want-ref" line corresponds to no refs, but
instead relies on the client to ensure that what the user needs has been
fetched. For example, a client could reasonably expand an abbreviated
name "foo" to "want-ref foo", "want-ref refs/heads/foo", "want-ref
refs/tags/foo", among others, and ensure that at least one such ref has
been fetched.

[1] pack-protocol.txt

Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
---
 Documentation/technical/http-protocol.txt         |  20 +-
 Documentation/technical/pack-protocol.txt         |  24 +-
 Documentation/technical/protocol-capabilities.txt |   6 +
 t/t5552-upload-pack-ref-in-want.sh                | 295 ++++++++++++++++++++++
 upload-pack.c                                     |  89 ++++++-
 5 files changed, 411 insertions(+), 23 deletions(-)
 create mode 100755 t/t5552-upload-pack-ref-in-want.sh

diff --git a/Documentation/technical/http-protocol.txt b/Documentation/technical/http-protocol.txt
index 1c561bdd9..162d6bc07 100644
--- a/Documentation/technical/http-protocol.txt
+++ b/Documentation/technical/http-protocol.txt
@@ -316,7 +316,8 @@ to prevent caching of the response.
 
 Servers SHOULD support all capabilities defined here.
 
-Clients MUST send at least one "want" command in the request body.
+Clients MUST send at least one "want" or "want-ref" command in the
+request body.
 Clients MUST NOT reference an id in a "want" command which did not
 appear in the response obtained through ref discovery unless the
 server advertises capability `allow-tip-sha1-in-want` or
@@ -330,7 +331,7 @@ server advertises capability `allow-tip-sha1-in-want` or
   want_list         =  PKT-LINE(want NUL cap_list LF)
 		       *(want_pkt)
   want_pkt          =  PKT-LINE(want LF)
-  want              =  "want" SP id
+  want              =  "want" SP id / "want-ref" SP name
   cap_list          =  *(SP capability) SP
 
   have_list         =  *PKT-LINE("have" SP id LF)
@@ -352,7 +353,8 @@ C: Build an empty set, `common`, to hold the objects that are later
    determined to be on both ends.
 
 C: Build a set, `want`, of the objects from `advertised` the client
-   wants to fetch, based on what it saw during ref discovery.
+   wants to fetch, based on what it saw during ref discovery. This is to
+   be used if the server does not support the ref-in-want capability.
 
 C: Start a queue, `c_pending`, ordered by commit time (popping newest
    first).  Add all client refs.  When a commit is popped from
@@ -363,8 +365,8 @@ C: Start a queue, `c_pending`, ordered by commit time (popping newest
 
 C: Send one `$GIT_URL/git-upload-pack` request:
 
-   C: 0032want <want #1>...............................
-   C: 0032want <want #2>...............................
+   C: <size>want-ref <want #1>...............................
+   C: <size>want-ref <want #2>...............................
    ....
    C: 0032have <common #1>.............................
    C: 0032have <common #2>.............................
@@ -384,7 +386,7 @@ the pkt-line value.
 Commands MUST appear in the following order, if they appear
 at all in the request stream:
 
-* "want"
+* "want" or "want-ref"
 * "have"
 
 The stream is terminated by a pkt-line flush (`0000`).
@@ -393,6 +395,9 @@ A single "want" or "have" command MUST have one hex formatted
 SHA-1 as its value.  Multiple SHA-1s MUST be sent by sending
 multiple commands.
 
+A "want-ref" command MUST be followed by a ref name (which may include
+shell glob characters) or a hex formatted SHA-1.
+
 The `have` list is created by popping the first 32 commits
 from `c_pending`.  Less can be supplied if `c_pending` empties.
 
@@ -410,6 +415,9 @@ Verify all objects in `want` are directly reachable from refs.
 The server MAY walk backwards through history or through
 the reflog to permit slightly stale requests.
 
+Treat "want-ref" requests as if the equivalent 0 or more "want" commands
+(according to the server's refs) were given instead.
+
 If no "want" objects are received, send an error:
 TODO: Define error if no "want" lines are requested.
 
diff --git a/Documentation/technical/pack-protocol.txt b/Documentation/technical/pack-protocol.txt
index c59ac9936..b8b2ffdf9 100644
--- a/Documentation/technical/pack-protocol.txt
+++ b/Documentation/technical/pack-protocol.txt
@@ -223,15 +223,20 @@ out of what the server said it could do with the first 'want' line.
 		       PKT-LINE("deepen-since" SP timestamp) /
 		       PKT-LINE("deepen-not" SP ref)
 
-  first-want        =  PKT-LINE("want" SP obj-id SP capability-list)
-  additional-want   =  PKT-LINE("want" SP obj-id)
+  first-want        =  PKT-LINE(want SP capability-list)
+  additional-want   =  PKT-LINE(want)
+
+  want              = "want" SP obj-id / "want-ref" SP name
 
   depth             =  1*DIGIT
 ----
 
-Clients MUST send all the obj-ids it wants from the reference
-discovery phase as 'want' lines. Clients MUST send at least one
-'want' command in the request body. Clients MUST NOT mention an
+Clients may specify the objects it wants by selecting obj-ids from the
+refs that have been advertised (using "want") or by specifying ref names
+and ref patterns directly (using "want-ref"). If the latter, the server
+will return the list of effective refs used using "wanted-ref" lines.
+Either way, clients MUST send at least one
+'want' or 'want-ref' command in the request body. Clients MUST NOT mention an
 obj-id in a 'want' command which did not appear in the response
 obtained through ref discovery.
 
@@ -249,7 +254,7 @@ complete those commits. Commits whose parents are not received as a
 result are defined as shallow and marked as such in the server. This
 information is sent back to the client in the next step.
 
-Once all the 'want's and 'shallow's (and optional 'deepen') are
+Once all the wants and 'shallow's (and optional 'deepen') are
 transferred, clients MUST send a flush-pkt, to tell the server side
 that it is done sending the list.
 
@@ -344,7 +349,9 @@ implementation if we have received at least one "ACK %s continue"
 during a prior round.  This helps to ensure that at least one common
 ancestor is found before we give up entirely.
 
-Once the 'done' line is read from the client, the server will either
+Once the 'done' line is read from the client, the server will send all the
+refs corresponding to "want-ref" lines from the client (prefixed by
+"wanted-ref"), and then either
 send a final 'ACK obj-id' or it will send a 'NAK'. 'obj-id' is the object
 name of the last commit determined to be common. The server only sends
 ACK after 'done' if there is at least one common base and multi_ack or
@@ -354,9 +361,10 @@ if there is no common base found.
 Then the server will start sending its packfile data.
 
 ----
-  server-response = *ack_multi ack / nak
+  server-response = *ack_multi *wanted_ref ack / nak
   ack_multi       = PKT-LINE("ACK" SP obj-id ack_status)
   ack_status      = "continue" / "common" / "ready"
+  wanted_ref      = PKT-LINE("wanted-ref" SP obj-id SP name)
   ack             = PKT-LINE("ACK" SP obj-id)
   nak             = PKT-LINE("NAK")
 ----
diff --git a/Documentation/technical/protocol-capabilities.txt b/Documentation/technical/protocol-capabilities.txt
index 26dcc6f50..72a2ff907 100644
--- a/Documentation/technical/protocol-capabilities.txt
+++ b/Documentation/technical/protocol-capabilities.txt
@@ -309,3 +309,9 @@ to accept a signed push certificate, and asks the <nonce> to be
 included in the push certificate.  A send-pack client MUST NOT
 send a push-cert packet unless the receive-pack server advertises
 this capability.
+
+ref-in-want
+-----------
+
+If the upload-pack server advertises this capability, fetch-pack may
+send "want-ref" lines.
diff --git a/t/t5552-upload-pack-ref-in-want.sh b/t/t5552-upload-pack-ref-in-want.sh
new file mode 100755
index 000000000..3496af641
--- /dev/null
+++ b/t/t5552-upload-pack-ref-in-want.sh
@@ -0,0 +1,295 @@
+#!/bin/sh
+
+test_description='upload-pack ref-in-want'
+
+. ./test-lib.sh
+
+line() {
+	len=$(printf '%s' "$1" | wc -c) &&
+	len=$((len + 5)) &&
+	printf '%04x%s\n' $len "$1" >>input
+}
+
+flush() {
+	printf '0000'
+}
+
+get_actual_refs() {
+	grep -a wanted-ref output | sed "s/^.*wanted-ref //" >actual_refs
+}
+
+get_actual_commits() {
+	perl -0777 -p -e "s/^.*PACK/PACK/s" <output >o.pack &&
+	git index-pack o.pack &&
+	git verify-pack -v o.idx | grep commit | cut -c-40 | sort >actual_commits
+}
+
+check_output() {
+	get_actual_refs &&
+	test_cmp expected_refs actual_refs &&
+	get_actual_commits &&
+	test_cmp expected_commits actual_commits
+}
+
+# c(o/foo) d(o/bar)
+#        \ /
+#         b   e(baz)  f(master)
+#          \__  |  __/
+#             \ | /
+#               a
+test_expect_success 'setup repository' '
+	test_commit a &&
+	git checkout -b o/foo &&
+	test_commit b &&
+	test_commit c &&
+	git checkout -b o/bar b &&
+	test_commit d &&
+	git checkout -b baz a &&
+	test_commit e &&
+	git checkout master &&
+	test_commit f
+'
+
+test_expect_success 'config controls ref-in-want advertisement' '
+	test_config uploadpack.advertiseRefInWant false &&
+	printf "0000" | git upload-pack . >output &&
+	! grep -a ref-in-want output &&
+	test_config uploadpack.advertiseRefInWant true &&
+	printf "0000" | git upload-pack . >output &&
+	grep -a ref-in-want output
+'
+
+test_expect_success 'mix want and want-ref' '
+	cat >expected_refs <<-EOF &&
+	$(git rev-parse f) refs/heads/master
+	EOF
+	git rev-parse e f | sort >expected_commits &&
+
+	line "want-ref refs/heads/master" >input &&
+	line "want $(git rev-parse e)" >>input &&
+	flush >>input &&
+	line "have $(git rev-parse a)" >>input &&
+	flush >>input &&
+	line "done" >>input &&
+	git upload-pack . <input >output &&
+	check_output
+'
+
+test_expect_success 'want-ref with glob and non-glob' '
+	cat >expected_refs <<-EOF &&
+	$(git rev-parse f) refs/heads/master
+	$(git rev-parse d) refs/heads/o/bar
+	$(git rev-parse c) refs/heads/o/foo
+	EOF
+	git rev-parse b c d f | sort >expected_commits &&
+
+	line "want-ref refs/head*/[op]/*" >input &&
+	line "want-ref refs/heads/master" >>input &&
+	line "want-ref refs/heads/non-existent/*" >>input &&
+	line "want-ref refs/heads/also-non-existent" >>input &&
+	flush >>input &&
+	line "have $(git rev-parse a)" >>input &&
+	flush >>input &&
+	line "done" >>input &&
+	git upload-pack . <input >output &&
+	check_output
+'
+
+test_expect_success 'want-ref with SHA-1' '
+	cat >expected_refs <<-EOF &&
+	$(git rev-parse f) $(git rev-parse f)
+	EOF
+	git rev-parse f | sort >expected_commits &&
+
+	line "want-ref $(git rev-parse f)" >input &&
+	flush >>input &&
+	line "have $(git rev-parse a)" >>input &&
+	flush >>input &&
+	line "done" >>input &&
+	git upload-pack . <input >output &&
+	check_output
+'
+
+test_expect_success 'want-ref with overlapping glob and non-glob' '
+	cat >expected_refs <<-EOF &&
+	$(git rev-parse d) refs/heads/o/bar
+	$(git rev-parse c) refs/heads/o/foo
+	EOF
+	git rev-parse b c d | sort >expected_commits &&
+
+	line "want-ref refs/heads/o/*" >input &&
+	line "want-ref refs/heads/o/foo" >>input &&
+	flush >>input &&
+	line "have $(git rev-parse a)" >>input &&
+	flush >>input &&
+	line "done" >>input &&
+	git upload-pack . <input >output &&
+	check_output
+'
+
+test_expect_success 'want-ref with overlapping non-glob and SHA-1' '
+	cat >expected_refs <<-EOF &&
+	$(git rev-parse f) $(git rev-parse f)
+	$(git rev-parse f) refs/heads/master
+	EOF
+	git rev-parse f | sort >expected_commits &&
+
+	line "want-ref $(git rev-parse f)" >input &&
+	line "want-ref refs/heads/master" >>input &&
+	flush >>input &&
+	line "have $(git rev-parse a)" >>input &&
+	flush >>input &&
+	line "done" >>input &&
+	git upload-pack . <input >output &&
+	check_output
+'
+
+test_expect_success 'want-ref with overlapping glob and SHA-1' '
+	cat >expected_refs <<-EOF &&
+	$(git rev-parse f) $(git rev-parse f)
+	$(git rev-parse f) refs/heads/master
+	EOF
+	git rev-parse f | sort >expected_commits &&
+
+	line "want-ref $(git rev-parse f)" >input &&
+	line "want-ref refs/heads/mas*" >>input &&
+	flush >>input &&
+	line "have $(git rev-parse a)" >>input &&
+	flush >>input &&
+	line "done" >>input &&
+	git upload-pack . <input >output &&
+	check_output
+'
+
+test_expect_success 'want-ref with overlapping globs' '
+	cat >expected_refs <<-EOF &&
+	$(git rev-parse d) refs/heads/o/bar
+	$(git rev-parse c) refs/heads/o/foo
+	EOF
+	git rev-parse b c d | sort >expected_commits &&
+
+	line "want-ref refs/heads/o/*" >input &&
+	line "want-ref refs/heads/o/f*" >>input &&
+	flush >>input &&
+	line "have $(git rev-parse a)" >>input &&
+	flush >>input &&
+	line "done" >>input &&
+	git upload-pack . <input >output &&
+	check_output
+'
+
+test_expect_success 'want-ref with ref we already have commit for' '
+	cat >expected_refs <<-EOF &&
+	$(git rev-parse c) refs/heads/o/foo
+	EOF
+	>expected_commits &&
+
+	line "want-ref refs/heads/o/foo" >input &&
+	flush >>input &&
+	line "have $(git rev-parse c)" >>input &&
+	flush >>input &&
+	line "done" >>input &&
+	git upload-pack . <input >output &&
+	check_output
+'
+
+test_expect_success 'send wanted-ref only at the end of negotiation' '
+	# Incomplete input; acknowledge "have" with NAK, but no wanted-ref
+	line "want-ref refs/heads/o/foo" >input &&
+	flush >>input &&
+	line "have 1234567890123456789012345678901234567890" >>input &&
+	flush >>input &&
+	test_must_fail git upload-pack . <input >output &&
+	grep -a "0008NAK" output &&
+	test_must_fail grep -a "wanted-ref" output &&
+
+	# Complete the input, and try again
+	line "have $(git rev-parse c)" >>input &&
+	flush >>input &&
+	line "done" >>input &&
+	flush >>input &&
+	git upload-pack . <input >output &&
+	grep -a "wanted-ref" output
+'
+
+test_expect_success 'want-ref with capability declaration' '
+	cat >expected_refs <<-EOF &&
+	$(git rev-parse c) refs/heads/o/foo
+	EOF
+	git rev-parse b c | sort >expected_commits &&
+
+	line "want-ref refs/heads/o/foo no_progress" >input &&
+	flush >>input &&
+	line "have $(git rev-parse a)" >>input &&
+	flush >>input &&
+	line "done" >>input &&
+	git upload-pack . <input >output &&
+	check_output
+'
+
+test_expect_success 'hideRefs' '
+	test_config transfer.hideRefs refs/heads/o/foo &&
+
+	cat >expected_refs <<-EOF &&
+	$(git rev-parse d) refs/heads/o/bar
+	EOF
+	git rev-parse b d | sort >expected_commits &&
+
+	line "want-ref refs/heads/o/*" >input &&
+	flush >>input &&
+	line "have $(git rev-parse a)" >>input &&
+	flush >>input &&
+	line "done" >>input &&
+	git upload-pack . <input >output &&
+	check_output
+'
+
+test_expect_success 'setup namespaced repo' '
+	git init n &&
+	(
+		cd n &&
+		test_commit a &&
+		test_commit b &&
+		git checkout a &&
+		test_commit c &&
+		git checkout a &&
+		test_commit d &&
+		git update-ref refs/heads/ns-no b
+		git update-ref refs/namespaces/ns/refs/heads/ns-yes c
+		git update-ref refs/namespaces/ns/refs/heads/another d
+	)
+'
+
+test_expect_success 'want-ref with namespaces' '
+	cat >expected_refs <<-EOF &&
+	$(git -C n rev-parse c) refs/heads/ns-yes
+	EOF
+	git -C n rev-parse c | sort >expected_commits &&
+
+	line "want-ref refs/heads/ns-*" >input &&
+	flush >>input &&
+	line "have $(git -C n rev-parse a)" >>input &&
+	flush >>input &&
+	line "done" >>input &&
+	git --namespace=ns -C n upload-pack . <input >output &&
+	check_output
+'
+
+test_expect_success 'hideRefs with namespaces' '
+	test_config transfer.hideRefs refs/heads/another &&
+
+	cat >expected_refs <<-EOF &&
+	$(git -C n rev-parse c) refs/heads/ns-yes
+	EOF
+	git -C n rev-parse c | sort >expected_commits &&
+
+	line "want-ref refs/heads/ns-*" >input &&
+	flush >>input &&
+	line "have $(git -C n rev-parse a)" >>input &&
+	flush >>input &&
+	line "done" >>input &&
+	git --namespace=ns -C n upload-pack . <input >output &&
+	check_output
+'
+
+test_done
diff --git a/upload-pack.c b/upload-pack.c
index 15c60a204..b88ed8e26 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -62,6 +62,7 @@ static int use_sideband;
 static int advertise_refs;
 static int stateless_rpc;
 static const char *pack_objects_hook;
+static int advertise_ref_in_want;
 
 static void reset_timeout(void)
 {
@@ -380,7 +381,25 @@ static int ok_to_give_up(void)
 	return 1;
 }
 
-static int get_common_commits(void)
+static void write_wanted_ns_refs(const struct string_list *wanted_ns_refs)
+{
+	const struct string_list_item *item;
+	for_each_string_list_item(item, wanted_ns_refs) {
+		unsigned char sha1[GIT_SHA1_RAWSZ];
+		if (!get_sha1_hex(item->string, sha1)) {
+			packet_write_fmt(1, "wanted-ref %s %s\n", item->string,
+					 item->string);
+		} else {
+			if (read_ref(item->string, sha1))
+				die("unable to read ref %s", item->string);
+			packet_write_fmt(1, "wanted-ref %s %s\n",
+					 sha1_to_hex(sha1),
+					 strip_namespace(item->string));
+		}
+	}
+}
+
+static int get_common_commits(const struct string_list *wanted_ns_refs)
 {
 	unsigned char sha1[20];
 	char last_hex[41];
@@ -442,6 +461,7 @@ static int get_common_commits(void)
 			continue;
 		}
 		if (!strcmp(line, "done")) {
+			write_wanted_ns_refs(wanted_ns_refs);
 			if (have_obj.nr > 0) {
 				if (multi_ack)
 					packet_write_fmt(1, "ACK %s\n", last_hex);
@@ -729,7 +749,26 @@ static void deepen_by_rev_list(int ac, const char **av,
 	packet_flush(1);
 }
 
-static void receive_needs(void)
+static int mark_ref_wanted(const char *ns_ref,
+			   const struct object_id *oid, int flags,
+			   void *wanted_ns_refs_)
+{
+	struct string_list *wanted_ns_refs = wanted_ns_refs_;
+	struct object *o;
+
+	if (ref_is_hidden(strip_namespace(ns_ref), ns_ref))
+		return 0;
+
+	o = parse_object_or_die(oid->hash, ns_ref);
+	if (!(o->flags & WANTED)) {
+		o->flags |= WANTED;
+		add_object_array(o, NULL, &want_obj);
+	}
+	string_list_insert(wanted_ns_refs, ns_ref);
+	return 0;
+}
+
+static void receive_needs(struct string_list *wanted_ns_refs)
 {
 	struct object_array shallows = OBJECT_ARRAY_INIT;
 	struct string_list deepen_not = STRING_LIST_INIT_DUP;
@@ -793,8 +832,35 @@ static void receive_needs(void)
 			deepen_rev_list = 1;
 			continue;
 		}
-		if (skip_prefix(line, "want ", &arg) &&
-		    !get_sha1_hex(arg, sha1_buf)) {
+		if (skip_prefix(line, "want-ref ", &arg)) {
+			struct object_id oid;
+
+			char *space = strchrnul(arg, ' ');
+			char *ns_ref = xstrfmt("%s%.*s",
+					       get_git_namespace(),
+					       (int)(space - arg),
+					       arg);
+			if (has_glob_specials(ns_ref))
+				for_each_glob_ref(mark_ref_wanted, ns_ref,
+						  wanted_ns_refs);
+			else if (!get_oid_hex(arg, &oid)) {
+				o = parse_object(oid.hash);
+				if (o && !(o->flags & WANTED)) {
+					o->flags |= WANTED;
+					if (!((allow_unadvertised_object_request & ALLOW_ANY_SHA1) == ALLOW_ANY_SHA1
+					      || is_our_ref(o)))
+						has_non_tip = 1;
+					add_object_array(o, NULL, &want_obj);
+				}
+				mark_ref_wanted(ns_ref, &oid, 0,
+						wanted_ns_refs);
+			} else if (!read_ref(ns_ref, oid.hash))
+				mark_ref_wanted(ns_ref, &oid, 0,
+						wanted_ns_refs);
+			free(ns_ref);
+			features = space;
+		} else if (skip_prefix(line, "want ", &arg) &&
+			   !get_sha1_hex(arg, sha1_buf)) {
 			o = parse_object(sha1_buf);
 			if (!o)
 				die("git upload-pack: not our ref %s",
@@ -806,12 +872,11 @@ static void receive_needs(void)
 					has_non_tip = 1;
 				add_object_array(o, NULL, &want_obj);
 			}
+			features = arg + 40;
 		} else
 			die("git upload-pack: protocol error, "
 			    "expected to get sha, not '%s'", line);
 
-		features = arg + 40;
-
 		if (parse_feature_request(features, "deepen-relative"))
 			deepen_relative = 1;
 		if (parse_feature_request(features, "multi_ack_detailed"))
@@ -935,7 +1000,7 @@ static int send_ref(const char *refname, const struct object_id *oid,
 		struct strbuf symref_info = STRBUF_INIT;
 
 		format_symref_info(&symref_info, cb_data);
-		packet_write_fmt(1, "%s %s%c%s%s%s%s%s agent=%s\n",
+		packet_write_fmt(1, "%s %s%c%s%s%s%s%s%s agent=%s\n",
 			     oid_to_hex(oid), refname_nons,
 			     0, capabilities,
 			     (allow_unadvertised_object_request & ALLOW_TIP_SHA1) ?
@@ -943,6 +1008,8 @@ static int send_ref(const char *refname, const struct object_id *oid,
 			     (allow_unadvertised_object_request & ALLOW_REACHABLE_SHA1) ?
 				     " allow-reachable-sha1-in-want" : "",
 			     stateless_rpc ? " no-done" : "",
+			     advertise_ref_in_want ?
+				     " ref-in-want" : "",
 			     symref_info.buf,
 			     git_user_agent_sanitized());
 		strbuf_release(&symref_info);
@@ -975,6 +1042,7 @@ static int find_symref(const char *refname, const struct object_id *oid,
 static void upload_pack(void)
 {
 	struct string_list symref = STRING_LIST_INIT_DUP;
+	struct string_list wanted_ns_refs = STRING_LIST_INIT_DUP;
 
 	head_ref_namespaced(find_symref, &symref);
 
@@ -992,11 +1060,12 @@ static void upload_pack(void)
 	if (advertise_refs)
 		return;
 
-	receive_needs();
+	receive_needs(&wanted_ns_refs);
 	if (want_obj.nr) {
-		get_common_commits();
+		get_common_commits(&wanted_ns_refs);
 		create_pack_file();
 	}
+	string_list_clear(&wanted_ns_refs, 0);
 }
 
 static int upload_pack_config(const char *var, const char *value, void *unused)
@@ -1020,6 +1089,8 @@ static int upload_pack_config(const char *var, const char *value, void *unused)
 		keepalive = git_config_int(var, value);
 		if (!keepalive)
 			keepalive = -1;
+	} else if (!strcmp("uploadpack.advertiserefinwant", var)) {
+		advertise_ref_in_want = git_config_bool(var, value);
 	} else if (current_config_scope() != CONFIG_SCOPE_REPO) {
 		if (!strcmp("uploadpack.packobjectshook", var))
 			return git_config_string(&pack_objects_hook, var, value);
-- 
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 ` Jonathan Tan [this message]
2017-01-26 22:23   ` [RFC 02/14] upload-pack: allow ref name and glob requests 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 ` [RFC 11/14] fetch-pack: support want-ref Jonathan Tan
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=d0d42b3bb4cf755f122591e191354c53848f197d.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.