diff: batch fetching of missing blobs
When running a command like "git show" or "git diff" in a partial clone,
batch all missing blobs to be fetched as one request.
This is similar to c0c578b33c ("unpack-trees: batch fetching of missing
blobs", 2017-12-08), but for another command.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
			
			
				maint
			
			
		
							parent
							
								
									0f4a4fb1c4
								
							
						
					
					
						commit
						7fbbcb21b1
					
				
							
								
								
									
										34
									
								
								diff.c
								
								
								
								
							
							
						
						
									
										34
									
								
								diff.c
								
								
								
								
							|  | @ -25,6 +25,7 @@ | |||
| #include "packfile.h" | ||||
| #include "parse-options.h" | ||||
| #include "help.h" | ||||
| #include "fetch-object.h" | ||||
|  | ||||
| #ifdef NO_FAST_WORKING_DIRECTORY | ||||
| #define FAST_WORKING_DIRECTORY 0 | ||||
|  | @ -6366,8 +6367,41 @@ void diffcore_fix_diff_index(void) | |||
| 	QSORT(q->queue, q->nr, diffnamecmp); | ||||
| } | ||||
|  | ||||
| static void add_if_missing(struct repository *r, | ||||
| 			   struct oid_array *to_fetch, | ||||
| 			   const struct diff_filespec *filespec) | ||||
| { | ||||
| 	if (filespec && filespec->oid_valid && | ||||
| 	    oid_object_info_extended(r, &filespec->oid, NULL, | ||||
| 				     OBJECT_INFO_FOR_PREFETCH)) | ||||
| 		oid_array_append(to_fetch, &filespec->oid); | ||||
| } | ||||
|  | ||||
| void diffcore_std(struct diff_options *options) | ||||
| { | ||||
| 	if (options->repo == the_repository && | ||||
| 	    repository_format_partial_clone) { | ||||
| 		/* | ||||
| 		 * Prefetch the diff pairs that are about to be flushed. | ||||
| 		 */ | ||||
| 		int i; | ||||
| 		struct diff_queue_struct *q = &diff_queued_diff; | ||||
| 		struct oid_array to_fetch = OID_ARRAY_INIT; | ||||
|  | ||||
| 		for (i = 0; i < q->nr; i++) { | ||||
| 			struct diff_filepair *p = q->queue[i]; | ||||
| 			add_if_missing(options->repo, &to_fetch, p->one); | ||||
| 			add_if_missing(options->repo, &to_fetch, p->two); | ||||
| 		} | ||||
| 		if (to_fetch.nr) | ||||
| 			/* | ||||
| 			 * NEEDSWORK: Consider deduplicating the OIDs sent. | ||||
| 			 */ | ||||
| 			fetch_objects(repository_format_partial_clone, | ||||
| 				      to_fetch.oid, to_fetch.nr); | ||||
| 		oid_array_clear(&to_fetch); | ||||
| 	} | ||||
|  | ||||
| 	/* NOTE please keep the following in sync with diff_tree_combined() */ | ||||
| 	if (options->skip_stat_unmatch) | ||||
| 		diffcore_skip_stat_unmatch(options); | ||||
|  |  | |||
|  | @ -0,0 +1,103 @@ | |||
| #!/bin/sh | ||||
|  | ||||
| test_description='behavior of diff when reading objects in a partial clone' | ||||
|  | ||||
| . ./test-lib.sh | ||||
|  | ||||
| test_expect_success 'git show batches blobs' ' | ||||
| 	test_when_finished "rm -rf server client trace" && | ||||
|  | ||||
| 	test_create_repo server && | ||||
| 	echo a >server/a && | ||||
| 	echo b >server/b && | ||||
| 	git -C server add a b && | ||||
| 	git -C server commit -m x && | ||||
|  | ||||
| 	test_config -C server uploadpack.allowfilter 1 && | ||||
| 	test_config -C server uploadpack.allowanysha1inwant 1 && | ||||
| 	git clone --bare --filter=blob:limit=0 "file://$(pwd)/server" client && | ||||
|  | ||||
| 	# Ensure that there is exactly 1 negotiation by checking that there is | ||||
| 	# only 1 "done" line sent. ("done" marks the end of negotiation.) | ||||
| 	GIT_TRACE_PACKET="$(pwd)/trace" git -C client show HEAD && | ||||
| 	grep "git> done" trace >done_lines && | ||||
| 	test_line_count = 1 done_lines | ||||
| ' | ||||
|  | ||||
| test_expect_success 'diff batches blobs' ' | ||||
| 	test_when_finished "rm -rf server client trace" && | ||||
|  | ||||
| 	test_create_repo server && | ||||
| 	echo a >server/a && | ||||
| 	echo b >server/b && | ||||
| 	git -C server add a b && | ||||
| 	git -C server commit -m x && | ||||
| 	echo c >server/c && | ||||
| 	echo d >server/d && | ||||
| 	git -C server add c d && | ||||
| 	git -C server commit -m x && | ||||
|  | ||||
| 	test_config -C server uploadpack.allowfilter 1 && | ||||
| 	test_config -C server uploadpack.allowanysha1inwant 1 && | ||||
| 	git clone --bare --filter=blob:limit=0 "file://$(pwd)/server" client && | ||||
|  | ||||
| 	# Ensure that there is exactly 1 negotiation by checking that there is | ||||
| 	# only 1 "done" line sent. ("done" marks the end of negotiation.) | ||||
| 	GIT_TRACE_PACKET="$(pwd)/trace" git -C client diff HEAD^ HEAD && | ||||
| 	grep "git> done" trace >done_lines && | ||||
| 	test_line_count = 1 done_lines | ||||
| ' | ||||
|  | ||||
| test_expect_success 'diff skips same-OID blobs' ' | ||||
| 	test_when_finished "rm -rf server client trace" && | ||||
|  | ||||
| 	test_create_repo server && | ||||
| 	echo a >server/a && | ||||
| 	echo b >server/b && | ||||
| 	git -C server add a b && | ||||
| 	git -C server commit -m x && | ||||
| 	echo another-a >server/a && | ||||
| 	git -C server add a && | ||||
| 	git -C server commit -m x && | ||||
|  | ||||
| 	test_config -C server uploadpack.allowfilter 1 && | ||||
| 	test_config -C server uploadpack.allowanysha1inwant 1 && | ||||
| 	git clone --bare --filter=blob:limit=0 "file://$(pwd)/server" client && | ||||
|  | ||||
| 	echo a | git hash-object --stdin >hash-old-a && | ||||
| 	echo another-a | git hash-object --stdin >hash-new-a && | ||||
| 	echo b | git hash-object --stdin >hash-b && | ||||
|  | ||||
| 	# Ensure that only a and another-a are fetched. | ||||
| 	GIT_TRACE_PACKET="$(pwd)/trace" git -C client diff HEAD^ HEAD && | ||||
| 	grep "want $(cat hash-old-a)" trace && | ||||
| 	grep "want $(cat hash-new-a)" trace && | ||||
| 	! grep "want $(cat hash-b)" trace | ||||
| ' | ||||
|  | ||||
| test_expect_success 'diff with rename detection batches blobs' ' | ||||
| 	test_when_finished "rm -rf server client trace" && | ||||
|  | ||||
| 	test_create_repo server && | ||||
| 	echo a >server/a && | ||||
| 	printf "b\nb\nb\nb\nb\n" >server/b && | ||||
| 	git -C server add a b && | ||||
| 	git -C server commit -m x && | ||||
| 	rm server/b && | ||||
| 	printf "b\nb\nb\nb\nbX\n" >server/c && | ||||
| 	git -C server add c && | ||||
| 	git -C server commit -a -m x && | ||||
|  | ||||
| 	test_config -C server uploadpack.allowfilter 1 && | ||||
| 	test_config -C server uploadpack.allowanysha1inwant 1 && | ||||
| 	git clone --bare --filter=blob:limit=0 "file://$(pwd)/server" client && | ||||
|  | ||||
| 	# Ensure that there is exactly 1 negotiation by checking that there is | ||||
| 	# only 1 "done" line sent. ("done" marks the end of negotiation.) | ||||
| 	GIT_TRACE_PACKET="$(pwd)/trace" git -C client diff -M HEAD^ HEAD >out && | ||||
| 	grep "similarity index" out && | ||||
| 	grep "git> done" trace >done_lines && | ||||
| 	test_line_count = 1 done_lines | ||||
| ' | ||||
|  | ||||
| test_done | ||||
		Loading…
	
		Reference in New Issue
	
	 Jonathan Tan
						Jonathan Tan