fetch-pack: perform a fetch using v2
When communicating with a v2 server, perform a fetch by requesting the 'fetch' command. Signed-off-by: Brandon Williams <bmwill@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
							parent
							
								
									3145ea957d
								
							
						
					
					
						commit
						685fbd3291
					
				|  | @ -255,12 +255,43 @@ A `fetch` request can take the following arguments: | |||
| 	to its base by position in pack rather than by an oid.  That is, | ||||
| 	they can read OBJ_OFS_DELTA (ake type 6) in a packfile. | ||||
|  | ||||
|     shallow <oid> | ||||
| 	A client must notify the server of all commits for which it only | ||||
| 	has shallow copies (meaning that it doesn't have the parents of | ||||
| 	a commit) by supplying a 'shallow <oid>' line for each such | ||||
| 	object so that the server is aware of the limitations of the | ||||
| 	client's history.  This is so that the server is aware that the | ||||
| 	client may not have all objects reachable from such commits. | ||||
|  | ||||
|     deepen <depth> | ||||
| 	Requests that the fetch/clone should be shallow having a commit | ||||
| 	depth of <depth> relative to the remote side. | ||||
|  | ||||
|     deepen-relative | ||||
| 	Requests that the semantics of the "deepen" command be changed | ||||
| 	to indicate that the depth requested is relative to the client's | ||||
| 	current shallow boundary, instead of relative to the requested | ||||
| 	commits. | ||||
|  | ||||
|     deepen-since <timestamp> | ||||
| 	Requests that the shallow clone/fetch should be cut at a | ||||
| 	specific time, instead of depth.  Internally it's equivalent to | ||||
| 	doing "git rev-list --max-age=<timestamp>". Cannot be used with | ||||
| 	"deepen". | ||||
|  | ||||
|     deepen-not <rev> | ||||
| 	Requests that the shallow clone/fetch should be cut at a | ||||
| 	specific revision specified by '<rev>', instead of a depth. | ||||
| 	Internally it's equivalent of doing "git rev-list --not <rev>". | ||||
| 	Cannot be used with "deepen", but can be used with | ||||
| 	"deepen-since". | ||||
|  | ||||
| The response of `fetch` is broken into a number of sections separated by | ||||
| delimiter packets (0001), with each section beginning with its section | ||||
| header. | ||||
|  | ||||
|     output = *section | ||||
|     section = (acknowledgments | packfile) | ||||
|     section = (acknowledgments | shallow-info | packfile) | ||||
| 	      (flush-pkt | delim-pkt) | ||||
|  | ||||
|     acknowledgments = PKT-LINE("acknowledgments" LF) | ||||
|  | @ -270,6 +301,11 @@ header. | |||
|     nak = PKT-LINE("NAK" LF) | ||||
|     ack = PKT-LINE("ACK" SP obj-id LF) | ||||
|  | ||||
|     shallow-info = PKT-LINE("shallow-info" LF) | ||||
| 		   *PKT-LINE((shallow | unshallow) LF) | ||||
|     shallow = "shallow" SP obj-id | ||||
|     unshallow = "unshallow" SP obj-id | ||||
|  | ||||
|     packfile = PKT-LINE("packfile" LF) | ||||
| 	       *PKT-LINE(%x01-03 *%x00-ff) | ||||
|  | ||||
|  | @ -301,6 +337,35 @@ header. | |||
| 	  determined the objects it plans to send to the client and no | ||||
| 	  further negotiation is needed. | ||||
|  | ||||
|     shallow-info section | ||||
| 	If the client has requested a shallow fetch/clone, a shallow | ||||
| 	client requests a fetch or the server is shallow then the | ||||
| 	server's response may include a shallow-info section.  The | ||||
| 	shallow-info section will be included if (due to one of the | ||||
| 	above conditions) the server needs to inform the client of any | ||||
| 	shallow boundaries or adjustments to the clients already | ||||
| 	existing shallow boundaries. | ||||
|  | ||||
| 	* Always begins with the section header "shallow-info" | ||||
|  | ||||
| 	* If a positive depth is requested, the server will compute the | ||||
| 	  set of commits which are no deeper than the desired depth. | ||||
|  | ||||
| 	* The server sends a "shallow obj-id" line for each commit whose | ||||
| 	  parents will not be sent in the following packfile. | ||||
|  | ||||
| 	* The server sends an "unshallow obj-id" line for each commit | ||||
| 	  which the client has indicated is shallow, but is no longer | ||||
| 	  shallow as a result of the fetch (due to its parents being | ||||
| 	  sent in the following packfile). | ||||
|  | ||||
| 	* The server MUST NOT send any "unshallow" lines for anything | ||||
| 	  which the client has not indicated was shallow as a part of | ||||
| 	  its request. | ||||
|  | ||||
| 	* This section is only included if a packfile section is also | ||||
| 	  included in the response. | ||||
|  | ||||
|     packfile section | ||||
| 	* This section is only included if the client has sent 'want' | ||||
| 	  lines in its request and either requested that no more | ||||
|  |  | |||
|  | @ -212,7 +212,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) | |||
| 	} | ||||
|  | ||||
| 	ref = fetch_pack(&args, fd, conn, ref, dest, sought, nr_sought, | ||||
| 			 &shallow, pack_lockfile_ptr); | ||||
| 			 &shallow, pack_lockfile_ptr, protocol_v0); | ||||
| 	if (pack_lockfile) { | ||||
| 		printf("lock %s\n", pack_lockfile); | ||||
| 		fflush(stdout); | ||||
|  |  | |||
							
								
								
									
										270
									
								
								fetch-pack.c
								
								
								
								
							
							
						
						
									
										270
									
								
								fetch-pack.c
								
								
								
								
							|  | @ -303,9 +303,9 @@ static void insert_one_alternate_object(struct object *obj) | |||
| #define PIPESAFE_FLUSH 32 | ||||
| #define LARGE_FLUSH 16384 | ||||
|  | ||||
| static int next_flush(struct fetch_pack_args *args, int count) | ||||
| static int next_flush(int stateless_rpc, int count) | ||||
| { | ||||
| 	if (args->stateless_rpc) { | ||||
| 	if (stateless_rpc) { | ||||
| 		if (count < LARGE_FLUSH) | ||||
| 			count <<= 1; | ||||
| 		else | ||||
|  | @ -461,7 +461,7 @@ static int find_common(struct fetch_pack_args *args, | |||
| 			send_request(args, fd[1], &req_buf); | ||||
| 			strbuf_setlen(&req_buf, state_len); | ||||
| 			flushes++; | ||||
| 			flush_at = next_flush(args, count); | ||||
| 			flush_at = next_flush(args->stateless_rpc, count); | ||||
|  | ||||
| 			/* | ||||
| 			 * We keep one window "ahead" of the other side, and | ||||
|  | @ -1008,6 +1008,259 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, | |||
| 	return ref; | ||||
| } | ||||
|  | ||||
| static void add_wants(const struct ref *wants, struct strbuf *req_buf) | ||||
| { | ||||
| 	for ( ; wants ; wants = wants->next) { | ||||
| 		const struct object_id *remote = &wants->old_oid; | ||||
| 		const char *remote_hex; | ||||
| 		struct object *o; | ||||
|  | ||||
| 		/* | ||||
| 		 * If that object is complete (i.e. it is an ancestor of a | ||||
| 		 * local ref), we tell them we have it but do not have to | ||||
| 		 * tell them about its ancestors, which they already know | ||||
| 		 * about. | ||||
| 		 * | ||||
| 		 * We use lookup_object here because we are only | ||||
| 		 * interested in the case we *know* the object is | ||||
| 		 * reachable and we have already scanned it. | ||||
| 		 */ | ||||
| 		if (((o = lookup_object(remote->hash)) != NULL) && | ||||
| 		    (o->flags & COMPLETE)) { | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		remote_hex = oid_to_hex(remote); | ||||
| 		packet_buf_write(req_buf, "want %s\n", remote_hex); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void add_common(struct strbuf *req_buf, struct oidset *common) | ||||
| { | ||||
| 	struct oidset_iter iter; | ||||
| 	const struct object_id *oid; | ||||
| 	oidset_iter_init(common, &iter); | ||||
|  | ||||
| 	while ((oid = oidset_iter_next(&iter))) { | ||||
| 		packet_buf_write(req_buf, "have %s\n", oid_to_hex(oid)); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static int add_haves(struct strbuf *req_buf, int *haves_to_send, int *in_vain) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 	int haves_added = 0; | ||||
| 	const struct object_id *oid; | ||||
|  | ||||
| 	while ((oid = get_rev())) { | ||||
| 		packet_buf_write(req_buf, "have %s\n", oid_to_hex(oid)); | ||||
| 		if (++haves_added >= *haves_to_send) | ||||
| 			break; | ||||
| 	} | ||||
|  | ||||
| 	*in_vain += haves_added; | ||||
| 	if (!haves_added || *in_vain >= MAX_IN_VAIN) { | ||||
| 		/* Send Done */ | ||||
| 		packet_buf_write(req_buf, "done\n"); | ||||
| 		ret = 1; | ||||
| 	} | ||||
|  | ||||
| 	/* Increase haves to send on next round */ | ||||
| 	*haves_to_send = next_flush(1, *haves_to_send); | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| static int send_fetch_request(int fd_out, const struct fetch_pack_args *args, | ||||
| 			      const struct ref *wants, struct oidset *common, | ||||
| 			      int *haves_to_send, int *in_vain) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 	struct strbuf req_buf = STRBUF_INIT; | ||||
|  | ||||
| 	if (server_supports_v2("fetch", 1)) | ||||
| 		packet_buf_write(&req_buf, "command=fetch"); | ||||
| 	if (server_supports_v2("agent", 0)) | ||||
| 		packet_buf_write(&req_buf, "agent=%s", git_user_agent_sanitized()); | ||||
|  | ||||
| 	packet_buf_delim(&req_buf); | ||||
| 	if (args->use_thin_pack) | ||||
| 		packet_buf_write(&req_buf, "thin-pack"); | ||||
| 	if (args->no_progress) | ||||
| 		packet_buf_write(&req_buf, "no-progress"); | ||||
| 	if (args->include_tag) | ||||
| 		packet_buf_write(&req_buf, "include-tag"); | ||||
| 	if (prefer_ofs_delta) | ||||
| 		packet_buf_write(&req_buf, "ofs-delta"); | ||||
|  | ||||
| 	/* add wants */ | ||||
| 	add_wants(wants, &req_buf); | ||||
|  | ||||
| 	/* Add all of the common commits we've found in previous rounds */ | ||||
| 	add_common(&req_buf, common); | ||||
|  | ||||
| 	/* Add initial haves */ | ||||
| 	ret = add_haves(&req_buf, haves_to_send, in_vain); | ||||
|  | ||||
| 	/* Send request */ | ||||
| 	packet_buf_flush(&req_buf); | ||||
| 	write_or_die(fd_out, req_buf.buf, req_buf.len); | ||||
|  | ||||
| 	strbuf_release(&req_buf); | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Processes a section header in a server's response and checks if it matches | ||||
|  * `section`.  If the value of `peek` is 1, the header line will be peeked (and | ||||
|  * not consumed); if 0, the line will be consumed and the function will die if | ||||
|  * the section header doesn't match what was expected. | ||||
|  */ | ||||
| static int process_section_header(struct packet_reader *reader, | ||||
| 				  const char *section, int peek) | ||||
| { | ||||
| 	int ret; | ||||
|  | ||||
| 	if (packet_reader_peek(reader) != PACKET_READ_NORMAL) | ||||
| 		die("error reading packet"); | ||||
|  | ||||
| 	ret = !strcmp(reader->line, section); | ||||
|  | ||||
| 	if (!peek) { | ||||
| 		if (!ret) | ||||
| 			die("expected '%s', received '%s'", | ||||
| 			    section, reader->line); | ||||
| 		packet_reader_read(reader); | ||||
| 	} | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| static int process_acks(struct packet_reader *reader, struct oidset *common) | ||||
| { | ||||
| 	/* received */ | ||||
| 	int received_ready = 0; | ||||
| 	int received_ack = 0; | ||||
|  | ||||
| 	process_section_header(reader, "acknowledgments", 0); | ||||
| 	while (packet_reader_read(reader) == PACKET_READ_NORMAL) { | ||||
| 		const char *arg; | ||||
|  | ||||
| 		if (!strcmp(reader->line, "NAK")) | ||||
| 			continue; | ||||
|  | ||||
| 		if (skip_prefix(reader->line, "ACK ", &arg)) { | ||||
| 			struct object_id oid; | ||||
| 			if (!get_oid_hex(arg, &oid)) { | ||||
| 				struct commit *commit; | ||||
| 				oidset_insert(common, &oid); | ||||
| 				commit = lookup_commit(&oid); | ||||
| 				mark_common(commit, 0, 1); | ||||
| 			} | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		if (!strcmp(reader->line, "ready")) { | ||||
| 			clear_prio_queue(&rev_list); | ||||
| 			received_ready = 1; | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		die("unexpected acknowledgment line: '%s'", reader->line); | ||||
| 	} | ||||
|  | ||||
| 	if (reader->status != PACKET_READ_FLUSH && | ||||
| 	    reader->status != PACKET_READ_DELIM) | ||||
| 		die("error processing acks: %d", reader->status); | ||||
|  | ||||
| 	/* return 0 if no common, 1 if there are common, or 2 if ready */ | ||||
| 	return received_ready ? 2 : (received_ack ? 1 : 0); | ||||
| } | ||||
|  | ||||
| enum fetch_state { | ||||
| 	FETCH_CHECK_LOCAL = 0, | ||||
| 	FETCH_SEND_REQUEST, | ||||
| 	FETCH_PROCESS_ACKS, | ||||
| 	FETCH_GET_PACK, | ||||
| 	FETCH_DONE, | ||||
| }; | ||||
|  | ||||
| static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args, | ||||
| 				    int fd[2], | ||||
| 				    const struct ref *orig_ref, | ||||
| 				    struct ref **sought, int nr_sought, | ||||
| 				    char **pack_lockfile) | ||||
| { | ||||
| 	struct ref *ref = copy_ref_list(orig_ref); | ||||
| 	enum fetch_state state = FETCH_CHECK_LOCAL; | ||||
| 	struct oidset common = OIDSET_INIT; | ||||
| 	struct packet_reader reader; | ||||
| 	int in_vain = 0; | ||||
| 	int haves_to_send = INITIAL_FLUSH; | ||||
| 	packet_reader_init(&reader, fd[0], NULL, 0, | ||||
| 			   PACKET_READ_CHOMP_NEWLINE); | ||||
|  | ||||
| 	while (state != FETCH_DONE) { | ||||
| 		switch (state) { | ||||
| 		case FETCH_CHECK_LOCAL: | ||||
| 			sort_ref_list(&ref, ref_compare_name); | ||||
| 			QSORT(sought, nr_sought, cmp_ref_by_name); | ||||
|  | ||||
| 			/* v2 supports these by default */ | ||||
| 			allow_unadvertised_object_request |= ALLOW_REACHABLE_SHA1; | ||||
| 			use_sideband = 2; | ||||
|  | ||||
| 			if (marked) | ||||
| 				for_each_ref(clear_marks, NULL); | ||||
| 			marked = 1; | ||||
|  | ||||
| 			for_each_ref(rev_list_insert_ref_oid, NULL); | ||||
| 			for_each_cached_alternate(insert_one_alternate_object); | ||||
|  | ||||
| 			/* Filter 'ref' by 'sought' and those that aren't local */ | ||||
| 			if (everything_local(args, &ref, sought, nr_sought)) | ||||
| 				state = FETCH_DONE; | ||||
| 			else | ||||
| 				state = FETCH_SEND_REQUEST; | ||||
| 			break; | ||||
| 		case FETCH_SEND_REQUEST: | ||||
| 			if (send_fetch_request(fd[1], args, ref, &common, | ||||
| 					       &haves_to_send, &in_vain)) | ||||
| 				state = FETCH_GET_PACK; | ||||
| 			else | ||||
| 				state = FETCH_PROCESS_ACKS; | ||||
| 			break; | ||||
| 		case FETCH_PROCESS_ACKS: | ||||
| 			/* Process ACKs/NAKs */ | ||||
| 			switch (process_acks(&reader, &common)) { | ||||
| 			case 2: | ||||
| 				state = FETCH_GET_PACK; | ||||
| 				break; | ||||
| 			case 1: | ||||
| 				in_vain = 0; | ||||
| 				/* fallthrough */ | ||||
| 			default: | ||||
| 				state = FETCH_SEND_REQUEST; | ||||
| 				break; | ||||
| 			} | ||||
| 			break; | ||||
| 		case FETCH_GET_PACK: | ||||
| 			/* get the pack */ | ||||
| 			process_section_header(&reader, "packfile", 0); | ||||
| 			if (get_pack(args, fd, pack_lockfile)) | ||||
| 				die(_("git fetch-pack: fetch failed.")); | ||||
|  | ||||
| 			state = FETCH_DONE; | ||||
| 			break; | ||||
| 		case FETCH_DONE: | ||||
| 			continue; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	oidset_clear(&common); | ||||
| 	return ref; | ||||
| } | ||||
|  | ||||
| static void fetch_pack_config(void) | ||||
| { | ||||
| 	git_config_get_int("fetch.unpacklimit", &fetch_unpack_limit); | ||||
|  | @ -1153,7 +1406,8 @@ struct ref *fetch_pack(struct fetch_pack_args *args, | |||
| 		       const char *dest, | ||||
| 		       struct ref **sought, int nr_sought, | ||||
| 		       struct oid_array *shallow, | ||||
| 		       char **pack_lockfile) | ||||
| 		       char **pack_lockfile, | ||||
| 		       enum protocol_version version) | ||||
| { | ||||
| 	struct ref *ref_cpy; | ||||
| 	struct shallow_info si; | ||||
|  | @ -1167,8 +1421,12 @@ 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); | ||||
| 	if (version == protocol_v2) | ||||
| 		ref_cpy = do_fetch_pack_v2(args, fd, ref, sought, nr_sought, | ||||
| 					   pack_lockfile); | ||||
| 	else | ||||
| 		ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought, | ||||
| 					&si, pack_lockfile); | ||||
| 	reprepare_packed_git(); | ||||
| 	update_shallow(args, sought, nr_sought, &si); | ||||
| 	clear_shallow_info(&si); | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ | |||
|  | ||||
| #include "string-list.h" | ||||
| #include "run-command.h" | ||||
| #include "protocol.h" | ||||
|  | ||||
| struct oid_array; | ||||
|  | ||||
|  | @ -43,7 +44,8 @@ struct ref *fetch_pack(struct fetch_pack_args *args, | |||
| 		       struct ref **sought, | ||||
| 		       int nr_sought, | ||||
| 		       struct oid_array *shallow, | ||||
| 		       char **pack_lockfile); | ||||
| 		       char **pack_lockfile, | ||||
| 		       enum protocol_version version); | ||||
|  | ||||
| /* | ||||
|  * Print an appropriate error message for each sought ref that wasn't | ||||
|  |  | |||
							
								
								
									
										2
									
								
								serve.c
								
								
								
								
							
							
						
						
									
										2
									
								
								serve.c
								
								
								
								
							|  | @ -55,7 +55,7 @@ struct protocol_capability { | |||
| static struct protocol_capability capabilities[] = { | ||||
| 	{ "agent", agent_advertise, NULL }, | ||||
| 	{ "ls-refs", always_advertise, ls_refs }, | ||||
| 	{ "fetch", always_advertise, upload_pack_v2 }, | ||||
| 	{ "fetch", upload_pack_advertise, upload_pack_v2 }, | ||||
| }; | ||||
|  | ||||
| static void advertise_capabilities(void) | ||||
|  |  | |||
|  | @ -9,7 +9,7 @@ test_expect_success 'test capability advertisement' ' | |||
| 	version 2 | ||||
| 	agent=git/$(git version | cut -d" " -f3) | ||||
| 	ls-refs | ||||
| 	fetch | ||||
| 	fetch=shallow | ||||
| 	0000 | ||||
| 	EOF | ||||
|  | ||||
|  |  | |||
|  | @ -45,6 +45,56 @@ test_expect_success 'ref advertisment is filtered with ls-remote using protocol | |||
| 	test_cmp actual expect | ||||
| ' | ||||
|  | ||||
| test_expect_success 'clone with git:// using protocol v2' ' | ||||
| 	test_when_finished "rm -f log" && | ||||
|  | ||||
| 	GIT_TRACE_PACKET="$(pwd)/log" git -c protocol.version=2 \ | ||||
| 		clone "$GIT_DAEMON_URL/parent" daemon_child && | ||||
|  | ||||
| 	git -C daemon_child log -1 --format=%s >actual && | ||||
| 	git -C "$daemon_parent" log -1 --format=%s >expect && | ||||
| 	test_cmp expect actual && | ||||
|  | ||||
| 	# Client requested to use protocol v2 | ||||
| 	grep "clone> .*\\\0\\\0version=2\\\0$" log && | ||||
| 	# Server responded using protocol v2 | ||||
| 	grep "clone< version 2" log | ||||
| ' | ||||
|  | ||||
| test_expect_success 'fetch with git:// using protocol v2' ' | ||||
| 	test_when_finished "rm -f log" && | ||||
|  | ||||
| 	test_commit -C "$daemon_parent" two && | ||||
|  | ||||
| 	GIT_TRACE_PACKET="$(pwd)/log" git -C daemon_child -c protocol.version=2 \ | ||||
| 		fetch && | ||||
|  | ||||
| 	git -C daemon_child log -1 --format=%s origin/master >actual && | ||||
| 	git -C "$daemon_parent" log -1 --format=%s >expect && | ||||
| 	test_cmp expect actual && | ||||
|  | ||||
| 	# Client requested to use protocol v2 | ||||
| 	grep "fetch> .*\\\0\\\0version=2\\\0$" log && | ||||
| 	# Server responded using protocol v2 | ||||
| 	grep "fetch< version 2" log | ||||
| ' | ||||
|  | ||||
| test_expect_success 'pull with git:// using protocol v2' ' | ||||
| 	test_when_finished "rm -f log" && | ||||
|  | ||||
| 	GIT_TRACE_PACKET="$(pwd)/log" git -C daemon_child -c protocol.version=2 \ | ||||
| 		pull && | ||||
|  | ||||
| 	git -C daemon_child log -1 --format=%s >actual && | ||||
| 	git -C "$daemon_parent" log -1 --format=%s >expect && | ||||
| 	test_cmp expect actual && | ||||
|  | ||||
| 	# Client requested to use protocol v2 | ||||
| 	grep "fetch> .*\\\0\\\0version=2\\\0$" log && | ||||
| 	# Server responded using protocol v2 | ||||
| 	grep "fetch< version 2" log | ||||
| ' | ||||
|  | ||||
| stop_git_daemon | ||||
|  | ||||
| # Test protocol v2 with 'file://' transport | ||||
|  | @ -80,4 +130,51 @@ test_expect_success 'ref advertisment is filtered with ls-remote using protocol | |||
| 	test_cmp actual expect | ||||
| ' | ||||
|  | ||||
| test_expect_success 'clone with file:// using protocol v2' ' | ||||
| 	test_when_finished "rm -f log" && | ||||
|  | ||||
| 	GIT_TRACE_PACKET="$(pwd)/log" git -c protocol.version=2 \ | ||||
| 		clone "file://$(pwd)/file_parent" file_child && | ||||
|  | ||||
| 	git -C file_child log -1 --format=%s >actual && | ||||
| 	git -C file_parent log -1 --format=%s >expect && | ||||
| 	test_cmp expect actual && | ||||
|  | ||||
| 	# Server responded using protocol v2 | ||||
| 	grep "clone< version 2" log | ||||
| ' | ||||
|  | ||||
| test_expect_success 'fetch with file:// using protocol v2' ' | ||||
| 	test_when_finished "rm -f log" && | ||||
|  | ||||
| 	test_commit -C file_parent two && | ||||
|  | ||||
| 	GIT_TRACE_PACKET="$(pwd)/log" git -C file_child -c protocol.version=2 \ | ||||
| 		fetch origin && | ||||
|  | ||||
| 	git -C file_child log -1 --format=%s origin/master >actual && | ||||
| 	git -C file_parent log -1 --format=%s >expect && | ||||
| 	test_cmp expect actual && | ||||
|  | ||||
| 	# Server responded using protocol v2 | ||||
| 	grep "fetch< version 2" log | ||||
| ' | ||||
|  | ||||
| test_expect_success 'ref advertisment is filtered during fetch using protocol v2' ' | ||||
| 	test_when_finished "rm -f log" && | ||||
|  | ||||
| 	test_commit -C file_parent three && | ||||
|  | ||||
| 	GIT_TRACE_PACKET="$(pwd)/log" git -C file_child -c protocol.version=2 \ | ||||
| 		fetch origin master && | ||||
|  | ||||
| 	git -C file_child log -1 --format=%s origin/master >actual && | ||||
| 	git -C file_parent log -1 --format=%s >expect && | ||||
| 	test_cmp expect actual && | ||||
|  | ||||
| 	! grep "refs/tags/one" log && | ||||
| 	! grep "refs/tags/two" log && | ||||
| 	! grep "refs/tags/three" log | ||||
| ' | ||||
|  | ||||
| test_done | ||||
|  |  | |||
|  | @ -258,14 +258,17 @@ static int fetch_refs_via_pack(struct transport *transport, | |||
|  | ||||
| 	switch (data->version) { | ||||
| 	case protocol_v2: | ||||
| 		die("support for protocol v2 not implemented yet"); | ||||
| 		refs = fetch_pack(&args, data->fd, data->conn, | ||||
| 				  refs_tmp ? refs_tmp : transport->remote_refs, | ||||
| 				  dest, to_fetch, nr_heads, &data->shallow, | ||||
| 				  &transport->pack_lockfile, data->version); | ||||
| 		break; | ||||
| 	case protocol_v1: | ||||
| 	case protocol_v0: | ||||
| 		refs = fetch_pack(&args, data->fd, data->conn, | ||||
| 				  refs_tmp ? refs_tmp : transport->remote_refs, | ||||
| 				  dest, to_fetch, nr_heads, &data->shallow, | ||||
| 				  &transport->pack_lockfile); | ||||
| 				  &transport->pack_lockfile, data->version); | ||||
| 		break; | ||||
| 	case protocol_unknown_version: | ||||
| 		BUG("unknown protocol version"); | ||||
|  |  | |||
							
								
								
									
										141
									
								
								upload-pack.c
								
								
								
								
							
							
						
						
									
										141
									
								
								upload-pack.c
								
								
								
								
							|  | @ -710,7 +710,6 @@ static void deepen(int depth, int deepen_relative, | |||
| 	} | ||||
|  | ||||
| 	send_unshallow(shallows); | ||||
| 	packet_flush(1); | ||||
| } | ||||
|  | ||||
| static void deepen_by_rev_list(int ac, const char **av, | ||||
|  | @ -722,7 +721,53 @@ static void deepen_by_rev_list(int ac, const char **av, | |||
| 	send_shallow(result); | ||||
| 	free_commit_list(result); | ||||
| 	send_unshallow(shallows); | ||||
| 	packet_flush(1); | ||||
| } | ||||
|  | ||||
| /* Returns 1 if a shallow list is sent or 0 otherwise */ | ||||
| static int send_shallow_list(int depth, int deepen_rev_list, | ||||
| 			     timestamp_t deepen_since, | ||||
| 			     struct string_list *deepen_not, | ||||
| 			     struct object_array *shallows) | ||||
| { | ||||
| 	int ret = 0; | ||||
|  | ||||
| 	if (depth > 0 && deepen_rev_list) | ||||
| 		die("git upload-pack: deepen and deepen-since (or deepen-not) cannot be used together"); | ||||
| 	if (depth > 0) { | ||||
| 		deepen(depth, deepen_relative, shallows); | ||||
| 		ret = 1; | ||||
| 	} else if (deepen_rev_list) { | ||||
| 		struct argv_array av = ARGV_ARRAY_INIT; | ||||
| 		int i; | ||||
|  | ||||
| 		argv_array_push(&av, "rev-list"); | ||||
| 		if (deepen_since) | ||||
| 			argv_array_pushf(&av, "--max-age=%"PRItime, deepen_since); | ||||
| 		if (deepen_not->nr) { | ||||
| 			argv_array_push(&av, "--not"); | ||||
| 			for (i = 0; i < deepen_not->nr; i++) { | ||||
| 				struct string_list_item *s = deepen_not->items + i; | ||||
| 				argv_array_push(&av, s->string); | ||||
| 			} | ||||
| 			argv_array_push(&av, "--not"); | ||||
| 		} | ||||
| 		for (i = 0; i < want_obj.nr; i++) { | ||||
| 			struct object *o = want_obj.objects[i].item; | ||||
| 			argv_array_push(&av, oid_to_hex(&o->oid)); | ||||
| 		} | ||||
| 		deepen_by_rev_list(av.argc, av.argv, shallows); | ||||
| 		argv_array_clear(&av); | ||||
| 		ret = 1; | ||||
| 	} else { | ||||
| 		if (shallows->nr > 0) { | ||||
| 			int i; | ||||
| 			for (i = 0; i < shallows->nr; i++) | ||||
| 				register_shallow(&shallows->objects[i].item->oid); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	shallow_nr += shallows->nr; | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| static int process_shallow(const char *line, struct object_array *shallows) | ||||
|  | @ -884,40 +929,10 @@ static void receive_needs(void) | |||
|  | ||||
| 	if (depth == 0 && !deepen_rev_list && shallows.nr == 0) | ||||
| 		return; | ||||
| 	if (depth > 0 && deepen_rev_list) | ||||
| 		die("git upload-pack: deepen and deepen-since (or deepen-not) cannot be used together"); | ||||
| 	if (depth > 0) | ||||
| 		deepen(depth, deepen_relative, &shallows); | ||||
| 	else if (deepen_rev_list) { | ||||
| 		struct argv_array av = ARGV_ARRAY_INIT; | ||||
| 		int i; | ||||
|  | ||||
| 		argv_array_push(&av, "rev-list"); | ||||
| 		if (deepen_since) | ||||
| 			argv_array_pushf(&av, "--max-age=%"PRItime, deepen_since); | ||||
| 		if (deepen_not.nr) { | ||||
| 			argv_array_push(&av, "--not"); | ||||
| 			for (i = 0; i < deepen_not.nr; i++) { | ||||
| 				struct string_list_item *s = deepen_not.items + i; | ||||
| 				argv_array_push(&av, s->string); | ||||
| 			} | ||||
| 			argv_array_push(&av, "--not"); | ||||
| 		} | ||||
| 		for (i = 0; i < want_obj.nr; i++) { | ||||
| 			struct object *o = want_obj.objects[i].item; | ||||
| 			argv_array_push(&av, oid_to_hex(&o->oid)); | ||||
| 		} | ||||
| 		deepen_by_rev_list(av.argc, av.argv, &shallows); | ||||
| 		argv_array_clear(&av); | ||||
| 	} | ||||
| 	else | ||||
| 		if (shallows.nr > 0) { | ||||
| 			int i; | ||||
| 			for (i = 0; i < shallows.nr; i++) | ||||
| 				register_shallow(&shallows.objects[i].item->oid); | ||||
| 		} | ||||
|  | ||||
| 	shallow_nr += shallows.nr; | ||||
| 	if (send_shallow_list(depth, deepen_rev_list, deepen_since, | ||||
| 			      &deepen_not, &shallows)) | ||||
| 		packet_flush(1); | ||||
| 	object_array_clear(&shallows); | ||||
| } | ||||
|  | ||||
|  | @ -1071,6 +1086,13 @@ struct upload_pack_data { | |||
| 	struct object_array wants; | ||||
| 	struct oid_array haves; | ||||
|  | ||||
| 	struct object_array shallows; | ||||
| 	struct string_list deepen_not; | ||||
| 	int depth; | ||||
| 	timestamp_t deepen_since; | ||||
| 	int deepen_rev_list; | ||||
| 	int deepen_relative; | ||||
|  | ||||
| 	unsigned stateless_rpc : 1; | ||||
|  | ||||
| 	unsigned use_thin_pack : 1; | ||||
|  | @ -1084,16 +1106,22 @@ static void upload_pack_data_init(struct upload_pack_data *data) | |||
| { | ||||
| 	struct object_array wants = OBJECT_ARRAY_INIT; | ||||
| 	struct oid_array haves = OID_ARRAY_INIT; | ||||
| 	struct object_array shallows = OBJECT_ARRAY_INIT; | ||||
| 	struct string_list deepen_not = STRING_LIST_INIT_DUP; | ||||
|  | ||||
| 	memset(data, 0, sizeof(*data)); | ||||
| 	data->wants = wants; | ||||
| 	data->haves = haves; | ||||
| 	data->shallows = shallows; | ||||
| 	data->deepen_not = deepen_not; | ||||
| } | ||||
|  | ||||
| static void upload_pack_data_clear(struct upload_pack_data *data) | ||||
| { | ||||
| 	object_array_clear(&data->wants); | ||||
| 	oid_array_clear(&data->haves); | ||||
| 	object_array_clear(&data->shallows); | ||||
| 	string_list_clear(&data->deepen_not, 0); | ||||
| } | ||||
|  | ||||
| static int parse_want(const char *line) | ||||
|  | @ -1177,6 +1205,22 @@ static void process_args(struct packet_reader *request, | |||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		/* Shallow related arguments */ | ||||
| 		if (process_shallow(arg, &data->shallows)) | ||||
| 			continue; | ||||
| 		if (process_deepen(arg, &data->depth)) | ||||
| 			continue; | ||||
| 		if (process_deepen_since(arg, &data->deepen_since, | ||||
| 					 &data->deepen_rev_list)) | ||||
| 			continue; | ||||
| 		if (process_deepen_not(arg, &data->deepen_not, | ||||
| 				       &data->deepen_rev_list)) | ||||
| 			continue; | ||||
| 		if (!strcmp(arg, "deepen-relative")) { | ||||
| 			data->deepen_relative = 1; | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		/* ignore unknown lines maybe? */ | ||||
| 		die("unexpect line: '%s'", arg); | ||||
| 	} | ||||
|  | @ -1272,6 +1316,23 @@ static int process_haves_and_send_acks(struct upload_pack_data *data) | |||
| 	return ret; | ||||
| } | ||||
|  | ||||
| static void send_shallow_info(struct upload_pack_data *data) | ||||
| { | ||||
| 	/* No shallow info needs to be sent */ | ||||
| 	if (!data->depth && !data->deepen_rev_list && !data->shallows.nr && | ||||
| 	    !is_repository_shallow()) | ||||
| 		return; | ||||
|  | ||||
| 	packet_write_fmt(1, "shallow-info\n"); | ||||
|  | ||||
| 	if (!send_shallow_list(data->depth, data->deepen_rev_list, | ||||
| 			       data->deepen_since, &data->deepen_not, | ||||
| 			       &data->shallows) && is_repository_shallow()) | ||||
| 		deepen(INFINITE_DEPTH, data->deepen_relative, &data->shallows); | ||||
|  | ||||
| 	packet_delim(1); | ||||
| } | ||||
|  | ||||
| enum fetch_state { | ||||
| 	FETCH_PROCESS_ARGS = 0, | ||||
| 	FETCH_SEND_ACKS, | ||||
|  | @ -1319,6 +1380,8 @@ int upload_pack_v2(struct repository *r, struct argv_array *keys, | |||
| 				state = FETCH_DONE; | ||||
| 			break; | ||||
| 		case FETCH_SEND_PACK: | ||||
| 			send_shallow_info(&data); | ||||
|  | ||||
| 			packet_write_fmt(1, "packfile\n"); | ||||
| 			create_pack_file(); | ||||
| 			state = FETCH_DONE; | ||||
|  | @ -1331,3 +1394,11 @@ int upload_pack_v2(struct repository *r, struct argv_array *keys, | |||
| 	upload_pack_data_clear(&data); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int upload_pack_advertise(struct repository *r, | ||||
| 			  struct strbuf *value) | ||||
| { | ||||
| 	if (value) | ||||
| 		strbuf_addstr(value, "shallow"); | ||||
| 	return 1; | ||||
| } | ||||
|  |  | |||
|  | @ -16,4 +16,8 @@ struct packet_reader; | |||
| extern int upload_pack_v2(struct repository *r, struct argv_array *keys, | ||||
| 			  struct packet_reader *request); | ||||
|  | ||||
| struct strbuf; | ||||
| extern int upload_pack_advertise(struct repository *r, | ||||
| 				 struct strbuf *value); | ||||
|  | ||||
| #endif /* UPLOAD_PACK_H */ | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Brandon Williams
						Brandon Williams