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

Re: [PATCH 2/2] fetch-pack.c: do not declare local commits as "have" in partial repos

From
Junio C Hamano <gitster@pobox.com>
Date
Sep 22, 2024, 06:53 UTC
Message-ID
<xmqqr09c89id.fsf@gitster.g>
In-Reply-To
<20240919234741.1317946-3-calvinwan@google.com>
Calvin Wan <calvinwan@google.com> writes:
Show 14 quoted lines
> In a partial repository, creating a local commit and then fetching
> causes the following state to occur:
>
> commit  tree  blob
>  C3 ---- T3 -- B3 (fetched from remote, in promisor pack)
>  |
>  C2 ---- T2 -- B2 (created locally, in non-promisor pack)
>  |
>  C1 ---- T1 -- B1 (fetched from remote, in promisor pack)
>
> During garbage collection, parents of promisor objects are marked as
> UNINTERESTING and are subsequently garbage collected. In this case, C2
> would be deleted and attempts to access that commit would result in "bad
> object" errors (originally reported here[1]).
Understandable.
> This is not a bug in gc since it should be the case that parents of
> promisor objects are also promisor objects (fsck assumes this as
> well).

I am not sure where this "not a bug" claim comes from. Here, the definition of "promisor objects" seems to be anything that are reachable from objects in promisor packs, but isn't the source of the bug that collects C2 exactly that "gc" uses such a definition for discardable objects that can be refetchd from promisor remotes?

> When promisor objects are fetched, the state of the repository
> should ensure that the above holds true. Therefore, do not declare local
> commits as "have" in partial repositores so they can be fetched into a
> promisor pack.

Could you clarify what it means in the context of the above example you gave in an updated version of the proposed log message?

We pretend that C2 and anything it reaches do not exist locally, to force them to be fetched from the remote? We'd end up having two copies of C2 (one that we created locally and had before starting this fetch, the other we fetched when we fetched C3 from them)? This sounds like it is awfully inefficient both network bandwidth- and local disk-wise.

I was hoping to see that the issue can be fixed on the "gc" side, regardless of how the objects enter our repository, but perhaps I am missing something. Isn't it just the matter of collecting C1, C3 but not C2? Or to put it another way, if we first create a list of objects to be packed (regardless of whether they are in promisor packs), and then remove the objects that are in promisor packs from the list, and pack the objects still remaining in the list?

Show 86 quoted lines
> diff --git a/fetch-pack.c b/fetch-pack.c
> index 58b4581ad8..c39b0f6ad4 100644
> --- a/fetch-pack.c
> +++ b/fetch-pack.c
> @@ -1297,12 +1297,23 @@ static void add_common(struct strbuf *req_buf, struct oidset *common)
>  
>  static int add_haves(struct fetch_negotiator *negotiator,
>  		     struct strbuf *req_buf,
> -		     int *haves_to_send)
> +		     int *haves_to_send,
> +		     int from_promisor)
>  {
>  	int haves_added = 0;
>  	const struct object_id *oid;
>  
>  	while ((oid = negotiator->next(negotiator))) {
> +		/* 
> +		 * In partial repos, do not declare local objects as "have"
> +		 * so that they can be fetched into a promisor pack. Certain
> +		 * operations mark parent commits of promisor objects as
> +		 * UNINTERESTING and are subsequently garbage collected so
> +		 * this ensures local commits are still available in promisor
> +		 * packs after a fetch + gc.
> +		 */
> +		if (from_promisor && !is_in_promisor_pack(oid, 0))
> +			continue;
>  		packet_buf_write(req_buf, "have %s\n", oid_to_hex(oid));
>  		if (++haves_added >= *haves_to_send)
>  			break;
> @@ -1405,7 +1416,7 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
>  	/* Add all of the common commits we've found in previous rounds */
>  	add_common(&req_buf, common);
>  
> -	haves_added = add_haves(negotiator, &req_buf, haves_to_send);
> +	haves_added = add_haves(negotiator, &req_buf, haves_to_send, args->from_promisor);
>  	*in_vain += haves_added;
>  	trace2_data_intmax("negotiation_v2", the_repository, "haves_added", haves_added);
>  	trace2_data_intmax("negotiation_v2", the_repository, "in_vain", *in_vain);
> @@ -2178,7 +2189,7 @@ void negotiate_using_fetch(const struct oid_array *negotiation_tips,
>  
>  		packet_buf_write(&req_buf, "wait-for-done");
>  
> -		haves_added = add_haves(&negotiator, &req_buf, &haves_to_send);
> +		haves_added = add_haves(&negotiator, &req_buf, &haves_to_send, 0);
>  		in_vain += haves_added;
>  		if (!haves_added || (seen_ack && in_vain >= MAX_IN_VAIN))
>  			last_iteration = 1;
> diff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh
> index 8415884754..cba9f7ed9b 100755
> --- a/t/t5616-partial-clone.sh
> +++ b/t/t5616-partial-clone.sh
> @@ -693,6 +693,35 @@ test_expect_success 'lazy-fetch in submodule succeeds' '
>  	git -C client restore --recurse-submodules --source=HEAD^ :/
>  '
>  
> +test_expect_success 'fetching from promisor remote fetches previously local commits' '
> +	# Setup
> +	git init full &&
> +	git -C full config uploadpack.allowfilter 1 &&
> + 	git -C full config uploadpack.allowanysha1inwant 1 &&
> +	touch full/foo &&
> +	git -C full add foo &&
> +	git -C full commit -m "commit 1" &&
> +	git -C full checkout --detach &&
> +
> +	# Partial clone and push commit to remote
> +	git clone "file://$(pwd)/full" --filter=blob:none partial &&
> +	echo "hello" > partial/foo &&
> +	git -C partial commit -a -m "commit 2" &&
> +	git -C partial push &&
> +
> +	# gc in partial repo
> +	git -C partial gc --prune=now &&
> +
> +	# Create another commit in normal repo
> +	git -C full checkout main &&
> +	echo " world" >> full/foo &&
> +	git -C full commit -a -m "commit 3" &&
> +
> +	# Pull from remote in partial repo, and run gc again
> +	git -C partial pull &&
> +	git -C partial gc --prune=now
> +'
> +
>  . "$TEST_DIRECTORY"/lib-httpd.sh
>  start_httpd
Previous: Calvin WanNext: Junio C Hamano
Message 20 of 65 in “revision: fix reachable objects being gc'ed in no blob clone repo”
  1. 0/1 revision: fix reachable objects being gc'ed in no blob clone repoHan Young, Aug 2, 2024
  2. 1/1 revision: don't set parents as uninteresting if exclude promisor objectsHan Young, Aug 2, 2024
  3. Junio C HamanoAug 2, 2024
  4. 韩仰Aug 12, 2024
  5. Junio C HamanoAug 12, 2024
  6. 韩仰Aug 22, 2024
  7. Jonathan TanAug 13, 2024
  8. Jonathan TanAug 13, 2024
  9. Junio C HamanoAug 14, 2024
  10. Jonathan TanAug 14, 2024
  11. 0/4 revision: fix reachable objects being gc'ed in no blob clone repoHan Young, Aug 23, 2024
  12. 1/4 packfile: split promisor objects oidset into twoHan Young, Aug 23, 2024
  13. 2/4 revision: add exclude-promisor-pack-objects optionHan Young, Aug 23, 2024
  14. 3/4 revision: don't mark commit as UNINTERESTING if --exclude-promisor-objects is setHan Young, Aug 23, 2024
  15. 4/4 repack: use new exclude promisor pack objects optionHan Young, Aug 23, 2024
  16. 0/2 revision: fix reachable commits being gc'ed in partial repoCalvin Wan, Sep 19, 2024
  17. 1/2 packfile: split promisor objects oidset into twoCalvin Wan, Sep 19, 2024
  18. Junio C HamanoSep 22, 2024
  19. 2/2 fetch-pack.c: do not declare local commits as "have" in partial reposCalvin Wan, Sep 19, 2024
  20. Junio C HamanoSep 22, 2024
  21. Junio C HamanoSep 22, 2024
  22. 韩仰Sep 23, 2024
  23. Junio C HamanoSep 23, 2024
  24. Calvin WanOct 2, 2024
  25. 0/2 repack: pack everything into promisor packfile in partial reposHan Young, Sep 25, 2024
  26. 1/2 repack: pack everything into packfileHan Young, Sep 25, 2024
  27. 2/2 t0410: adapt tests to repack changesHan Young, Sep 25, 2024
  28. Phillip WoodSep 25, 2024
  29. Junio C HamanoSep 25, 2024
  30. Junio C HamanoSep 25, 2024
  31. Missing Promisor Objects in Partial Repo Design DocCalvin Wan, Oct 1, 2024
  32. Junio C HamanoOct 1, 2024
  33. Junio C HamanoOct 2, 2024
  34. Han YoungOct 2, 2024
  35. Calvin WanOct 8, 2024
  36. Han YoungOct 9, 2024
  37. Jonathan TanOct 9, 2024
  38. Jonathan TanOct 12, 2024
  39. Han YoungOct 12, 2024
  40. Jonathan TanOct 14, 2024
  41. Jonathan TanOct 9, 2024
  42. 0/3 repack: pack everything into promisor packfile in partial reposHan Young, Oct 8, 2024
  43. 1/3 repack: pack everything into packfileHan Young, Oct 8, 2024
  44. Calvin WanOct 8, 2024
  45. 2/3 t0410: adapt tests to repack changesHan Young, Oct 8, 2024
  46. 3/3 partial-clone: update docHan Young, Oct 8, 2024
  47. Junio C HamanoOct 8, 2024
  48. Junio C HamanoOct 8, 2024
  49. Han YoungOct 9, 2024
  50. 0/3 repack: pack everything into promisor packfile in partial reposHan Young, Oct 11, 2024
  51. 1/3 repack: pack everything into packfileHan Young, Oct 11, 2024
  52. 2/3 repack: adapt tests to repack changesHan Young, Oct 11, 2024
  53. 3/3 partial-clone: update docHan Young, Oct 11, 2024
  54. Junio C HamanoOct 11, 2024
  55. Junio C HamanoOct 11, 2024
  56. 0/3 repack: pack everything into promisor packfile in partial reposHan Young, Oct 14, 2024
  57. 1/3 repack: pack everything into packfileHan Young, Oct 14, 2024
  58. 2/3 t0410: adapt tests to repack changesHan Young, Oct 14, 2024
  59. 3/3 partial-clone: update docHan Young, Oct 14, 2024
  60. 0/3 Repack on fetchJonathan Tan, Oct 21, 2024
  61. 1/3 move variableJonathan Tan, Oct 21, 2024
  62. 2/3 pack-objectsJonathan Tan, Oct 21, 2024
  63. 3/3 record local links and call pack-objectsJonathan Tan, Oct 21, 2024
  64. Han YoungOct 23, 2024
  65. Jonathan TanOct 23, 2024

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.