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

[PATCH] bundle-uri: refresh packed_git if unbundle succeed

From
BGblanet via GitGitGadget <gitgitgadget@gmail.com>
Date
May 15, 2024, 03:01 UTC
Message-ID
<pull.1730.git.1715742069966.gitgitgadget@gmail.com>
From: Xing Xin <xingxin.xx@bytedance.com>

When using the bundle-uri mechanism with a bundle list containing multiple interrelated bundles, we encountered a bug where tips from downloaded bundles were not being discovered, resulting in rather slow clones. This was particularly problematic when employing the heuristic `creationTokens`.

And this is easy to reproduce. Suppose we have a repository with a single branch `main` pointing to commit `A`, firstly we create a base bundle with

  git bundle create base.bundle main

Then let's add a new commit `B` on top of `A`, so that an incremental bundle for `main` can be created with

  git bundle create incr.bundle A..main
Now we can generate a bundle list with the following content:
  [bundle]
      version = 1
      mode = all
      heuristic = creationToken
  [bundle "base"]
      uri = base.bundle
      creationToken = 1
  [bundle "incr"]
      uri = incr.bundle
      creationToken = 2

A fresh clone with the bundle list above would give the expected `refs/bundles/main` pointing at `B` in new repository, in other words we already had everything locally from the bundles, but git would still download everything from server as if we got nothing.

So why the `refs/bundles/main` is not discovered? After some digging I found that:

1. when unbundling a downloaded bundle, a `verify_bundle` is called to
   check its prerequisites if any. The verify procedure would find oids
   so `packed_git` is initialized.
2. after unbundled all bundles, we would enter `do_fetch_pack_v2`,
   during which `mark_complete_and_common_ref` and `mark_tips` would
   find oids with `OBJECT_INFO_QUICK` flag set, so no new packs would be
   enlisted if `packed_git` has already initialized in 1.

Back to the example above, when unbunding `incr.bundle`, `base.pack` is enlisted to `packed_git` bacause of the prerequisites to verify. Then we can not find `B` for negotiation at a latter time bacause `B` exists in `incr.pack` which is not enlisted in `packed_git`.

This commit fixes this by adding a `reprepare_packed_git` call for every successfully unbundled bundle, which ensures to enlist all generated packs from bundle uri. And a set of negotiation related tests are added.

Signed-off-by: Xing Xin <xingxin.xx@bytedance.com>
---
    bundle-uri: refresh packed_git if unbundle succeed
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1730%2Fblanet%2Fxx%2Fbundle-uri-bug-using-bundle-list-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1730/blanet/xx/bundle-uri-bug-using-bundle-list-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/1730
 bundle-uri.c                |   3 +
 t/t5558-clone-bundle-uri.sh | 129 ++++++++++++++++++++++++++++++++++++
 2 files changed, 132 insertions(+)
diff --git a/bundle-uri.c b/bundle-uri.c
index ca32050a78f..2b9d36cfd8e 100644
--- a/bundle-uri.c
+++ b/bundle-uri.c
@@ -7,6 +7,7 @@
 #include "refs.h"
 #include "run-command.h"
 #include "hashmap.h"
+#include "packfile.h"
 #include "pkt-line.h"
 #include "config.h"
 #include "remote.h"
@@ -376,6 +377,8 @@ static int unbundle_from_file(struct repository *r, const char *file)
 			       VERIFY_BUNDLE_QUIET)))
 		return 1;
 
+	reprepare_packed_git(r);
+
 	/*
 	 * Convert all refs/heads/ from the bundle into refs/bundles/
 	 * in the local repository.
diff --git a/t/t5558-clone-bundle-uri.sh b/t/t5558-clone-bundle-uri.sh
index 1ca5f745e73..a5b04d6f187 100755
--- a/t/t5558-clone-bundle-uri.sh
+++ b/t/t5558-clone-bundle-uri.sh
@@ -20,7 +20,10 @@ test_expect_success 'fail to clone from non-bundle file' '
 test_expect_success 'create bundle' '
 	git init clone-from &&
 	git -C clone-from checkout -b topic &&
+
 	test_commit -C clone-from A &&
+	git -C clone-from bundle create A.bundle topic &&
+
 	test_commit -C clone-from B &&
 	git -C clone-from bundle create B.bundle topic
 '
@@ -259,6 +262,132 @@ test_expect_success 'clone bundle list (file, any mode, all failures)' '
 	! grep "refs/bundles/" refs
 '
 
+#########################################################################
+# Clone negotiation related tests begin here
+
+test_expect_success 'negotiation: bundle with part of wanted commits' '
+	test_when_finished rm -rf trace*.txt &&
+	GIT_TRACE_PACKET="$(pwd)/trace-packet.txt" \
+	git clone --no-local --bundle-uri="clone-from/A.bundle" \
+		clone-from nego-bundle-part &&
+	git -C nego-bundle-part for-each-ref --format="%(refname)" >refs &&
+	grep "refs/bundles/" refs >actual &&
+	cat >expect <<-\EOF &&
+	refs/bundles/topic
+	EOF
+	test_cmp expect actual &&
+	# Ensure that refs/bundles/topic are sent as "have".
+	grep "clone> have $(git -C clone-from rev-parse A)" trace-packet.txt
+'
+
+test_expect_success 'negotiation: bundle with all wanted commits' '
+	test_when_finished rm -rf trace*.txt &&
+	GIT_TRACE_PACKET="$(pwd)/trace-packet.txt" \
+	git clone --no-local --single-branch --branch=topic --no-tags \
+		--bundle-uri="clone-from/B.bundle" \
+		clone-from nego-bundle-all &&
+	git -C nego-bundle-all for-each-ref --format="%(refname)" >refs &&
+	grep "refs/bundles/" refs >actual &&
+	cat >expect <<-\EOF &&
+	refs/bundles/topic
+	EOF
+	test_cmp expect actual &&
+	# We already have all needed commits so no "want" needed.
+	! grep "clone> want " trace-packet.txt
+'
+
+test_expect_success 'negotiation: bundle list (no heuristic)' '
+	test_when_finished rm -f trace*.txt &&
+	cat >bundle-list <<-EOF &&
+	[bundle]
+		version = 1
+		mode = all
+
+	[bundle "bundle-1"]
+		uri = file://$(pwd)/clone-from/bundle-1.bundle
+
+	[bundle "bundle-2"]
+		uri = file://$(pwd)/clone-from/bundle-2.bundle
+	EOF
+
+	GIT_TRACE_PACKET="$(pwd)/trace-packet.txt" \
+	git clone --no-local --bundle-uri="file://$(pwd)/bundle-list" \
+		clone-from nego-bundle-list-no-heuristic &&
+
+	git -C nego-bundle-list-no-heuristic for-each-ref --format="%(refname)" >refs &&
+	grep "refs/bundles/" refs >actual &&
+	cat >expect <<-\EOF &&
+	refs/bundles/base
+	refs/bundles/left
+	EOF
+	test_cmp expect actual &&
+	grep "clone> have $(git -C nego-bundle-list-no-heuristic rev-parse refs/bundles/left)" trace-packet.txt
+'
+
+test_expect_success 'negotiation: bundle list (creationToken)' '
+	test_when_finished rm -f trace*.txt &&
+	cat >bundle-list <<-EOF &&
+	[bundle]
+		version = 1
+		mode = all
+		heuristic = creationToken
+
+	[bundle "bundle-1"]
+		uri = file://$(pwd)/clone-from/bundle-1.bundle
+		creationToken = 1
+
+	[bundle "bundle-2"]
+		uri = file://$(pwd)/clone-from/bundle-2.bundle
+		creationToken = 2
+	EOF
+
+	GIT_TRACE_PACKET="$(pwd)/trace-packet.txt" \
+	git clone --no-local --bundle-uri="file://$(pwd)/bundle-list" \
+		clone-from nego-bundle-list-heuristic &&
+
+	git -C nego-bundle-list-heuristic for-each-ref --format="%(refname)" >refs &&
+	grep "refs/bundles/" refs >actual &&
+	cat >expect <<-\EOF &&
+	refs/bundles/base
+	refs/bundles/left
+	EOF
+	test_cmp expect actual &&
+	grep "clone> have $(git -C nego-bundle-list-heuristic rev-parse refs/bundles/left)" trace-packet.txt
+'
+
+test_expect_success 'negotiation: bundle list with all wanted commits' '
+	test_when_finished rm -f trace*.txt &&
+	cat >bundle-list <<-EOF &&
+	[bundle]
+		version = 1
+		mode = all
+		heuristic = creationToken
+
+	[bundle "bundle-1"]
+		uri = file://$(pwd)/clone-from/bundle-1.bundle
+		creationToken = 1
+
+	[bundle "bundle-2"]
+		uri = file://$(pwd)/clone-from/bundle-2.bundle
+		creationToken = 2
+	EOF
+
+	GIT_TRACE_PACKET="$(pwd)/trace-packet.txt" \
+	git clone --no-local --single-branch --branch=left --no-tags \
+		--bundle-uri="file://$(pwd)/bundle-list" \
+		clone-from nego-bundle-list-all &&
+
+	git -C nego-bundle-list-all for-each-ref --format="%(refname)" >refs &&
+	grep "refs/bundles/" refs >actual &&
+	cat >expect <<-\EOF &&
+	refs/bundles/base
+	refs/bundles/left
+	EOF
+	test_cmp expect actual &&
+	# We already have all needed commits so no "want" needed.
+	! grep "clone> want " trace-packet.txt
+'
+
 #########################################################################
 # HTTP tests begin here
 

base-commit: 83f1add914c6b4682de1e944ec0d1ac043d53d78
-- 
gitgitgadget
Next: Patrick Steinhardt
Message 1 of 66 in “bundle-uri: refresh packed_git if unbundle succeed”
  1. bundle-uri: refresh packed_git if unbundle succeedblanet via GitGitGadget, May 15, 2024
  2. Patrick SteinhardtMay 17, 2024
  3. Junio C HamanoMay 17, 2024
  4. Xing XinMay 20, 2024
  5. Junio C HamanoMay 20, 2024
  6. Xing XinMay 27, 2024
  7. Xing XinMay 20, 2024
  8. Karthik NayakMay 17, 2024
  9. Xing XinMay 20, 2024
  10. bundle-uri: verify oid before writing refsblanet via GitGitGadget, May 20, 2024
  11. Karthik NayakMay 21, 2024
  12. 0/4 object checking related additions and fixes for bundles in fetchesblanet via GitGitGadget, May 27, 2024
  13. 1/4 bundle-uri: verify oid before writing refsXing Xin via GitGitGadget, May 27, 2024
  14. Patrick SteinhardtMay 28, 2024
  15. Xing XinMay 30, 2024
  16. 2/4 unbundle: introduce unbundle_fsck_flags for fsckobjects handlingXing Xin via GitGitGadget, May 27, 2024
  17. Patrick SteinhardtMay 28, 2024
  18. Xing XinMay 29, 2024
  19. Patrick SteinhardtMay 30, 2024
  20. Xing XinMay 30, 2024
  21. 3/4 fetch-pack: expose fsckObjects configuration logicXing Xin via GitGitGadget, May 27, 2024
  22. Patrick SteinhardtMay 28, 2024
  23. Junio C HamanoMay 28, 2024
  24. Junio C HamanoMay 28, 2024
  25. Patrick SteinhardtMay 29, 2024
  26. Xing XinMay 30, 2024
  27. Patrick SteinhardtMay 29, 2024
  28. 4/4 unbundle: introduce new option UNBUNDLE_FSCK_FOLLOW_FETCHXing Xin via GitGitGadget, May 27, 2024
  29. Patrick SteinhardtMay 28, 2024
  30. Xing XinMay 30, 2024
  31. 0/4 object checking related additions and fixes for bundles in fetchesblanet via GitGitGadget, May 30, 2024
  32. 1/4 bundle-uri: verify oid before writing refsXing Xin via GitGitGadget, May 30, 2024
  33. 2/4 unbundle: extend verify_bundle_flags to support fsck-objectsXing Xin via GitGitGadget, May 30, 2024
  34. Patrick SteinhardtJun 6, 2024
  35. Xing XinJun 11, 2024
  36. 3/4 fetch-pack: expose fsckObjects configuration logicXing Xin via GitGitGadget, May 30, 2024
  37. 4/4 unbundle: introduce option VERIFY_BUNDLE_FSCK_FOLLOW_FETCHXing Xin via GitGitGadget, May 30, 2024
  38. Patrick SteinhardtJun 6, 2024
  39. Xing XinJun 11, 2024
  40. 0/4 object checking related additions and fixes for bundles in fetchesblanet via GitGitGadget, Jun 11, 2024
  41. 2/4 fetch-pack: expose fsckObjects configuration logicXing Xin via GitGitGadget, Jun 11, 2024
  42. 1/4 bundle-uri: verify oid before writing refsXing Xin via GitGitGadget, Jun 11, 2024
  43. 3/4 unbundle: extend options to support object verificationXing Xin via GitGitGadget, Jun 11, 2024
  44. Patrick SteinhardtJun 11, 2024
  45. Xing XinJun 11, 2024
  46. 4/4 unbundle: use VERIFY_BUNDLE_FSCK_FOLLOW_FETCH for fetchesXing Xin via GitGitGadget, Jun 11, 2024
  47. 0/3 object checking related additions and fixes for bundles in fetchesblanet via GitGitGadget, Jun 11, 2024
  48. 1/3 bundle-uri: verify oid before writing refsXing Xin via GitGitGadget, Jun 11, 2024
  49. Junio C HamanoJun 11, 2024
  50. Xing XinJun 17, 2024
  51. 2/3 fetch-pack: expose fsckObjects configuration logicXing Xin via GitGitGadget, Jun 11, 2024
  52. Junio C HamanoJun 11, 2024
  53. 3/3 unbundle: support object verification for fetchesXing Xin via GitGitGadget, Jun 11, 2024
  54. Junio C HamanoJun 11, 2024
  55. Xing XinJun 12, 2024
  56. Patrick SteinhardtJun 11, 2024
  57. 0/3 object checking related additions and fixes for bundles in fetchesblanet via GitGitGadget, Jun 17, 2024
  58. 1/3 bundle-uri: verify oid before writing refsXing Xin via GitGitGadget, Jun 17, 2024
  59. Junio C HamanoJun 18, 2024
  60. Xing XinJun 19, 2024
  61. 2/3 fetch-pack: expose fsckObjects configuration logicXing Xin via GitGitGadget, Jun 17, 2024
  62. 3/3 unbundle: extend object verification for fetchesXing Xin via GitGitGadget, Jun 17, 2024
  63. 0/3 object checking related additions and fixes for bundles in fetchesblanet via GitGitGadget, Jun 19, 2024
  64. 2/3 fetch-pack: expose fsckObjects configuration logicXing Xin via GitGitGadget, Jun 19, 2024
  65. 1/3 bundle-uri: verify oid before writing refsXing Xin via GitGitGadget, Jun 19, 2024
  66. 3/3 unbundle: extend object verification for fetchesXing Xin via GitGitGadget, Jun 19, 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.