Merge branch 'jt/avoid-prefetch-when-able-in-diff'
"git diff" in a partial clone learned to avoid lazy loading blob objects in more casese when they are not needed. * jt/avoid-prefetch-when-able-in-diff: diff: restrict when prefetching occurs diff: refactor object read diff: make diff_populate_filespec_options struct promisor-remote: accept 0 as oid_nr in functionmaint
						commit
						8f5dc5a4af
					
				|  | @ -1368,9 +1368,8 @@ static void fix_unresolved_deltas(struct hashfile *f) | ||||||
| 				continue; | 				continue; | ||||||
| 			oid_array_append(&to_fetch, &d->oid); | 			oid_array_append(&to_fetch, &d->oid); | ||||||
| 		} | 		} | ||||||
| 		if (to_fetch.nr) | 		promisor_remote_get_direct(the_repository, | ||||||
| 			promisor_remote_get_direct(the_repository, | 					   to_fetch.oid, to_fetch.nr); | ||||||
| 						   to_fetch.oid, to_fetch.nr); |  | ||||||
| 		oid_array_clear(&to_fetch); | 		oid_array_clear(&to_fetch); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  |  | ||||||
							
								
								
									
										157
									
								
								diff.c
								
								
								
								
							
							
						
						
									
										157
									
								
								diff.c
								
								
								
								
							|  | @ -573,7 +573,7 @@ static int fill_mmfile(struct repository *r, mmfile_t *mf, | ||||||
| 		mf->size = 0; | 		mf->size = 0; | ||||||
| 		return 0; | 		return 0; | ||||||
| 	} | 	} | ||||||
| 	else if (diff_populate_filespec(r, one, 0)) | 	else if (diff_populate_filespec(r, one, NULL)) | ||||||
| 		return -1; | 		return -1; | ||||||
|  |  | ||||||
| 	mf->ptr = one->data; | 	mf->ptr = one->data; | ||||||
|  | @ -585,9 +585,13 @@ static int fill_mmfile(struct repository *r, mmfile_t *mf, | ||||||
| static unsigned long diff_filespec_size(struct repository *r, | static unsigned long diff_filespec_size(struct repository *r, | ||||||
| 					struct diff_filespec *one) | 					struct diff_filespec *one) | ||||||
| { | { | ||||||
|  | 	struct diff_populate_filespec_options dpf_options = { | ||||||
|  | 		.check_size_only = 1, | ||||||
|  | 	}; | ||||||
|  |  | ||||||
| 	if (!DIFF_FILE_VALID(one)) | 	if (!DIFF_FILE_VALID(one)) | ||||||
| 		return 0; | 		return 0; | ||||||
| 	diff_populate_filespec(r, one, CHECK_SIZE_ONLY); | 	diff_populate_filespec(r, one, &dpf_options); | ||||||
| 	return one->size; | 	return one->size; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -3020,6 +3024,9 @@ static void show_dirstat(struct diff_options *options) | ||||||
| 		struct diff_filepair *p = q->queue[i]; | 		struct diff_filepair *p = q->queue[i]; | ||||||
| 		const char *name; | 		const char *name; | ||||||
| 		unsigned long copied, added, damage; | 		unsigned long copied, added, damage; | ||||||
|  | 		struct diff_populate_filespec_options dpf_options = { | ||||||
|  | 			.check_size_only = 1, | ||||||
|  | 		}; | ||||||
|  |  | ||||||
| 		name = p->two->path ? p->two->path : p->one->path; | 		name = p->two->path ? p->two->path : p->one->path; | ||||||
|  |  | ||||||
|  | @ -3047,19 +3054,19 @@ static void show_dirstat(struct diff_options *options) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) { | 		if (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) { | ||||||
| 			diff_populate_filespec(options->repo, p->one, 0); | 			diff_populate_filespec(options->repo, p->one, NULL); | ||||||
| 			diff_populate_filespec(options->repo, p->two, 0); | 			diff_populate_filespec(options->repo, p->two, NULL); | ||||||
| 			diffcore_count_changes(options->repo, | 			diffcore_count_changes(options->repo, | ||||||
| 					       p->one, p->two, NULL, NULL, | 					       p->one, p->two, NULL, NULL, | ||||||
| 					       &copied, &added); | 					       &copied, &added); | ||||||
| 			diff_free_filespec_data(p->one); | 			diff_free_filespec_data(p->one); | ||||||
| 			diff_free_filespec_data(p->two); | 			diff_free_filespec_data(p->two); | ||||||
| 		} else if (DIFF_FILE_VALID(p->one)) { | 		} else if (DIFF_FILE_VALID(p->one)) { | ||||||
| 			diff_populate_filespec(options->repo, p->one, CHECK_SIZE_ONLY); | 			diff_populate_filespec(options->repo, p->one, &dpf_options); | ||||||
| 			copied = added = 0; | 			copied = added = 0; | ||||||
| 			diff_free_filespec_data(p->one); | 			diff_free_filespec_data(p->one); | ||||||
| 		} else if (DIFF_FILE_VALID(p->two)) { | 		} else if (DIFF_FILE_VALID(p->two)) { | ||||||
| 			diff_populate_filespec(options->repo, p->two, CHECK_SIZE_ONLY); | 			diff_populate_filespec(options->repo, p->two, &dpf_options); | ||||||
| 			copied = 0; | 			copied = 0; | ||||||
| 			added = p->two->size; | 			added = p->two->size; | ||||||
| 			diff_free_filespec_data(p->two); | 			diff_free_filespec_data(p->two); | ||||||
|  | @ -3339,13 +3346,17 @@ static void emit_binary_diff(struct diff_options *o, | ||||||
| int diff_filespec_is_binary(struct repository *r, | int diff_filespec_is_binary(struct repository *r, | ||||||
| 			    struct diff_filespec *one) | 			    struct diff_filespec *one) | ||||||
| { | { | ||||||
|  | 	struct diff_populate_filespec_options dpf_options = { | ||||||
|  | 		.check_binary = 1, | ||||||
|  | 	}; | ||||||
|  |  | ||||||
| 	if (one->is_binary == -1) { | 	if (one->is_binary == -1) { | ||||||
| 		diff_filespec_load_driver(one, r->index); | 		diff_filespec_load_driver(one, r->index); | ||||||
| 		if (one->driver->binary != -1) | 		if (one->driver->binary != -1) | ||||||
| 			one->is_binary = one->driver->binary; | 			one->is_binary = one->driver->binary; | ||||||
| 		else { | 		else { | ||||||
| 			if (!one->data && DIFF_FILE_VALID(one)) | 			if (!one->data && DIFF_FILE_VALID(one)) | ||||||
| 				diff_populate_filespec(r, one, CHECK_BINARY); | 				diff_populate_filespec(r, one, &dpf_options); | ||||||
| 			if (one->is_binary == -1 && one->data) | 			if (one->is_binary == -1 && one->data) | ||||||
| 				one->is_binary = buffer_is_binary(one->data, | 				one->is_binary = buffer_is_binary(one->data, | ||||||
| 						one->size); | 						one->size); | ||||||
|  | @ -3677,8 +3688,8 @@ static void builtin_diffstat(const char *name_a, const char *name_b, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	else if (complete_rewrite) { | 	else if (complete_rewrite) { | ||||||
| 		diff_populate_filespec(o->repo, one, 0); | 		diff_populate_filespec(o->repo, one, NULL); | ||||||
| 		diff_populate_filespec(o->repo, two, 0); | 		diff_populate_filespec(o->repo, two, NULL); | ||||||
| 		data->deleted = count_lines(one->data, one->size); | 		data->deleted = count_lines(one->data, one->size); | ||||||
| 		data->added = count_lines(two->data, two->size); | 		data->added = count_lines(two->data, two->size); | ||||||
| 	} | 	} | ||||||
|  | @ -3914,9 +3925,10 @@ static int diff_populate_gitlink(struct diff_filespec *s, int size_only) | ||||||
|  */ |  */ | ||||||
| int diff_populate_filespec(struct repository *r, | int diff_populate_filespec(struct repository *r, | ||||||
| 			   struct diff_filespec *s, | 			   struct diff_filespec *s, | ||||||
| 			   unsigned int flags) | 			   const struct diff_populate_filespec_options *options) | ||||||
| { | { | ||||||
| 	int size_only = flags & CHECK_SIZE_ONLY; | 	int size_only = options ? options->check_size_only : 0; | ||||||
|  | 	int check_binary = options ? options->check_binary : 0; | ||||||
| 	int err = 0; | 	int err = 0; | ||||||
| 	int conv_flags = global_conv_flags_eol; | 	int conv_flags = global_conv_flags_eol; | ||||||
| 	/* | 	/* | ||||||
|  | @ -3986,7 +3998,7 @@ int diff_populate_filespec(struct repository *r, | ||||||
| 		 * opening the file and inspecting the contents, this | 		 * opening the file and inspecting the contents, this | ||||||
| 		 * is probably fine. | 		 * is probably fine. | ||||||
| 		 */ | 		 */ | ||||||
| 		if ((flags & CHECK_BINARY) && | 		if (check_binary && | ||||||
| 		    s->size > big_file_threshold && s->is_binary == -1) { | 		    s->size > big_file_threshold && s->is_binary == -1) { | ||||||
| 			s->is_binary = 1; | 			s->is_binary = 1; | ||||||
| 			return 0; | 			return 0; | ||||||
|  | @ -4011,12 +4023,30 @@ int diff_populate_filespec(struct repository *r, | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	else { | 	else { | ||||||
| 		enum object_type type; | 		struct object_info info = { | ||||||
| 		if (size_only || (flags & CHECK_BINARY)) { | 			.sizep = &s->size | ||||||
| 			type = oid_object_info(r, &s->oid, &s->size); | 		}; | ||||||
| 			if (type < 0) |  | ||||||
| 				die("unable to read %s", | 		if (!(size_only || check_binary)) | ||||||
| 				    oid_to_hex(&s->oid)); | 			/* | ||||||
|  | 			 * Set contentp, since there is no chance that merely | ||||||
|  | 			 * the size is sufficient. | ||||||
|  | 			 */ | ||||||
|  | 			info.contentp = &s->data; | ||||||
|  |  | ||||||
|  | 		if (options && options->missing_object_cb) { | ||||||
|  | 			if (!oid_object_info_extended(r, &s->oid, &info, | ||||||
|  | 						      OBJECT_INFO_LOOKUP_REPLACE | | ||||||
|  | 						      OBJECT_INFO_SKIP_FETCH_OBJECT)) | ||||||
|  | 				goto object_read; | ||||||
|  | 			options->missing_object_cb(options->missing_object_data); | ||||||
|  | 		} | ||||||
|  | 		if (oid_object_info_extended(r, &s->oid, &info, | ||||||
|  | 					     OBJECT_INFO_LOOKUP_REPLACE)) | ||||||
|  | 			die("unable to read %s", oid_to_hex(&s->oid)); | ||||||
|  |  | ||||||
|  | object_read: | ||||||
|  | 		if (size_only || check_binary) { | ||||||
| 			if (size_only) | 			if (size_only) | ||||||
| 				return 0; | 				return 0; | ||||||
| 			if (s->size > big_file_threshold && s->is_binary == -1) { | 			if (s->size > big_file_threshold && s->is_binary == -1) { | ||||||
|  | @ -4024,9 +4054,12 @@ int diff_populate_filespec(struct repository *r, | ||||||
| 				return 0; | 				return 0; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		s->data = repo_read_object_file(r, &s->oid, &type, &s->size); | 		if (!info.contentp) { | ||||||
| 		if (!s->data) | 			info.contentp = &s->data; | ||||||
| 			die("unable to read %s", oid_to_hex(&s->oid)); | 			if (oid_object_info_extended(r, &s->oid, &info, | ||||||
|  | 						     OBJECT_INFO_LOOKUP_REPLACE)) | ||||||
|  | 				die("unable to read %s", oid_to_hex(&s->oid)); | ||||||
|  | 		} | ||||||
| 		s->should_free = 1; | 		s->should_free = 1; | ||||||
| 	} | 	} | ||||||
| 	return 0; | 	return 0; | ||||||
|  | @ -4144,7 +4177,7 @@ static struct diff_tempfile *prepare_temp_file(struct repository *r, | ||||||
| 		return temp; | 		return temp; | ||||||
| 	} | 	} | ||||||
| 	else { | 	else { | ||||||
| 		if (diff_populate_filespec(r, one, 0)) | 		if (diff_populate_filespec(r, one, NULL)) | ||||||
| 			die("cannot read data blob for %s", one->path); | 			die("cannot read data blob for %s", one->path); | ||||||
| 		prep_temp_blob(r->index, name, temp, | 		prep_temp_blob(r->index, name, temp, | ||||||
| 			       one->data, one->size, | 			       one->data, one->size, | ||||||
|  | @ -6410,9 +6443,9 @@ static int diff_filespec_is_identical(struct repository *r, | ||||||
| { | { | ||||||
| 	if (S_ISGITLINK(one->mode)) | 	if (S_ISGITLINK(one->mode)) | ||||||
| 		return 0; | 		return 0; | ||||||
| 	if (diff_populate_filespec(r, one, 0)) | 	if (diff_populate_filespec(r, one, NULL)) | ||||||
| 		return 0; | 		return 0; | ||||||
| 	if (diff_populate_filespec(r, two, 0)) | 	if (diff_populate_filespec(r, two, NULL)) | ||||||
| 		return 0; | 		return 0; | ||||||
| 	return !memcmp(one->data, two->data, one->size); | 	return !memcmp(one->data, two->data, one->size); | ||||||
| } | } | ||||||
|  | @ -6420,6 +6453,12 @@ static int diff_filespec_is_identical(struct repository *r, | ||||||
| static int diff_filespec_check_stat_unmatch(struct repository *r, | static int diff_filespec_check_stat_unmatch(struct repository *r, | ||||||
| 					    struct diff_filepair *p) | 					    struct diff_filepair *p) | ||||||
| { | { | ||||||
|  | 	struct diff_populate_filespec_options dpf_options = { | ||||||
|  | 		.check_size_only = 1, | ||||||
|  | 		.missing_object_cb = diff_queued_diff_prefetch, | ||||||
|  | 		.missing_object_data = r, | ||||||
|  | 	}; | ||||||
|  |  | ||||||
| 	if (p->done_skip_stat_unmatch) | 	if (p->done_skip_stat_unmatch) | ||||||
| 		return p->skip_stat_unmatch_result; | 		return p->skip_stat_unmatch_result; | ||||||
|  |  | ||||||
|  | @ -6442,8 +6481,8 @@ static int diff_filespec_check_stat_unmatch(struct repository *r, | ||||||
| 	    !DIFF_FILE_VALID(p->two) || | 	    !DIFF_FILE_VALID(p->two) || | ||||||
| 	    (p->one->oid_valid && p->two->oid_valid) || | 	    (p->one->oid_valid && p->two->oid_valid) || | ||||||
| 	    (p->one->mode != p->two->mode) || | 	    (p->one->mode != p->two->mode) || | ||||||
| 	    diff_populate_filespec(r, p->one, CHECK_SIZE_ONLY) || | 	    diff_populate_filespec(r, p->one, &dpf_options) || | ||||||
| 	    diff_populate_filespec(r, p->two, CHECK_SIZE_ONLY) || | 	    diff_populate_filespec(r, p->two, &dpf_options) || | ||||||
| 	    (p->one->size != p->two->size) || | 	    (p->one->size != p->two->size) || | ||||||
| 	    !diff_filespec_is_identical(r, p->one, p->two)) /* (2) */ | 	    !diff_filespec_is_identical(r, p->one, p->two)) /* (2) */ | ||||||
| 		p->skip_stat_unmatch_result = 1; | 		p->skip_stat_unmatch_result = 1; | ||||||
|  | @ -6494,9 +6533,9 @@ void diffcore_fix_diff_index(void) | ||||||
| 	QSORT(q->queue, q->nr, diffnamecmp); | 	QSORT(q->queue, q->nr, diffnamecmp); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void add_if_missing(struct repository *r, | void diff_add_if_missing(struct repository *r, | ||||||
| 			   struct oid_array *to_fetch, | 			 struct oid_array *to_fetch, | ||||||
| 			   const struct diff_filespec *filespec) | 			 const struct diff_filespec *filespec) | ||||||
| { | { | ||||||
| 	if (filespec && filespec->oid_valid && | 	if (filespec && filespec->oid_valid && | ||||||
| 	    !S_ISGITLINK(filespec->mode) && | 	    !S_ISGITLINK(filespec->mode) && | ||||||
|  | @ -6505,29 +6544,47 @@ static void add_if_missing(struct repository *r, | ||||||
| 		oid_array_append(to_fetch, &filespec->oid); | 		oid_array_append(to_fetch, &filespec->oid); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void diff_queued_diff_prefetch(void *repository) | ||||||
|  | { | ||||||
|  | 	struct repository *repo = repository; | ||||||
|  | 	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]; | ||||||
|  | 		diff_add_if_missing(repo, &to_fetch, p->one); | ||||||
|  | 		diff_add_if_missing(repo, &to_fetch, p->two); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* | ||||||
|  | 	 * NEEDSWORK: Consider deduplicating the OIDs sent. | ||||||
|  | 	 */ | ||||||
|  | 	promisor_remote_get_direct(repo, to_fetch.oid, to_fetch.nr); | ||||||
|  |  | ||||||
|  | 	oid_array_clear(&to_fetch); | ||||||
|  | } | ||||||
|  |  | ||||||
| void diffcore_std(struct diff_options *options) | void diffcore_std(struct diff_options *options) | ||||||
| { | { | ||||||
| 	if (options->repo == the_repository && has_promisor_remote()) { | 	int output_formats_to_prefetch = DIFF_FORMAT_DIFFSTAT | | ||||||
| 		/* | 		DIFF_FORMAT_NUMSTAT | | ||||||
| 		 * Prefetch the diff pairs that are about to be flushed. | 		DIFF_FORMAT_PATCH | | ||||||
| 		 */ | 		DIFF_FORMAT_SHORTSTAT | | ||||||
| 		int i; | 		DIFF_FORMAT_DIRSTAT; | ||||||
| 		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]; | 	 * Check if the user requested a blob-data-requiring diff output and/or | ||||||
| 			add_if_missing(options->repo, &to_fetch, p->one); | 	 * break-rewrite detection (which requires blob data). If yes, prefetch | ||||||
| 			add_if_missing(options->repo, &to_fetch, p->two); | 	 * the diff pairs. | ||||||
| 		} | 	 * | ||||||
| 		if (to_fetch.nr) | 	 * If no prefetching occurs, diffcore_rename() will prefetch if it | ||||||
| 			/* | 	 * decides that it needs inexact rename detection. | ||||||
| 			 * NEEDSWORK: Consider deduplicating the OIDs sent. | 	 */ | ||||||
| 			 */ | 	if (options->repo == the_repository && has_promisor_remote() && | ||||||
| 			promisor_remote_get_direct(options->repo, | 	    (options->output_format & output_formats_to_prefetch || | ||||||
| 						   to_fetch.oid, to_fetch.nr); | 	     options->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK)) | ||||||
| 		oid_array_clear(&to_fetch); | 		diff_queued_diff_prefetch(options->repo); | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/* NOTE please keep the following in sync with diff_tree_combined() */ | 	/* NOTE please keep the following in sync with diff_tree_combined() */ | ||||||
| 	if (options->skip_stat_unmatch) | 	if (options->skip_stat_unmatch) | ||||||
|  | @ -6774,7 +6831,7 @@ size_t fill_textconv(struct repository *r, | ||||||
| 			*outbuf = ""; | 			*outbuf = ""; | ||||||
| 			return 0; | 			return 0; | ||||||
| 		} | 		} | ||||||
| 		if (diff_populate_filespec(r, df, 0)) | 		if (diff_populate_filespec(r, df, NULL)) | ||||||
| 			die("unable to read files to diff"); | 			die("unable to read files to diff"); | ||||||
| 		*outbuf = df->data; | 		*outbuf = df->data; | ||||||
| 		return df->size; | 		return df->size; | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ | ||||||
| #include "cache.h" | #include "cache.h" | ||||||
| #include "diff.h" | #include "diff.h" | ||||||
| #include "diffcore.h" | #include "diffcore.h" | ||||||
|  | #include "promisor-remote.h" | ||||||
|  |  | ||||||
| static int should_break(struct repository *r, | static int should_break(struct repository *r, | ||||||
| 			struct diff_filespec *src, | 			struct diff_filespec *src, | ||||||
|  | @ -49,6 +50,8 @@ static int should_break(struct repository *r, | ||||||
| 	unsigned long delta_size, max_size; | 	unsigned long delta_size, max_size; | ||||||
| 	unsigned long src_copied, literal_added, src_removed; | 	unsigned long src_copied, literal_added, src_removed; | ||||||
|  |  | ||||||
|  | 	struct diff_populate_filespec_options options = { 0 }; | ||||||
|  |  | ||||||
| 	*merge_score_p = 0; /* assume no deletion --- "do not break" | 	*merge_score_p = 0; /* assume no deletion --- "do not break" | ||||||
| 			     * is the default. | 			     * is the default. | ||||||
| 			     */ | 			     */ | ||||||
|  | @ -62,8 +65,13 @@ static int should_break(struct repository *r, | ||||||
| 	    oideq(&src->oid, &dst->oid)) | 	    oideq(&src->oid, &dst->oid)) | ||||||
| 		return 0; /* they are the same */ | 		return 0; /* they are the same */ | ||||||
|  |  | ||||||
| 	if (diff_populate_filespec(r, src, 0) || | 	if (r == the_repository && has_promisor_remote()) { | ||||||
| 	    diff_populate_filespec(r, dst, 0)) | 		options.missing_object_cb = diff_queued_diff_prefetch; | ||||||
|  | 		options.missing_object_data = r; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (diff_populate_filespec(r, src, &options) || | ||||||
|  | 	    diff_populate_filespec(r, dst, &options)) | ||||||
| 		return 0; /* error but caught downstream */ | 		return 0; /* error but caught downstream */ | ||||||
|  |  | ||||||
| 	max_size = ((src->size > dst->size) ? src->size : dst->size); | 	max_size = ((src->size > dst->size) ? src->size : dst->size); | ||||||
|  |  | ||||||
|  | @ -1,4 +1,5 @@ | ||||||
| /* | /* | ||||||
|  |  * | ||||||
|  * Copyright (C) 2005 Junio C Hamano |  * Copyright (C) 2005 Junio C Hamano | ||||||
|  */ |  */ | ||||||
| #include "cache.h" | #include "cache.h" | ||||||
|  | @ -7,6 +8,7 @@ | ||||||
| #include "object-store.h" | #include "object-store.h" | ||||||
| #include "hashmap.h" | #include "hashmap.h" | ||||||
| #include "progress.h" | #include "progress.h" | ||||||
|  | #include "promisor-remote.h" | ||||||
|  |  | ||||||
| /* Table of rename/copy destinations */ | /* Table of rename/copy destinations */ | ||||||
|  |  | ||||||
|  | @ -128,10 +130,46 @@ struct diff_score { | ||||||
| 	short name_score; | 	short name_score; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | struct prefetch_options { | ||||||
|  | 	struct repository *repo; | ||||||
|  | 	int skip_unmodified; | ||||||
|  | }; | ||||||
|  | static void prefetch(void *prefetch_options) | ||||||
|  | { | ||||||
|  | 	struct prefetch_options *options = prefetch_options; | ||||||
|  | 	int i; | ||||||
|  | 	struct oid_array to_fetch = OID_ARRAY_INIT; | ||||||
|  |  | ||||||
|  | 	for (i = 0; i < rename_dst_nr; i++) { | ||||||
|  | 		if (rename_dst[i].pair) | ||||||
|  | 			/* | ||||||
|  | 			 * The loop in diffcore_rename() will not need these | ||||||
|  | 			 * blobs, so skip prefetching. | ||||||
|  | 			 */ | ||||||
|  | 			continue; /* already found exact match */ | ||||||
|  | 		diff_add_if_missing(options->repo, &to_fetch, | ||||||
|  | 				    rename_dst[i].two); | ||||||
|  | 	} | ||||||
|  | 	for (i = 0; i < rename_src_nr; i++) { | ||||||
|  | 		if (options->skip_unmodified && | ||||||
|  | 		    diff_unmodified_pair(rename_src[i].p)) | ||||||
|  | 			/* | ||||||
|  | 			 * The loop in diffcore_rename() will not need these | ||||||
|  | 			 * blobs, so skip prefetching. | ||||||
|  | 			 */ | ||||||
|  | 			continue; | ||||||
|  | 		diff_add_if_missing(options->repo, &to_fetch, | ||||||
|  | 				    rename_src[i].p->one); | ||||||
|  | 	} | ||||||
|  | 	promisor_remote_get_direct(options->repo, to_fetch.oid, to_fetch.nr); | ||||||
|  | 	oid_array_clear(&to_fetch); | ||||||
|  | } | ||||||
|  |  | ||||||
| static int estimate_similarity(struct repository *r, | static int estimate_similarity(struct repository *r, | ||||||
| 			       struct diff_filespec *src, | 			       struct diff_filespec *src, | ||||||
| 			       struct diff_filespec *dst, | 			       struct diff_filespec *dst, | ||||||
| 			       int minimum_score) | 			       int minimum_score, | ||||||
|  | 			       int skip_unmodified) | ||||||
| { | { | ||||||
| 	/* src points at a file that existed in the original tree (or | 	/* src points at a file that existed in the original tree (or | ||||||
| 	 * optionally a file in the destination tree) and dst points | 	 * optionally a file in the destination tree) and dst points | ||||||
|  | @ -148,6 +186,15 @@ static int estimate_similarity(struct repository *r, | ||||||
| 	 */ | 	 */ | ||||||
| 	unsigned long max_size, delta_size, base_size, src_copied, literal_added; | 	unsigned long max_size, delta_size, base_size, src_copied, literal_added; | ||||||
| 	int score; | 	int score; | ||||||
|  | 	struct diff_populate_filespec_options dpf_options = { | ||||||
|  | 		.check_size_only = 1 | ||||||
|  | 	}; | ||||||
|  | 	struct prefetch_options prefetch_options = {r, skip_unmodified}; | ||||||
|  |  | ||||||
|  | 	if (r == the_repository && has_promisor_remote()) { | ||||||
|  | 		dpf_options.missing_object_cb = prefetch; | ||||||
|  | 		dpf_options.missing_object_data = &prefetch_options; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	/* We deal only with regular files.  Symlink renames are handled | 	/* We deal only with regular files.  Symlink renames are handled | ||||||
| 	 * only when they are exact matches --- in other words, no edits | 	 * only when they are exact matches --- in other words, no edits | ||||||
|  | @ -166,10 +213,10 @@ static int estimate_similarity(struct repository *r, | ||||||
| 	 * say whether the size is valid or not!) | 	 * say whether the size is valid or not!) | ||||||
| 	 */ | 	 */ | ||||||
| 	if (!src->cnt_data && | 	if (!src->cnt_data && | ||||||
| 	    diff_populate_filespec(r, src, CHECK_SIZE_ONLY)) | 	    diff_populate_filespec(r, src, &dpf_options)) | ||||||
| 		return 0; | 		return 0; | ||||||
| 	if (!dst->cnt_data && | 	if (!dst->cnt_data && | ||||||
| 	    diff_populate_filespec(r, dst, CHECK_SIZE_ONLY)) | 	    diff_populate_filespec(r, dst, &dpf_options)) | ||||||
| 		return 0; | 		return 0; | ||||||
|  |  | ||||||
| 	max_size = ((src->size > dst->size) ? src->size : dst->size); | 	max_size = ((src->size > dst->size) ? src->size : dst->size); | ||||||
|  | @ -187,9 +234,11 @@ static int estimate_similarity(struct repository *r, | ||||||
| 	if (max_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE) | 	if (max_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE) | ||||||
| 		return 0; | 		return 0; | ||||||
|  |  | ||||||
| 	if (!src->cnt_data && diff_populate_filespec(r, src, 0)) | 	dpf_options.check_size_only = 0; | ||||||
|  |  | ||||||
|  | 	if (!src->cnt_data && diff_populate_filespec(r, src, &dpf_options)) | ||||||
| 		return 0; | 		return 0; | ||||||
| 	if (!dst->cnt_data && diff_populate_filespec(r, dst, 0)) | 	if (!dst->cnt_data && diff_populate_filespec(r, dst, &dpf_options)) | ||||||
| 		return 0; | 		return 0; | ||||||
|  |  | ||||||
| 	if (diffcore_count_changes(r, src, dst, | 	if (diffcore_count_changes(r, src, dst, | ||||||
|  | @ -261,7 +310,7 @@ static unsigned int hash_filespec(struct repository *r, | ||||||
| 				  struct diff_filespec *filespec) | 				  struct diff_filespec *filespec) | ||||||
| { | { | ||||||
| 	if (!filespec->oid_valid) { | 	if (!filespec->oid_valid) { | ||||||
| 		if (diff_populate_filespec(r, filespec, 0)) | 		if (diff_populate_filespec(r, filespec, NULL)) | ||||||
| 			return 0; | 			return 0; | ||||||
| 		hash_object_file(r->hash_algo, filespec->data, filespec->size, | 		hash_object_file(r->hash_algo, filespec->data, filespec->size, | ||||||
| 				 "blob", &filespec->oid); | 				 "blob", &filespec->oid); | ||||||
|  | @ -566,7 +615,8 @@ void diffcore_rename(struct diff_options *options) | ||||||
|  |  | ||||||
| 			this_src.score = estimate_similarity(options->repo, | 			this_src.score = estimate_similarity(options->repo, | ||||||
| 							     one, two, | 							     one, two, | ||||||
| 							     minimum_score); | 							     minimum_score, | ||||||
|  | 							     skip_unmodified); | ||||||
| 			this_src.name_score = basename_same(one, two); | 			this_src.name_score = basename_same(one, two); | ||||||
| 			this_src.dst = i; | 			this_src.dst = i; | ||||||
| 			this_src.src = j; | 			this_src.src = j; | ||||||
|  |  | ||||||
							
								
								
									
										30
									
								
								diffcore.h
								
								
								
								
							
							
						
						
									
										30
									
								
								diffcore.h
								
								
								
								
							|  | @ -65,9 +65,25 @@ void free_filespec(struct diff_filespec *); | ||||||
| void fill_filespec(struct diff_filespec *, const struct object_id *, | void fill_filespec(struct diff_filespec *, const struct object_id *, | ||||||
| 		   int, unsigned short); | 		   int, unsigned short); | ||||||
|  |  | ||||||
| #define CHECK_SIZE_ONLY 1 | /* | ||||||
| #define CHECK_BINARY    2 |  * Prefetch the entries in diff_queued_diff. The parameter is a pointer to a | ||||||
| int diff_populate_filespec(struct repository *, struct diff_filespec *, unsigned int); |  * struct repository. | ||||||
|  |  */ | ||||||
|  | void diff_queued_diff_prefetch(void *repository); | ||||||
|  |  | ||||||
|  | struct diff_populate_filespec_options { | ||||||
|  | 	unsigned check_size_only : 1; | ||||||
|  | 	unsigned check_binary : 1; | ||||||
|  |  | ||||||
|  | 	/* | ||||||
|  | 	 * If an object is missing, diff_populate_filespec() will invoke this | ||||||
|  | 	 * callback before attempting to read that object again. | ||||||
|  | 	 */ | ||||||
|  | 	void (*missing_object_cb)(void *); | ||||||
|  | 	void *missing_object_data; | ||||||
|  | }; | ||||||
|  | int diff_populate_filespec(struct repository *, struct diff_filespec *, | ||||||
|  | 			   const struct diff_populate_filespec_options *); | ||||||
| void diff_free_filespec_data(struct diff_filespec *); | void diff_free_filespec_data(struct diff_filespec *); | ||||||
| void diff_free_filespec_blob(struct diff_filespec *); | void diff_free_filespec_blob(struct diff_filespec *); | ||||||
| int diff_filespec_is_binary(struct repository *, struct diff_filespec *); | int diff_filespec_is_binary(struct repository *, struct diff_filespec *); | ||||||
|  | @ -182,4 +198,12 @@ int diffcore_count_changes(struct repository *r, | ||||||
| 			   unsigned long *src_copied, | 			   unsigned long *src_copied, | ||||||
| 			   unsigned long *literal_added); | 			   unsigned long *literal_added); | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * If filespec contains an OID and if that object is missing from the given | ||||||
|  |  * repository, add that OID to to_fetch. | ||||||
|  |  */ | ||||||
|  | void diff_add_if_missing(struct repository *r, | ||||||
|  | 			 struct oid_array *to_fetch, | ||||||
|  | 			 const struct diff_filespec *filespec); | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -519,7 +519,7 @@ static void fill_line_ends(struct repository *r, | ||||||
| 	unsigned long *ends = NULL; | 	unsigned long *ends = NULL; | ||||||
| 	char *data = NULL; | 	char *data = NULL; | ||||||
|  |  | ||||||
| 	if (diff_populate_filespec(r, spec, 0)) | 	if (diff_populate_filespec(r, spec, NULL)) | ||||||
| 		die("Cannot read blob %s", oid_to_hex(&spec->oid)); | 		die("Cannot read blob %s", oid_to_hex(&spec->oid)); | ||||||
|  |  | ||||||
| 	ALLOC_ARRAY(ends, size); | 	ALLOC_ARRAY(ends, size); | ||||||
|  | @ -1045,12 +1045,12 @@ static int process_diff_filepair(struct rev_info *rev, | ||||||
| 		return 0; | 		return 0; | ||||||
|  |  | ||||||
| 	assert(pair->two->oid_valid); | 	assert(pair->two->oid_valid); | ||||||
| 	diff_populate_filespec(rev->diffopt.repo, pair->two, 0); | 	diff_populate_filespec(rev->diffopt.repo, pair->two, NULL); | ||||||
| 	file_target.ptr = pair->two->data; | 	file_target.ptr = pair->two->data; | ||||||
| 	file_target.size = pair->two->size; | 	file_target.size = pair->two->size; | ||||||
|  |  | ||||||
| 	if (pair->one->oid_valid) { | 	if (pair->one->oid_valid) { | ||||||
| 		diff_populate_filespec(rev->diffopt.repo, pair->one, 0); | 		diff_populate_filespec(rev->diffopt.repo, pair->one, NULL); | ||||||
| 		file_parent.ptr = pair->one->data; | 		file_parent.ptr = pair->one->data; | ||||||
| 		file_parent.size = pair->one->size; | 		file_parent.size = pair->one->size; | ||||||
| 	} else { | 	} else { | ||||||
|  |  | ||||||
|  | @ -241,6 +241,9 @@ int promisor_remote_get_direct(struct repository *repo, | ||||||
| 	int to_free = 0; | 	int to_free = 0; | ||||||
| 	int res = -1; | 	int res = -1; | ||||||
|  |  | ||||||
|  | 	if (oid_nr == 0) | ||||||
|  | 		return 0; | ||||||
|  |  | ||||||
| 	promisor_remote_init(); | 	promisor_remote_init(); | ||||||
|  |  | ||||||
| 	for (r = promisors; r; r = r->next) { | 	for (r = promisors; r; r = r->next) { | ||||||
|  |  | ||||||
|  | @ -20,6 +20,14 @@ struct promisor_remote { | ||||||
| void promisor_remote_reinit(void); | void promisor_remote_reinit(void); | ||||||
| struct promisor_remote *promisor_remote_find(const char *remote_name); | struct promisor_remote *promisor_remote_find(const char *remote_name); | ||||||
| int has_promisor_remote(void); | int has_promisor_remote(void); | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Fetches all requested objects from all promisor remotes, trying them one at | ||||||
|  |  * a time until all objects are fetched. Returns 0 upon success, and non-zero | ||||||
|  |  * otherwise. | ||||||
|  |  * | ||||||
|  |  * If oid_nr is 0, this function returns 0 (success) immediately. | ||||||
|  |  */ | ||||||
| int promisor_remote_get_direct(struct repository *repo, | int promisor_remote_get_direct(struct repository *repo, | ||||||
| 			       const struct object_id *oids, | 			       const struct object_id *oids, | ||||||
| 			       int oid_nr); | 			       int oid_nr); | ||||||
|  |  | ||||||
|  | @ -131,4 +131,52 @@ test_expect_success 'diff with rename detection batches blobs' ' | ||||||
| 	test_line_count = 1 done_lines | 	test_line_count = 1 done_lines | ||||||
| ' | ' | ||||||
|  |  | ||||||
|  | test_expect_success 'diff does not fetch anything if inexact rename detection is not needed' ' | ||||||
|  | 	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 && | ||||||
|  | 	mv server/b 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 no fetches. | ||||||
|  | 	GIT_TRACE_PACKET="$(pwd)/trace" git -C client diff --raw -M HEAD^ HEAD && | ||||||
|  | 	! test_path_exists trace | ||||||
|  | ' | ||||||
|  |  | ||||||
|  | test_expect_success 'diff --break-rewrites fetches only if necessary, and batches blobs if it does' ' | ||||||
|  | 	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 && | ||||||
|  | 	printf "c\nc\nc\nc\nc\n" >server/b && | ||||||
|  | 	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 no fetches. | ||||||
|  | 	GIT_TRACE_PACKET="$(pwd)/trace" git -C client diff --raw -M HEAD^ HEAD && | ||||||
|  | 	! test_path_exists trace && | ||||||
|  |  | ||||||
|  | 	# But with --break-rewrites, 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 --break-rewrites --raw -M HEAD^ HEAD && | ||||||
|  | 	grep "git> done" trace >done_lines && | ||||||
|  | 	test_line_count = 1 done_lines | ||||||
|  | ' | ||||||
|  |  | ||||||
| test_done | test_done | ||||||
|  |  | ||||||
|  | @ -423,9 +423,8 @@ static int check_updates(struct unpack_trees_options *o) | ||||||
| 				continue; | 				continue; | ||||||
| 			oid_array_append(&to_fetch, &ce->oid); | 			oid_array_append(&to_fetch, &ce->oid); | ||||||
| 		} | 		} | ||||||
| 		if (to_fetch.nr) | 		promisor_remote_get_direct(the_repository, | ||||||
| 			promisor_remote_get_direct(the_repository, | 					   to_fetch.oid, to_fetch.nr); | ||||||
| 						   to_fetch.oid, to_fetch.nr); |  | ||||||
| 		oid_array_clear(&to_fetch); | 		oid_array_clear(&to_fetch); | ||||||
| 	} | 	} | ||||||
| 	for (i = 0; i < index->cache_nr; i++) { | 	for (i = 0; i < index->cache_nr; i++) { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Junio C Hamano
						Junio C Hamano