git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH] diff: batch fetching of missing blobs

From
Jeff King <peff@peff.net>
Date
Mar 28, 2019, 06:52 UTC
Message-ID
<20190328065252.GA1930@sigill.intra.peff.net>
In-Reply-To
<20190326220906.111879-1-jonathantanmy@google.com>
On Tue, Mar 26, 2019 at 03:09:06PM -0700, Jonathan Tan wrote:
Show 5 quoted lines
> 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.

Sounds like a good idea, and this should make some cases much better without making any cases worse. Two observations about how we might do even better, though:

> @@ -6067,6 +6068,32 @@ static void diff_flush_patch_all_file_pairs(struct diff_options *o)
> [...]

At this stage we're looking at actually diffing the contents themselves. But we'd also potentially need the blobs during rename and break detection. It's not always the same set of blobs (e.g., unless you've cranked up the copy-detection flags, renames only look at added/deleted entries). We could have each phase do its own bulk fetch, which worst-case gives us probably three fetches. But I wonder if we could figure out a complete plausible set immediately after the tree diff.

Show 19 quoted lines
> +	if (repository_format_partial_clone) {
> +		/*
> +		 * Prefetch the diff pairs that are about to be flushed.
> +		 */
> +		struct oid_array to_fetch = OID_ARRAY_INIT;
> +		int fetch_if_missing_store = fetch_if_missing;
> +
> +		fetch_if_missing = 0;
> +		for (i = 0; i < q->nr; i++) {
> +			struct diff_filepair *p = q->queue[i];
> +			if (!check_pair_status(p))
> +				continue;
> +			if (p->one && p->one->oid_valid &&
> +			    !has_object_file(&p->one->oid))
> +				oid_array_append(&to_fetch, &p->one->oid);
> +			if (p->two && p->two->oid_valid &&
> +			    !has_object_file(&p->two->oid))
> +				oid_array_append(&to_fetch, &p->two->oid);
> +		}

These has_object_file() calls may repeatedly re-scan the pack directory, once per call. Since it's likely that some may be missing, that may be a noticeable amount of wasted work for a big diff (still way less than individually fetching each missing object, so it's a net win, but read on).

If you use the QUICK flag, that avoids the re-scans, but we may miss erroneously say "we don't have it" if we race with a repack. For that, we can either:

  1. Just ignore it. It's relatively rare, and the worst case is that we
     re-fetch an object.
  2. Do a series of QUICK checks, followed by a single
     reprepare_packed_git() if we had any missing, and then another
     series of QUICK checks. Then worst-case we have a single re-scan.
Something like:
  int object_is_missing_cb(const struct object_id *oid, void *data)
  {
	return !has_object_file_with_flags(oid, OBJECT_INFO_QUICK);
  }
  ...
  /* collect all of the possible blobs we need */
  for (i = 0; i < q->nr; i++) {
	...
	oid_array_append(&to_fetch, &p->one->oid);
	oid_array_append(&to_fetch, &p->two->oid);
  }
  /* drop any we already have */
  oid_array_filter(&to_fetch, object_is_missing_cb, NULL);
  /* any missing ones might actually be a race; try again */
  if (to_fetch.nr) {
	  reprepare_packed_git(the_repository);
	  oid_array_filter(&to_fetch, object_is_missing_cb, NULL);
  }
  /* and now anything we have left is definitely not here */
  if (to_fetch.nr)
	fetch_objects(..., to_fetch.oid, to_fetch.nr).

One thing I noticed while writing this: we don't seem to do any de-duplication of the list (in yours or mine), and it doesn't look like fetch_objects() does either. I wonder if an oidset would be a better data structure.

-Peff
Previous: Johannes SchindelinNext: Jonathan Tan
Message 4 of 29 in “diff: batch fetching of missing blobs”
  1. diff: batch fetching of missing blobsJonathan Tan, Mar 26, 2019
  2. SZEDER GáborMar 27, 2019
  3. Johannes SchindelinMar 27, 2019
  4. Jeff KingMar 28, 2019
  5. 0/2 Batch fetching of missing blobs in diff and showJonathan Tan, Mar 29, 2019
  6. 1/2 sha1-file: support OBJECT_INFO_FOR_PREFETCHJonathan Tan, Mar 29, 2019
  7. Johannes SchindelinApr 5, 2019
  8. Jeff KingApr 5, 2019
  9. 2/2 diff: batch fetching of missing blobsJonathan Tan, Mar 29, 2019
  10. SZEDER GáborApr 4, 2019
  11. Johannes SchindelinApr 5, 2019
  12. Christian CouderApr 7, 2019
  13. Junio C HamanoApr 8, 2019
  14. Junio C HamanoApr 8, 2019
  15. Junio C HamanoApr 8, 2019
  16. Christian CouderApr 8, 2019
  17. Christian CouderApr 8, 2019
  18. Junio C HamanoApr 8, 2019
  19. Christian CouderApr 8, 2019
  20. Duy NguyenApr 5, 2019
  21. fixup! diff: batch fetching of missing blobsJonathan Tan, Apr 5, 2019
  22. Johannes SchindelinApr 5, 2019
  23. Duy NguyenApr 6, 2019
  24. Junio C HamanoApr 8, 2019
  25. Junio C HamanoApr 8, 2019
  26. Duy NguyenApr 8, 2019
  27. Junio C HamanoApr 9, 2019
  28. Johannes SchindelinApr 5, 2019
  29. Jeff KingApr 5, 2019

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.