Git Mailing List Archive mirror
 help / color / mirror / Atom feed
* [PATCH] bundle-uri: copy all bundle references ino the refs/bundle space
@ 2025-02-25 13:19 Scott Chacon via GitGitGadget
  2025-02-25 18:14 ` Junio C Hamano
  2025-03-01 10:33 ` [PATCH v2 0/3] " Scott Chacon via GitGitGadget
  0 siblings, 2 replies; 19+ messages in thread
From: Scott Chacon via GitGitGadget @ 2025-02-25 13:19 UTC (permalink / raw)
  To: git; +Cc: Scott Chacon, Scott Chacon

From: Scott Chacon <schacon@gmail.com>

When downloading bundles via the bundle-uri functionality, we only copy the
references from refs/heads into the refs/bundle space. I'm not sure why this
refspec is hardcoded to be so limited, but it makes the ref negotiation on
the subsequent fetch suboptimal, since it won't use objects that are
referenced outside of the current heads of the bundled repository.

This change to copy everything in refs/ in the bundle to refs/bundles/
significantly helps the subsequent fetch, since nearly all the references
are now included in the negotiation.

Signed-off-by: Scott Chacon <schacon@gmail.com>
---
    bundle-uri: copy all bundle references ino the refs/bundle space
    
    This patch probably isn't meant for inclusion, but I wanted to see if
    I'm crazy here or missing something.
    
    It appears that the bundle-uri functionality has an issue with ref
    negotiation. I hit this because I assumed all the objects I bundled
    would be seen in the negotiation, but since only references under
    refs/heads are copied to refs/bundles, they are the only ones that are
    seen for negotiation, so it's quite inefficient.
    
    I did several experiments trying to create a bundle where the subsequent
    fetch was almost a no-op and it was frustratingly impossible and it took
    me a while to figure out why it kept trying to get tons of other
    objects.
    
    Furthermore, when I bundled just a tag (thinking it would have most
    reachable objects) it completely failed to work because there were no
    refs/heads/ available for negotiation - so it downloaded a huge file and
    then still started from scratch on the fetch.
    
    However, if I copy all the refs in the bundle, it makes a big
    difference.
    
    Here are some benchmarks from the gitlab oss repo.
    
    A normal clone pulls down 3,005,985 objects:
    
    ❯  time git clone https://gitlab.com/gitlab-org/gitlab-foss.git gl5
    Cloning into 'gl5'...
    remote: Enumerating objects: 3005985, done.
    remote: Counting objects: 100% (314617/314617), done.
    remote: Compressing objects: 100% (64278/64278), done.
    remote: Total 3005985 (delta 244429), reused 311002 (delta 241404), pack-reused 2691368 (from 1)
    Receiving objects: 100% (3005985/3005985), 1.35 GiB | 23.91 MiB/s, done.
    Resolving deltas: 100% (2361484/2361484), done.
    Updating files: 100% (59972/59972), done.
    (*) 162.93s user 37.94s system 128% cpu 2:36.49 total
    
    
    Then, I tried to bundle everything from a fresh clone, including all the
    refs.
    
     ❯  git bundle create gitlab-base.bundle --all
    
    
    This creates a 1.4G bundle, which I uploaded to a CDN and cloned again
    with the bundle-uri:
    
    ❯  time git clone --bundle-uri=https://[cdn]/bundle/gitlab-base.bundle https://gitlab.com/gitlab-org/gitlab-foss.git gl4
    Cloning into 'gl4'...
    remote: Enumerating objects: 1092703, done.
    remote: Counting objects: 100% (973405/973405), done.
    remote: Compressing objects: 100% (385827/385827), done.
    remote: Total 959773 (delta 710976), reused 766809 (delta 554276), pack-reused 0 (from 0)
    Receiving objects: 100% (959773/959773), 366.94 MiB | 20.87 MiB/s, done.
    Resolving deltas: 100% (710976/710976), completed with 9081 local objects.
    Checking objects: 100% (4194304/4194304), done.
    Checking connectivity: 959668, done.
    Updating files: 100% (59972/59972), done.
    (*) 181.98s user 40.23s system 110% cpu 3:20.89 total
    
    
    Which is better from an "objects from the server" perspective, but still
    has to download 959,773 objects, so 32% of the total. But it also takes
    quite a lot longer, because it's redownloading most of those objects for
    a second time.
    
    If I apply this patch where I change the refspec for the bundle ref copy
    from refs/heads/ to just refs/ and clone with this patched version, it's
    much better:
    
    ❯  time ./git clone --bundle-uri=https://[cdn]/bundle/gitlab-base.bundle https://gitlab.com/gitlab-org/gitlab-foss.git gl3
    Cloning into 'gl3'...
    remote: Enumerating objects: 65538, done.
    remote: Counting objects: 100% (56054/56054), done.
    remote: Compressing objects: 100% (28950/28950), done.
    remote: Total 43877 (delta 27401), reused 25170 (delta 13546), pack-reused 0 (from 0)
    Receiving objects: 100% (43877/43877), 40.42 MiB | 22.27 MiB/s, done.
    Resolving deltas: 100% (27401/27401), completed with 8564 local objects.
    Updating files: 100% (59972/59972), done.
    (*) 143.45s user 29.33s system 124% cpu 2:19.27 total
    
    
    Now I'm only getting an extra 43k objects, so 1% of the original total,
    and the entire operation is a bit faster as well.
    
    I'm not sure if there is a downside here, it seems clearly how you would
    want the negotiation to go. It ends up with way more refs under
    refs/bundle (now there is refs/bundle/origin/master, etc) but that's
    being polluted by the head refs anyhow, right?
    
    Is this a reasonable change?

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1897%2Fschacon%2Fsc-more-bundle-refs-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1897/schacon/sc-more-bundle-refs-v1
Pull-Request: https://github.com/git/git/pull/1897

 bundle-uri.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/bundle-uri.c b/bundle-uri.c
index 744257c49c1..3371d56f4ce 100644
--- a/bundle-uri.c
+++ b/bundle-uri.c
@@ -403,7 +403,7 @@ static int unbundle_from_file(struct repository *r, const char *file)
 		const char *branch_name;
 		int has_old;
 
-		if (!skip_prefix(refname->string, "refs/heads/", &branch_name))
+		if (!skip_prefix(refname->string, "refs/", &branch_name))
 			continue;
 
 		strbuf_setlen(&bundle_ref, bundle_prefix_len);

base-commit: 2d2a71ce85026edcc40f469678a1035df0dfcf57
-- 
gitgitgadget

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH] bundle-uri: copy all bundle references ino the refs/bundle space
  2025-02-25 13:19 [PATCH] bundle-uri: copy all bundle references ino the refs/bundle space Scott Chacon via GitGitGadget
@ 2025-02-25 18:14 ` Junio C Hamano
  2025-02-25 23:36   ` Derrick Stolee
  2025-03-01 10:33 ` [PATCH v2 0/3] " Scott Chacon via GitGitGadget
  1 sibling, 1 reply; 19+ messages in thread
From: Junio C Hamano @ 2025-02-25 18:14 UTC (permalink / raw)
  To: Scott Chacon via GitGitGadget, Derrick Stolee; +Cc: git, Scott Chacon

"Scott Chacon via GitGitGadget" <gitgitgadget@gmail.com> writes:

>     Furthermore, when I bundled just a tag (thinking it would have most
>     reachable objects) it completely failed to work because there were no
>     refs/heads/ available for negotiation - so it downloaded a huge file and
>     then still started from scratch on the fetch.

Nice finding.

>     Now I'm only getting an extra 43k objects, so 1% of the original total,
>     and the entire operation is a bit faster as well.

That makes all sense.

>     I'm not sure if there is a downside here, it seems clearly how you would
>     want the negotiation to go. It ends up with way more refs under
>     refs/bundle (now there is refs/bundle/origin/master, etc) but that's
>     being polluted by the head refs anyhow, right?

I am not sure what you mean by "being polluted by the head refs
anyhow", but we should be equipped to deal with a repository with
tons of local branches, so having the comparable number of
remote-tracking branches instead of local branches now exposed in
refs/bundle/remotes/* hierarchy should work equally as well, or we
would have something we need to fix.  So in principle I do not see
a problem with this approach.

The mapping used to be "refs/heads/foo" to "refs/bundles/foo", but
now "refs/heads/foo" is mapped to "refs/bundles/heads/foo", so that
you would presumably be mapping "refs/remotes/origin/master" to
"refs/bundles/remotes/origin/master", right?  I hope existing users
are *not* looking at their resulting refs/bundles/ hierarchy and
somehow assuming the original mapping.

This is not something this "fix" changes, but unbundle_all_bundles()
apparently is prepared to handle more than one bundles.  I wonder
what happens when multiple bundles expose the same branch pointing
at different objects?  The way I read unbundle_from_file() is that
a new one overwrites the previous value, so even though we may have
all unbundled many bundles, the objects from earlier bundles may
lose their anchors, subject to be garbage-collected.

Imagine creating a bundle with two refs, refs/heads and
refs/remotes, and append that bundle as the last bundle of a bunch
of bundles full of local and remote-tracking branches, that have
populated refs/bundles/ hierarchy with tons of refs.  Now the last
bundle is unbundled, and these two phoney refs would nuke everything
that used to be under refs/bundles/heads/* and refs/bundles/remotes/*
left by unpacking previous bundles, right?

>     Is this a reasonable change?

This is mostly Stolee's design, IIRC, so I have CC'ed; the work is
mostly from 53a50892 (bundle-uri: create basic file-copy logic,
2022-08-09) that is more than 2 years ago.

> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1897%2Fschacon%2Fsc-more-bundle-refs-v1
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1897/schacon/sc-more-bundle-refs-v1
> Pull-Request: https://github.com/git/git/pull/1897
>
>  bundle-uri.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/bundle-uri.c b/bundle-uri.c
> index 744257c49c1..3371d56f4ce 100644
> --- a/bundle-uri.c
> +++ b/bundle-uri.c
> @@ -403,7 +403,7 @@ static int unbundle_from_file(struct repository *r, const char *file)
>  		const char *branch_name;
>  		int has_old;
>  
> -		if (!skip_prefix(refname->string, "refs/heads/", &branch_name))
> +		if (!skip_prefix(refname->string, "refs/", &branch_name))
>  			continue;

By skipping only "refs/", "branch_name" is no longer a branch name,
which may be something we may want to fix, but if we were to address
the "names from later bundles seem to overwrite names used by
previous boundles, and unanchor objects obtained from them" problem,
I suspect we'd rather want to create new and unique names there,
without assuming or relying on that the names used by these bundles
are reasonably unique, so this part of the code may have to change
anyway, so we may not care too deeply at this point.


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH] bundle-uri: copy all bundle references ino the refs/bundle space
  2025-02-25 18:14 ` Junio C Hamano
@ 2025-02-25 23:36   ` Derrick Stolee
  2025-03-01 10:23     ` Scott Chacon
  0 siblings, 1 reply; 19+ messages in thread
From: Derrick Stolee @ 2025-02-25 23:36 UTC (permalink / raw)
  To: Junio C Hamano, Scott Chacon via GitGitGadget; +Cc: git, Scott Chacon

On 2/25/25 1:14 PM, Junio C Hamano wrote:
> "Scott Chacon via GitGitGadget" <gitgitgadget@gmail.com> writes:
> 
>>      Furthermore, when I bundled just a tag (thinking it would have most
>>      reachable objects) it completely failed to work because there were no
>>      refs/heads/ available for negotiation - so it downloaded a huge file and
>>      then still started from scratch on the fetch.
> 
> Nice finding.

This is an interesting case that I had not considered in the original
implementation.

The intention of the design is to avoid having the bundle URI fetch
changing tag refs, especially annotated tags. Those tag updates are
expected to be advertised in the "git fetch" output. It would probably
be best to peel the tag refs to a commit and then create a fake branch
for the bundle.

>>      Now I'm only getting an extra 43k objects, so 1% of the original total,
>>      and the entire operation is a bit faster as well.
> 
> That makes all sense.
> 
>>      I'm not sure if there is a downside here, it seems clearly how you would
>>      want the negotiation to go. It ends up with way more refs under
>>      refs/bundle (now there is refs/bundle/origin/master, etc) but that's
>>      being polluted by the head refs anyhow, right?
> 
> I am not sure what you mean by "being polluted by the head refs
> anyhow", but we should be equipped to deal with a repository with
> tons of local branches, so having the comparable number of
> remote-tracking branches instead of local branches now exposed in
> refs/bundle/remotes/* hierarchy should work equally as well, or we
> would have something we need to fix.  So in principle I do not see
> a problem with this approach.
> 
> The mapping used to be "refs/heads/foo" to "refs/bundles/foo", but
> now "refs/heads/foo" is mapped to "refs/bundles/heads/foo", so that
> you would presumably be mapping "refs/remotes/origin/master" to
> "refs/bundles/remotes/origin/master", right?  I hope existing users
> are *not* looking at their resulting refs/bundles/ hierarchy and
> somehow assuming the original mapping.
> 
> This is not something this "fix" changes, but unbundle_all_bundles()
> apparently is prepared to handle more than one bundles.  I wonder
> what happens when multiple bundles expose the same branch pointing
> at different objects?  The way I read unbundle_from_file() is that
> a new one overwrites the previous value, so even though we may have
> all unbundled many bundles, the objects from earlier bundles may
> lose their anchors, subject to be garbage-collected.
> 
> Imagine creating a bundle with two refs, refs/heads and
> refs/remotes, and append that bundle as the last bundle of a bunch
> of bundles full of local and remote-tracking branches, that have
> populated refs/bundles/ hierarchy with tons of refs.  Now the last
> bundle is unbundled, and these two phoney refs would nuke everything
> that used to be under refs/bundles/heads/* and refs/bundles/remotes/*
> left by unpacking previous bundles, right?
> 
>>      Is this a reasonable change?
> 
> This is mostly Stolee's design, IIRC, so I have CC'ed; the work is
> mostly from 53a50892 (bundle-uri: create basic file-copy logic,
> 2022-08-09) that is more than 2 years ago.

The biggest question I had (and tried to get ahead of on the PR) is
the use of a test to demonstrate what kind of bundle files cause this
issue. It would be important to demosntrate that the repo is still
usable if "refs/bundles/tags/v1.0" exists and points to a tag object.

>> -		if (!skip_prefix(refname->string, "refs/heads/", &branch_name))
>> +		if (!skip_prefix(refname->string, "refs/", &branch_name))
>>   			continue;

So I'm OK with relaxing this to be more flexible, but I'm not sure
why the bundles couldn't be created using "refs/heads/", possibly via
changing the ref names during bundle creation.

> By skipping only "refs/", "branch_name" is no longer a branch name,
> which may be something we may want to fix, but if we were to address
> the "names from later bundles seem to overwrite names used by
> previous boundles, and unanchor objects obtained from them" problem,
> I suspect we'd rather want to create new and unique names there,
> without assuming or relying on that the names used by these bundles
> are reasonably unique, so this part of the code may have to change
> anyway, so we may not care too deeply at this point.

Thanks,
-Stolee


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH] bundle-uri: copy all bundle references ino the refs/bundle space
  2025-02-25 23:36   ` Derrick Stolee
@ 2025-03-01 10:23     ` Scott Chacon
  2025-03-03 17:12       ` Junio C Hamano
  0 siblings, 1 reply; 19+ messages in thread
From: Scott Chacon @ 2025-03-01 10:23 UTC (permalink / raw)
  To: Derrick Stolee; +Cc: Junio C Hamano, Scott Chacon via GitGitGadget, git

Hey,

On Wed, Feb 26, 2025 at 12:36 AM Derrick Stolee <stolee@gmail.com> wrote:
>
> The intention of the design is to avoid having the bundle URI fetch
> changing tag refs, especially annotated tags. Those tag updates are
> expected to be advertised in the "git fetch" output. It would probably
> be best to peel the tag refs to a commit and then create a fake branch
> for the bundle.

The issue for this and also for the other suggestion you have later on
is that I'm not sure how this can be easily done with the bundle
command. It seems like everyone would have to write some sort of
script to create a special type of bundle so that all these objects
are referenced in a way that makes the bundle-uri helper actually get
most of the objects that are needed.

Is there some option to rev-list that does this? Or are you saying
it's better to write a script?

> The biggest question I had (and tried to get ahead of on the PR) is
> the use of a test to demonstrate what kind of bundle files cause this
> issue. It would be important to demosntrate that the repo is still
> usable if "refs/bundles/tags/v1.0" exists and points to a tag object.

I have written a test and I'll submit the new series in a minute, but
I'm not sure what you mean by 'usable' in this context. Is there a
situation where Git gets mad if there are annotated tags that aren't
under refs/tags?

I have done these test clones and nothing bad seems to happen having
them in refs/bundle/tags/v1.0 that I notice, but I don't know how to
write a test that specifically verifies that.

> So I'm OK with relaxing this to be more flexible, but I'm not sure
> why the bundles couldn't be created using "refs/heads/", possibly via
> changing the ref names during bundle creation.

So same point here. I think the bundle-uri functionality isn't
particularly effective if the creation of the bundle needs special
scripts to create in a way that is expected.

One other approach would be to add an option to `git bundle` that does
this sanitization (unpeeling things into fake branch heads), like some
`--bundle-for-uri`, but I feel like just using `--all` and having the
clone handle it in the way I proposed might be much simpler and more
usable.

We could also immediately delete everything under `refs/bundle/tags`
after the fetch if we don't like them there, but still having them be
available for the fetch negotiation.

I'll send a new series with the existing tests updated to look for
`refs/bundle/heads/*` instead of `refs/bundle/*` and adding a very
simple test to see that the tags were unpacked as the next step.

Scott

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 0/3] bundle-uri: copy all bundle references ino the refs/bundle space
  2025-02-25 13:19 [PATCH] bundle-uri: copy all bundle references ino the refs/bundle space Scott Chacon via GitGitGadget
  2025-02-25 18:14 ` Junio C Hamano
@ 2025-03-01 10:33 ` Scott Chacon via GitGitGadget
  2025-03-01 10:33   ` [PATCH v2 1/3] " Scott Chacon via GitGitGadget
                     ` (4 more replies)
  1 sibling, 5 replies; 19+ messages in thread
From: Scott Chacon via GitGitGadget @ 2025-03-01 10:33 UTC (permalink / raw)
  To: git; +Cc: Derrick Stolee, Scott Chacon

Updating the series by fixing the tests to look in the new space that heads
are placed and added a small test to check for tags.

Scott Chacon (3):
  bundle-uri: copy all bundle references ino the refs/bundle space
  bundle-uri: update bundle clone tests with new refspec path
  bundle-uri: add test for bundle-uri clones with tags

 bundle-uri.c                |   2 +-
 t/t5558-clone-bundle-uri.sh | 203 +++++++++++++++++++++---------------
 2 files changed, 118 insertions(+), 87 deletions(-)


base-commit: cb0ae672aeabefca9704477ea8018ac94f523970
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1897%2Fschacon%2Fsc-more-bundle-refs-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1897/schacon/sc-more-bundle-refs-v2
Pull-Request: https://github.com/git/git/pull/1897

Range-diff vs v1:

 1:  96d78614214 = 1:  b36bc876fe1 bundle-uri: copy all bundle references ino the refs/bundle space
 -:  ----------- > 2:  5e198ba5c66 bundle-uri: update bundle clone tests with new refspec path
 -:  ----------- > 3:  ea204679cb0 bundle-uri: add test for bundle-uri clones with tags

-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 1/3] bundle-uri: copy all bundle references ino the refs/bundle space
  2025-03-01 10:33 ` [PATCH v2 0/3] " Scott Chacon via GitGitGadget
@ 2025-03-01 10:33   ` Scott Chacon via GitGitGadget
  2025-03-01 10:33   ` [PATCH v2 2/3] bundle-uri: update bundle clone tests with new refspec path Scott Chacon via GitGitGadget
                     ` (3 subsequent siblings)
  4 siblings, 0 replies; 19+ messages in thread
From: Scott Chacon via GitGitGadget @ 2025-03-01 10:33 UTC (permalink / raw)
  To: git; +Cc: Derrick Stolee, Scott Chacon, Scott Chacon

From: Scott Chacon <schacon@gmail.com>

When downloading bundles via the bundle-uri functionality, we only copy the
references from refs/heads into the refs/bundle space. I'm not sure why this
refspec is hardcoded to be so limited, but it makes the ref negotiation on
the subsequent fetch suboptimal, since it won't use objects that are
referenced outside of the current heads of the bundled repository.

This change to copy everything in refs/ in the bundle to refs/bundles/
significantly helps the subsequent fetch, since nearly all the references
are now included in the negotiation.

Signed-off-by: Scott Chacon <schacon@gmail.com>
---
 bundle-uri.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/bundle-uri.c b/bundle-uri.c
index 744257c49c1..3371d56f4ce 100644
--- a/bundle-uri.c
+++ b/bundle-uri.c
@@ -403,7 +403,7 @@ static int unbundle_from_file(struct repository *r, const char *file)
 		const char *branch_name;
 		int has_old;
 
-		if (!skip_prefix(refname->string, "refs/heads/", &branch_name))
+		if (!skip_prefix(refname->string, "refs/", &branch_name))
 			continue;
 
 		strbuf_setlen(&bundle_ref, bundle_prefix_len);
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2 2/3] bundle-uri: update bundle clone tests with new refspec path
  2025-03-01 10:33 ` [PATCH v2 0/3] " Scott Chacon via GitGitGadget
  2025-03-01 10:33   ` [PATCH v2 1/3] " Scott Chacon via GitGitGadget
@ 2025-03-01 10:33   ` Scott Chacon via GitGitGadget
  2025-03-01 10:33   ` [PATCH v2 3/3] bundle-uri: add test for bundle-uri clones with tags Scott Chacon via GitGitGadget
                     ` (2 subsequent siblings)
  4 siblings, 0 replies; 19+ messages in thread
From: Scott Chacon via GitGitGadget @ 2025-03-01 10:33 UTC (permalink / raw)
  To: git; +Cc: Derrick Stolee, Scott Chacon, Scott Chacon

From: Scott Chacon <schacon@gmail.com>

The update to the bundle-uri unbundling refspec puts all the heads from a
bundle file into refs/bundle/heads instead of directly into refs/bundle/ so
the tests need to be updated to look in the new heirarchy.

Signed-off-by: Scott Chacon <schacon@gmail.com>
---
 t/t5558-clone-bundle-uri.sh | 172 ++++++++++++++++++------------------
 1 file changed, 86 insertions(+), 86 deletions(-)

diff --git a/t/t5558-clone-bundle-uri.sh b/t/t5558-clone-bundle-uri.sh
index 3816ed5058d..33a7009e9a2 100755
--- a/t/t5558-clone-bundle-uri.sh
+++ b/t/t5558-clone-bundle-uri.sh
@@ -58,7 +58,7 @@ test_expect_success 'create bundle' '
 test_expect_success 'clone with path bundle' '
 	git clone --bundle-uri="clone-from/B.bundle" \
 		clone-from clone-path &&
-	git -C clone-path rev-parse refs/bundles/topic >actual &&
+	git -C clone-path rev-parse refs/bundles/heads/topic >actual &&
 	git -C clone-from rev-parse topic >expect &&
 	test_cmp expect actual
 '
@@ -68,9 +68,9 @@ test_expect_success 'clone with bundle that has bad header' '
 	git clone --bundle-uri="clone-from/bad-header.bundle" \
 		clone-from clone-bad-header 2>err &&
 	commit_b=$(git -C clone-from rev-parse B) &&
-	test_grep "trying to write ref '\''refs/bundles/topic'\'' with nonexistent object $commit_b" err &&
+	test_grep "trying to write ref '\''refs/bundles/heads/topic'\'' with nonexistent object $commit_b" err &&
 	git -C clone-bad-header for-each-ref --format="%(refname)" >refs &&
-	test_grep ! "refs/bundles/" refs
+	test_grep ! "refs/bundles/heads/" refs
 '
 
 test_expect_success 'clone with bundle that has bad object' '
@@ -78,8 +78,8 @@ test_expect_success 'clone with bundle that has bad object' '
 	git clone --bundle-uri="clone-from/bad-object.bundle" \
 		clone-from clone-bad-object-no-fsck &&
 	git -C clone-bad-object-no-fsck for-each-ref --format="%(refname)" >refs &&
-	grep "refs/bundles/" refs >actual &&
-	test_write_lines refs/bundles/bad >expect &&
+	grep "refs/bundles/heads/" refs >actual &&
+	test_write_lines refs/bundles/heads/bad >expect &&
 	test_cmp expect actual &&
 
 	# Unbundle fails with fsckObjects set true, but clone can still proceed.
@@ -87,14 +87,14 @@ test_expect_success 'clone with bundle that has bad object' '
 		clone-from clone-bad-object-fsck 2>err &&
 	test_grep "missingEmail" err &&
 	git -C clone-bad-object-fsck for-each-ref --format="%(refname)" >refs &&
-	test_grep ! "refs/bundles/" refs
+	test_grep ! "refs/bundles/heads/" refs
 '
 
 test_expect_success 'clone with path bundle and non-default hash' '
 	test_when_finished "rm -rf clone-path-non-default-hash" &&
 	GIT_DEFAULT_HASH=sha256 git clone --bundle-uri="clone-from/B.bundle" \
 		clone-from clone-path-non-default-hash &&
-	git -C clone-path-non-default-hash rev-parse refs/bundles/topic >actual &&
+	git -C clone-path-non-default-hash rev-parse refs/bundles/heads/topic >actual &&
 	git -C clone-from rev-parse topic >expect &&
 	test_cmp expect actual
 '
@@ -102,7 +102,7 @@ test_expect_success 'clone with path bundle and non-default hash' '
 test_expect_success 'clone with file:// bundle' '
 	git clone --bundle-uri="file://$(pwd)/clone-from/B.bundle" \
 		clone-from clone-file &&
-	git -C clone-file rev-parse refs/bundles/topic >actual &&
+	git -C clone-file rev-parse refs/bundles/heads/topic >actual &&
 	git -C clone-from rev-parse topic >expect &&
 	test_cmp expect actual
 '
@@ -173,12 +173,12 @@ test_expect_success 'clone bundle list (file, no heuristic)' '
 	git -C clone-list-file cat-file --batch-check <oids &&
 
 	git -C clone-list-file for-each-ref --format="%(refname)" >refs &&
-	grep "refs/bundles/" refs >actual &&
+	grep "refs/bundles/heads/" refs >actual &&
 	cat >expect <<-\EOF &&
-	refs/bundles/base
-	refs/bundles/left
-	refs/bundles/merge
-	refs/bundles/right
+	refs/bundles/heads/base
+	refs/bundles/heads/left
+	refs/bundles/heads/merge
+	refs/bundles/heads/right
 	EOF
 	test_cmp expect actual
 '
@@ -220,10 +220,10 @@ test_expect_success 'clone bundle list (file, all mode, some failures)' '
 	git -C clone-all-some cat-file --batch-check <oids &&
 
 	git -C clone-all-some for-each-ref --format="%(refname)" >refs &&
-	grep "refs/bundles/" refs >actual &&
+	grep "refs/bundles/heads/" refs >actual &&
 	cat >expect <<-\EOF &&
-	refs/bundles/base
-	refs/bundles/left
+	refs/bundles/heads/base
+	refs/bundles/heads/left
 	EOF
 	test_cmp expect actual
 '
@@ -253,7 +253,7 @@ test_expect_success 'clone bundle list (file, all mode, all failures)' '
 	git -C clone-all-fail cat-file --batch-check <oids &&
 
 	git -C clone-all-fail for-each-ref --format="%(refname)" >refs &&
-	! grep "refs/bundles/" refs
+	! grep "refs/bundles/heads/" refs
 '
 
 test_expect_success 'clone bundle list (file, any mode)' '
@@ -282,9 +282,9 @@ test_expect_success 'clone bundle list (file, any mode)' '
 	git -C clone-any-file cat-file --batch-check <oids &&
 
 	git -C clone-any-file for-each-ref --format="%(refname)" >refs &&
-	grep "refs/bundles/" refs >actual &&
+	grep "refs/bundles/heads/" refs >actual &&
 	cat >expect <<-\EOF &&
-	refs/bundles/base
+	refs/bundles/heads/base
 	EOF
 	test_cmp expect actual
 '
@@ -313,7 +313,7 @@ test_expect_success 'clone bundle list (file, any mode, all failures)' '
 	git -C clone-any-fail cat-file --batch-check <oids &&
 
 	git -C clone-any-fail for-each-ref --format="%(refname)" >refs &&
-	! grep "refs/bundles/" refs
+	! grep "refs/bundles/heads/" refs
 '
 
 test_expect_success 'negotiation: bundle with part of wanted commits' '
@@ -322,10 +322,10 @@ test_expect_success 'negotiation: bundle with part of wanted commits' '
 	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 &&
-	test_write_lines refs/bundles/topic >expect &&
+	grep "refs/bundles/heads/" refs >actual &&
+	test_write_lines refs/bundles/heads/topic >expect &&
 	test_cmp expect actual &&
-	# Ensure that refs/bundles/topic are sent as "have".
+	# Ensure that refs/bundles/heads/topic are sent as "have".
 	tip=$(git -C clone-from rev-parse A) &&
 	test_grep "clone> have $tip" trace-packet.txt
 '
@@ -337,8 +337,8 @@ test_expect_success 'negotiation: bundle with all wanted commits' '
 		--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 &&
-	test_write_lines refs/bundles/topic >expect &&
+	grep "refs/bundles/heads/" refs >actual &&
+	test_write_lines refs/bundles/heads/topic >expect &&
 	test_cmp expect actual &&
 	# We already have all needed commits so no "want" needed.
 	test_grep ! "clone> want " trace-packet.txt
@@ -363,13 +363,13 @@ test_expect_success 'negotiation: bundle list (no heuristic)' '
 		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 &&
+	grep "refs/bundles/heads/" refs >actual &&
 	cat >expect <<-\EOF &&
-	refs/bundles/base
-	refs/bundles/left
+	refs/bundles/heads/base
+	refs/bundles/heads/left
 	EOF
 	test_cmp expect actual &&
-	tip=$(git -C nego-bundle-list-no-heuristic rev-parse refs/bundles/left) &&
+	tip=$(git -C nego-bundle-list-no-heuristic rev-parse refs/bundles/heads/left) &&
 	test_grep "clone> have $tip" trace-packet.txt
 '
 
@@ -395,13 +395,13 @@ test_expect_success 'negotiation: bundle list (creationToken)' '
 		clone-from nego-bundle-list-heuristic &&
 
 	git -C nego-bundle-list-heuristic for-each-ref --format="%(refname)" >refs &&
-	grep "refs/bundles/" refs >actual &&
+	grep "refs/bundles/heads/" refs >actual &&
 	cat >expect <<-\EOF &&
-	refs/bundles/base
-	refs/bundles/left
+	refs/bundles/heads/base
+	refs/bundles/heads/left
 	EOF
 	test_cmp expect actual &&
-	tip=$(git -C nego-bundle-list-heuristic rev-parse refs/bundles/left) &&
+	tip=$(git -C nego-bundle-list-heuristic rev-parse refs/bundles/heads/left) &&
 	test_grep "clone> have $tip" trace-packet.txt
 '
 
@@ -428,10 +428,10 @@ test_expect_success 'negotiation: bundle list with all wanted commits' '
 		clone-from nego-bundle-list-all &&
 
 	git -C nego-bundle-list-all for-each-ref --format="%(refname)" >refs &&
-	grep "refs/bundles/" refs >actual &&
+	grep "refs/bundles/heads/" refs >actual &&
 	cat >expect <<-\EOF &&
-	refs/bundles/base
-	refs/bundles/left
+	refs/bundles/heads/base
+	refs/bundles/heads/left
 	EOF
 	test_cmp expect actual &&
 	# We already have all needed commits so no "want" needed.
@@ -465,7 +465,7 @@ test_expect_success 'clone HTTP bundle' '
 
 	git clone --bundle-uri="$HTTPD_URL/B.bundle" \
 		"$HTTPD_URL/smart/fetch.git" clone-http &&
-	git -C clone-http rev-parse refs/bundles/topic >actual &&
+	git -C clone-http rev-parse refs/bundles/heads/topic >actual &&
 	git -C clone-from rev-parse topic >expect &&
 	test_cmp expect actual &&
 
@@ -476,7 +476,7 @@ test_expect_success 'clone HTTP bundle with non-default hash' '
 	test_when_finished "rm -rf clone-http-non-default-hash" &&
 	GIT_DEFAULT_HASH=sha256 git clone --bundle-uri="$HTTPD_URL/B.bundle" \
 		"$HTTPD_URL/smart/fetch.git" clone-http-non-default-hash &&
-	git -C clone-http-non-default-hash rev-parse refs/bundles/topic >actual &&
+	git -C clone-http-non-default-hash rev-parse refs/bundles/heads/topic >actual &&
 	git -C clone-from rev-parse topic >expect &&
 	test_cmp expect actual
 '
@@ -553,12 +553,12 @@ test_expect_success 'clone bundle list (HTTP, any mode)' '
 	git -C clone-any-http cat-file --batch-check <oids &&
 
 	git -C clone-list-file for-each-ref --format="%(refname)" >refs &&
-	grep "refs/bundles/" refs >actual &&
+	grep "refs/bundles/heads/" refs >actual &&
 	cat >expect <<-\EOF &&
-	refs/bundles/base
-	refs/bundles/left
-	refs/bundles/merge
-	refs/bundles/right
+	refs/bundles/heads/base
+	refs/bundles/heads/left
+	refs/bundles/heads/merge
+	refs/bundles/heads/right
 	EOF
 	test_cmp expect actual
 '
@@ -641,9 +641,9 @@ test_expect_success 'clone incomplete bundle list (http, creationToken)' '
 	test_cmp expect actual &&
 
 	# We now have only one bundle ref.
-	git -C clone-token-http for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+	git -C clone-token-http for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
 	cat >expect <<-\EOF &&
-	refs/bundles/base
+	refs/bundles/heads/base
 	EOF
 	test_cmp expect refs &&
 
@@ -679,13 +679,13 @@ test_expect_success 'clone incomplete bundle list (http, creationToken)' '
 	test_cmp expect actual &&
 
 	# We now have all bundle refs.
-	git -C clone-token-http for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+	git -C clone-token-http for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
 
 	cat >expect <<-\EOF &&
-	refs/bundles/base
-	refs/bundles/left
-	refs/bundles/merge
-	refs/bundles/right
+	refs/bundles/heads/base
+	refs/bundles/heads/left
+	refs/bundles/heads/merge
+	refs/bundles/heads/right
 	EOF
 	test_cmp expect refs
 '
@@ -721,9 +721,9 @@ test_expect_success 'http clone with bundle.heuristic creates fetch.bundleURI' '
 	test_cmp expect actual &&
 
 	# only received base ref from bundle-1
-	git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+	git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
 	cat >expect <<-\EOF &&
-	refs/bundles/base
+	refs/bundles/heads/base
 	EOF
 	test_cmp expect refs &&
 
@@ -749,10 +749,10 @@ test_expect_success 'http clone with bundle.heuristic creates fetch.bundleURI' '
 	test_cmp expect actual &&
 
 	# received left from bundle-2
-	git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+	git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
 	cat >expect <<-\EOF &&
-	refs/bundles/base
-	refs/bundles/left
+	refs/bundles/heads/base
+	refs/bundles/heads/left
 	EOF
 	test_cmp expect refs &&
 
@@ -795,12 +795,12 @@ test_expect_success 'http clone with bundle.heuristic creates fetch.bundleURI' '
 
 	# received merge ref from bundle-4, but right is missing
 	# because we did not download bundle-3.
-	git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+	git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
 
 	cat >expect <<-\EOF &&
-	refs/bundles/base
-	refs/bundles/left
-	refs/bundles/merge
+	refs/bundles/heads/base
+	refs/bundles/heads/left
+	refs/bundles/heads/merge
 	EOF
 	test_cmp expect refs &&
 
@@ -862,7 +862,7 @@ test_expect_success 'creationToken heuristic with failed downloads (clone)' '
 	test_cmp expect actual &&
 
 	# All bundles failed to unbundle
-	git -C download-1 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+	git -C download-1 for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
 	test_must_be_empty refs &&
 
 	# Case 2: middle bundle does not exist, only two bundles can unbundle
@@ -909,10 +909,10 @@ test_expect_success 'creationToken heuristic with failed downloads (clone)' '
 	test_cmp expect actual &&
 
 	# bundle-1 and bundle-3 could unbundle, but bundle-4 could not
-	git -C download-2 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+	git -C download-2 for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
 	cat >expect <<-EOF &&
-	refs/bundles/base
-	refs/bundles/right
+	refs/bundles/heads/base
+	refs/bundles/heads/right
 	EOF
 	test_cmp expect refs &&
 
@@ -961,11 +961,11 @@ test_expect_success 'creationToken heuristic with failed downloads (clone)' '
 	test_cmp expect actual &&
 
 	# fake.bundle did not unbundle, but the others did.
-	git -C download-3 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+	git -C download-3 for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
 	cat >expect <<-EOF &&
-	refs/bundles/base
-	refs/bundles/left
-	refs/bundles/right
+	refs/bundles/heads/base
+	refs/bundles/heads/left
+	refs/bundles/heads/right
 	EOF
 	test_cmp expect refs
 '
@@ -1083,15 +1083,15 @@ test_expect_success 'creationToken heuristic with failed downloads (fetch)' '
 	test_cmp expect actual &&
 
 	# Check which bundles have unbundled by refs
-	git -C fetch-1 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+	git -C fetch-1 for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
 	cat >expect <<-EOF &&
-	refs/bundles/base
-	refs/bundles/left
-	refs/bundles/lefter
-	refs/bundles/merge
-	refs/bundles/right
-	refs/bundles/righter
-	refs/bundles/top
+	refs/bundles/heads/base
+	refs/bundles/heads/left
+	refs/bundles/heads/lefter
+	refs/bundles/heads/merge
+	refs/bundles/heads/right
+	refs/bundles/heads/righter
+	refs/bundles/heads/top
 	EOF
 	test_cmp expect refs &&
 
@@ -1144,12 +1144,12 @@ test_expect_success 'creationToken heuristic with failed downloads (fetch)' '
 	test_cmp expect actual &&
 
 	# Check which bundles have unbundled by refs
-	git -C fetch-2 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+	git -C fetch-2 for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
 	cat >expect <<-EOF &&
-	refs/bundles/base
-	refs/bundles/left
-	refs/bundles/merge
-	refs/bundles/right
+	refs/bundles/heads/base
+	refs/bundles/heads/left
+	refs/bundles/heads/merge
+	refs/bundles/heads/right
 	EOF
 	test_cmp expect refs &&
 
@@ -1204,13 +1204,13 @@ test_expect_success 'creationToken heuristic with failed downloads (fetch)' '
 	test_cmp expect actual &&
 
 	# Check which bundles have unbundled by refs
-	git -C fetch-3 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+	git -C fetch-3 for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
 	cat >expect <<-EOF &&
-	refs/bundles/base
-	refs/bundles/left
-	refs/bundles/lefter
-	refs/bundles/right
-	refs/bundles/righter
+	refs/bundles/heads/base
+	refs/bundles/heads/left
+	refs/bundles/heads/lefter
+	refs/bundles/heads/right
+	refs/bundles/heads/righter
 	EOF
 	test_cmp expect refs
 '
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2 3/3] bundle-uri: add test for bundle-uri clones with tags
  2025-03-01 10:33 ` [PATCH v2 0/3] " Scott Chacon via GitGitGadget
  2025-03-01 10:33   ` [PATCH v2 1/3] " Scott Chacon via GitGitGadget
  2025-03-01 10:33   ` [PATCH v2 2/3] bundle-uri: update bundle clone tests with new refspec path Scott Chacon via GitGitGadget
@ 2025-03-01 10:33   ` Scott Chacon via GitGitGadget
  2025-03-03 18:49   ` [PATCH v2 0/3] bundle-uri: copy all bundle references ino the refs/bundle space Derrick Stolee
  2025-03-18 15:36   ` [PATCH v3 0/2] " Scott Chacon via GitGitGadget
  4 siblings, 0 replies; 19+ messages in thread
From: Scott Chacon via GitGitGadget @ 2025-03-01 10:33 UTC (permalink / raw)
  To: git; +Cc: Derrick Stolee, Scott Chacon, Scott Chacon

From: Scott Chacon <schacon@gmail.com>

The change to the bundle-uri unbundling refspec now includes tags, so this
adds a simple test to make sure that tags in a bundle are properly added to
the cloned repository and will be included in ref negotiation with the
subsequent fetch.

Signed-off-by: Scott Chacon <schacon@gmail.com>
---
 t/t5558-clone-bundle-uri.sh | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/t/t5558-clone-bundle-uri.sh b/t/t5558-clone-bundle-uri.sh
index 33a7009e9a2..b1276ba295c 100755
--- a/t/t5558-clone-bundle-uri.sh
+++ b/t/t5558-clone-bundle-uri.sh
@@ -107,6 +107,37 @@ test_expect_success 'clone with file:// bundle' '
 	test_cmp expect actual
 '
 
+test_expect_success 'create bundle with tags' '
+	git init clone-from-tags &&
+	(
+		cd clone-from-tags &&
+		git checkout -b base &&
+		git checkout -b topic &&
+
+		test_commit A &&
+		git tag tag-A &&
+		git checkout -b base &&
+		git branch -d topic &&
+		test_commit B &&
+
+		git bundle create ALL.bundle --all &&
+		git bundle verify ALL.bundle
+	)
+'
+
+test_expect_success 'clone with tags bundle' '
+	git clone --bundle-uri="clone-from-tags/ALL.bundle" \
+		clone-from-tags clone-tags-path &&
+	git -C clone-tags-path for-each-ref --format="%(refname)" >refs &&
+	grep "refs/bundles/tags/" refs >actual &&
+	cat >expect <<-\EOF &&
+	refs/bundles/tags/A
+	refs/bundles/tags/B
+	refs/bundles/tags/tag-A
+	EOF
+	test_cmp expect actual
+'
+
 # To get interesting tests for bundle lists, we need to construct a
 # somewhat-interesting commit history.
 #
-- 
gitgitgadget

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH] bundle-uri: copy all bundle references ino the refs/bundle space
  2025-03-01 10:23     ` Scott Chacon
@ 2025-03-03 17:12       ` Junio C Hamano
  2025-03-03 18:46         ` Derrick Stolee
  0 siblings, 1 reply; 19+ messages in thread
From: Junio C Hamano @ 2025-03-03 17:12 UTC (permalink / raw)
  To: Scott Chacon; +Cc: Derrick Stolee, Scott Chacon via GitGitGadget, git

Scott Chacon <schacon@gmail.com> writes:

> Hey,
>
> On Wed, Feb 26, 2025 at 12:36 AM Derrick Stolee <stolee@gmail.com> wrote:
>>
>> The intention of the design is to avoid having the bundle URI fetch
>> changing tag refs, especially annotated tags. Those tag updates are
>> expected to be advertised in the "git fetch" output. It would probably
>> be best to peel the tag refs to a commit and then create a fake branch
>> for the bundle.

I am not sure where that need to avoid including tags comes from.

>> The biggest question I had (and tried to get ahead of on the PR) is
>> the use of a test to demonstrate what kind of bundle files cause this
>> issue. It would be important to demosntrate that the repo is still
>> usable if "refs/bundles/tags/v1.0" exists and points to a tag object.
>
> I have written a test and I'll submit the new series in a minute, but
> I'm not sure what you mean by 'usable' in this context. Is there a
> situation where Git gets mad if there are annotated tags that aren't
> under refs/tags?

I do not know of any at least for a local consumption of these tags.

> I have done these test clones and nothing bad seems to happen having
> them in refs/bundle/tags/v1.0 that I notice, but I don't know how to
> write a test that specifically verifies that.

Can it be some brittleness Derrick is worried about auto-following
of tags during future "git fetch"?  You store a tag that a regular
fetch may want to store at refs/tags/v1.0 in refs/bundles/tags/v1.0
taken from the bundle, and then a later fetch may advance the
history based on you extracted from the bundle---without having to
run an explicit "git fetch --tags" or "git fetch origin v1.0", would
we ever obtain "refs/tags/v1.0" with only the usual auto-following
when we have the same tag elsewhere?

In any case, instead of me speculating, I'd prefer to hear from
Derrick, who is a lot more familiar with the mechanism under
discussion, what the issues are that we want to limit ourselves to
local branches.

Thanks.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH] bundle-uri: copy all bundle references ino the refs/bundle space
  2025-03-03 17:12       ` Junio C Hamano
@ 2025-03-03 18:46         ` Derrick Stolee
  0 siblings, 0 replies; 19+ messages in thread
From: Derrick Stolee @ 2025-03-03 18:46 UTC (permalink / raw)
  To: Junio C Hamano, Scott Chacon; +Cc: Scott Chacon via GitGitGadget, git, vdye

On 3/3/25 12:12 PM, Junio C Hamano wrote:
> Scott Chacon <schacon@gmail.com> writes:
> 
>> Hey,
>>
>> On Wed, Feb 26, 2025 at 12:36 AM Derrick Stolee <stolee@gmail.com> wrote:
>>>
>>> The intention of the design is to avoid having the bundle URI fetch
>>> changing tag refs, especially annotated tags. Those tag updates are
>>> expected to be advertised in the "git fetch" output. It would probably
>>> be best to peel the tag refs to a commit and then create a fake branch
>>> for the bundle.
> 
> I am not sure where that need to avoid including tags comes from.
> 
>>> The biggest question I had (and tried to get ahead of on the PR) is
>>> the use of a test to demonstrate what kind of bundle files cause this
>>> issue. It would be important to demosntrate that the repo is still
>>> usable if "refs/bundles/tags/v1.0" exists and points to a tag object.
>>
>> I have written a test and I'll submit the new series in a minute, but
>> I'm not sure what you mean by 'usable' in this context. Is there a
>> situation where Git gets mad if there are annotated tags that aren't
>> under refs/tags?
> 
> I do not know of any at least for a local consumption of these tags.

These ideas about avoiding annotated tags outside of refs/tags/ is
likely an invention of my own, and must not be a firm expectation of
the Git tool.

>> I have done these test clones and nothing bad seems to happen having
>> them in refs/bundle/tags/v1.0 that I notice, but I don't know how to
>> write a test that specifically verifies that.
> 
> Can it be some brittleness Derrick is worried about auto-following
> of tags during future "git fetch"?  You store a tag that a regular
> fetch may want to store at refs/tags/v1.0 in refs/bundles/tags/v1.0
> taken from the bundle, and then a later fetch may advance the
> history based on you extracted from the bundle---without having to
> run an explicit "git fetch --tags" or "git fetch origin v1.0", would
> we ever obtain "refs/tags/v1.0" with only the usual auto-following
> when we have the same tag elsewhere?
> 
> In any case, instead of me speculating, I'd prefer to hear from
> Derrick, who is a lot more familiar with the mechanism under
> discussion, what the issues are that we want to limit ourselves to
> local branches.
My thinking here is similar to the prefetch maintenance task: we want
users who run "git fetch [origin]" to see the refs that are being
updated by that action in the normal foreground messages. This includes
new tag messages, such as in my local copy of the Git repository:

$ git fetch origin
remote: Enumerating objects: 186, done.
remote: Counting objects: 100% (168/168), done.
remote: Compressing objects: 100% (51/51), done.
remote: Total 186 (delta 120), reused 162 (delta 117), pack-reused 18 (from 2)
Receiving objects: 100% (186/186), 118.04 KiB | 10.73 MiB/s, done.
Resolving deltas: 100% (122/122), completed with 24 local objects.
 From github.com:git/git
    b838bf19389..db91954e186  master      -> origin/master
    2feabab25ac..627208d89de  next        -> origin/next
  + 9b2be6f7989...39dfbbf0521 seen        -> origin/seen  (forced update)
    5fa232d8520..fb8899337a8  todo        -> origin/todo
  * [new tag]                 v2.49.0-rc0 -> v2.49.0-rc0

As long as these messages are still appearing, then I'm fine with
these annotated tags being added to the object database earlier by
the bundle URI mechanism.

Scott: You also asked about the intended design for bundle URIs and
things, and the best places to look are Git's technical docs [1] and
the bundle server reference implementation docs [2]. CC Victoria who
implemented the reference implementation and wrote those docs.

[1] https://github.com/git/git/blob/master/Documentation/technical/bundle-uri.adoc

[2] 
https://github.com/git-ecosystem/git-bundle-server/blob/main/docs/technical/architecture.md

Thanks,
-Stolee

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 0/3] bundle-uri: copy all bundle references ino the refs/bundle space
  2025-03-01 10:33 ` [PATCH v2 0/3] " Scott Chacon via GitGitGadget
                     ` (2 preceding siblings ...)
  2025-03-01 10:33   ` [PATCH v2 3/3] bundle-uri: add test for bundle-uri clones with tags Scott Chacon via GitGitGadget
@ 2025-03-03 18:49   ` Derrick Stolee
  2025-03-18 15:36   ` [PATCH v3 0/2] " Scott Chacon via GitGitGadget
  4 siblings, 0 replies; 19+ messages in thread
From: Derrick Stolee @ 2025-03-03 18:49 UTC (permalink / raw)
  To: Scott Chacon via GitGitGadget, git; +Cc: Scott Chacon

On 3/1/25 5:33 AM, Scott Chacon via GitGitGadget wrote:
> Updating the series by fixing the tests to look in the new space that heads
> are placed and added a small test to check for tags.
> 
> Scott Chacon (3):
>    bundle-uri: copy all bundle references ino the refs/bundle space
>    bundle-uri: update bundle clone tests with new refspec path

Thanks for updating these tests. While I appreciate a small commit, we do
want to make sure that each commit passes the test suite whenever possible,
so these two should be squashed together.

>    bundle-uri: add test for bundle-uri clones with tags

This new test is good to stay isolated into its own commit for easier review.

Thanks,
-Stolee


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v3 0/2] bundle-uri: copy all bundle references ino the refs/bundle space
  2025-03-01 10:33 ` [PATCH v2 0/3] " Scott Chacon via GitGitGadget
                     ` (3 preceding siblings ...)
  2025-03-03 18:49   ` [PATCH v2 0/3] bundle-uri: copy all bundle references ino the refs/bundle space Derrick Stolee
@ 2025-03-18 15:36   ` Scott Chacon via GitGitGadget
  2025-03-18 15:36     ` [PATCH v3 1/2] " Scott Chacon via GitGitGadget
  2025-03-18 15:36     ` [PATCH v3 2/2] bundle-uri: add test for bundle-uri clones with tags Scott Chacon via GitGitGadget
  4 siblings, 2 replies; 19+ messages in thread
From: Scott Chacon via GitGitGadget @ 2025-03-18 15:36 UTC (permalink / raw)
  To: git; +Cc: Derrick Stolee, Scott Chacon

Rebased the series onto current master and squashed the patch that modified
the existing test into the main patch.

> bundle-uri: copy all bundle references ino the refs/bundle space
> bundle-uri: update bundle clone tests with new refspec path

Scott Chacon (2):
  bundle-uri: copy all bundle references ino the refs/bundle space
  bundle-uri: add test for bundle-uri clones with tags

 bundle-uri.c                |   2 +-
 t/t5558-clone-bundle-uri.sh | 203 +++++++++++++++++++++---------------
 2 files changed, 118 insertions(+), 87 deletions(-)


base-commit: 683c54c999c301c2cd6f715c411407c413b1d84e
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1897%2Fschacon%2Fsc-more-bundle-refs-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1897/schacon/sc-more-bundle-refs-v3
Pull-Request: https://github.com/git/git/pull/1897

Range-diff vs v2:

 1:  b36bc876fe1 < -:  ----------- bundle-uri: copy all bundle references ino the refs/bundle space
 2:  5e198ba5c66 ! 1:  2ccbfdcc2dc bundle-uri: update bundle clone tests with new refspec path
     @@ Metadata
      Author: Scott Chacon <schacon@gmail.com>
      
       ## Commit message ##
     -    bundle-uri: update bundle clone tests with new refspec path
     +    bundle-uri: copy all bundle references ino the refs/bundle space
     +
     +    When downloading bundles via the bundle-uri functionality, we only copy the
     +    references from refs/heads into the refs/bundle space. I'm not sure why this
     +    refspec is hardcoded to be so limited, but it makes the ref negotiation on
     +    the subsequent fetch suboptimal, since it won't use objects that are
     +    referenced outside of the current heads of the bundled repository.
     +
     +    This change to copy everything in refs/ in the bundle to refs/bundles/
     +    significantly helps the subsequent fetch, since nearly all the references
     +    are now included in the negotiation.
      
          The update to the bundle-uri unbundling refspec puts all the heads from a
          bundle file into refs/bundle/heads instead of directly into refs/bundle/ so
     -    the tests need to be updated to look in the new heirarchy.
     +    the tests also need to be updated to look in the new heirarchy.
      
          Signed-off-by: Scott Chacon <schacon@gmail.com>
      
     + ## bundle-uri.c ##
     +@@ bundle-uri.c: static int unbundle_from_file(struct repository *r, const char *file)
     + 		const char *branch_name;
     + 		int has_old;
     + 
     +-		if (!skip_prefix(refname->string, "refs/heads/", &branch_name))
     ++		if (!skip_prefix(refname->string, "refs/", &branch_name))
     + 			continue;
     + 
     + 		strbuf_setlen(&bundle_ref, bundle_prefix_len);
     +
       ## t/t5558-clone-bundle-uri.sh ##
      @@ t/t5558-clone-bundle-uri.sh: test_expect_success 'create bundle' '
       test_expect_success 'clone with path bundle' '
 3:  ea204679cb0 = 2:  d148b14c390 bundle-uri: add test for bundle-uri clones with tags

-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v3 1/2] bundle-uri: copy all bundle references ino the refs/bundle space
  2025-03-18 15:36   ` [PATCH v3 0/2] " Scott Chacon via GitGitGadget
@ 2025-03-18 15:36     ` Scott Chacon via GitGitGadget
  2025-03-19 10:24       ` Phillip Wood
  2025-03-18 15:36     ` [PATCH v3 2/2] bundle-uri: add test for bundle-uri clones with tags Scott Chacon via GitGitGadget
  1 sibling, 1 reply; 19+ messages in thread
From: Scott Chacon via GitGitGadget @ 2025-03-18 15:36 UTC (permalink / raw)
  To: git; +Cc: Derrick Stolee, Scott Chacon, Scott Chacon

From: Scott Chacon <schacon@gmail.com>

When downloading bundles via the bundle-uri functionality, we only copy the
references from refs/heads into the refs/bundle space. I'm not sure why this
refspec is hardcoded to be so limited, but it makes the ref negotiation on
the subsequent fetch suboptimal, since it won't use objects that are
referenced outside of the current heads of the bundled repository.

This change to copy everything in refs/ in the bundle to refs/bundles/
significantly helps the subsequent fetch, since nearly all the references
are now included in the negotiation.

The update to the bundle-uri unbundling refspec puts all the heads from a
bundle file into refs/bundle/heads instead of directly into refs/bundle/ so
the tests also need to be updated to look in the new heirarchy.

Signed-off-by: Scott Chacon <schacon@gmail.com>
---
 bundle-uri.c                |   2 +-
 t/t5558-clone-bundle-uri.sh | 172 ++++++++++++++++++------------------
 2 files changed, 87 insertions(+), 87 deletions(-)

diff --git a/bundle-uri.c b/bundle-uri.c
index 744257c49c1..3371d56f4ce 100644
--- a/bundle-uri.c
+++ b/bundle-uri.c
@@ -403,7 +403,7 @@ static int unbundle_from_file(struct repository *r, const char *file)
 		const char *branch_name;
 		int has_old;
 
-		if (!skip_prefix(refname->string, "refs/heads/", &branch_name))
+		if (!skip_prefix(refname->string, "refs/", &branch_name))
 			continue;
 
 		strbuf_setlen(&bundle_ref, bundle_prefix_len);
diff --git a/t/t5558-clone-bundle-uri.sh b/t/t5558-clone-bundle-uri.sh
index 3816ed5058d..33a7009e9a2 100755
--- a/t/t5558-clone-bundle-uri.sh
+++ b/t/t5558-clone-bundle-uri.sh
@@ -58,7 +58,7 @@ test_expect_success 'create bundle' '
 test_expect_success 'clone with path bundle' '
 	git clone --bundle-uri="clone-from/B.bundle" \
 		clone-from clone-path &&
-	git -C clone-path rev-parse refs/bundles/topic >actual &&
+	git -C clone-path rev-parse refs/bundles/heads/topic >actual &&
 	git -C clone-from rev-parse topic >expect &&
 	test_cmp expect actual
 '
@@ -68,9 +68,9 @@ test_expect_success 'clone with bundle that has bad header' '
 	git clone --bundle-uri="clone-from/bad-header.bundle" \
 		clone-from clone-bad-header 2>err &&
 	commit_b=$(git -C clone-from rev-parse B) &&
-	test_grep "trying to write ref '\''refs/bundles/topic'\'' with nonexistent object $commit_b" err &&
+	test_grep "trying to write ref '\''refs/bundles/heads/topic'\'' with nonexistent object $commit_b" err &&
 	git -C clone-bad-header for-each-ref --format="%(refname)" >refs &&
-	test_grep ! "refs/bundles/" refs
+	test_grep ! "refs/bundles/heads/" refs
 '
 
 test_expect_success 'clone with bundle that has bad object' '
@@ -78,8 +78,8 @@ test_expect_success 'clone with bundle that has bad object' '
 	git clone --bundle-uri="clone-from/bad-object.bundle" \
 		clone-from clone-bad-object-no-fsck &&
 	git -C clone-bad-object-no-fsck for-each-ref --format="%(refname)" >refs &&
-	grep "refs/bundles/" refs >actual &&
-	test_write_lines refs/bundles/bad >expect &&
+	grep "refs/bundles/heads/" refs >actual &&
+	test_write_lines refs/bundles/heads/bad >expect &&
 	test_cmp expect actual &&
 
 	# Unbundle fails with fsckObjects set true, but clone can still proceed.
@@ -87,14 +87,14 @@ test_expect_success 'clone with bundle that has bad object' '
 		clone-from clone-bad-object-fsck 2>err &&
 	test_grep "missingEmail" err &&
 	git -C clone-bad-object-fsck for-each-ref --format="%(refname)" >refs &&
-	test_grep ! "refs/bundles/" refs
+	test_grep ! "refs/bundles/heads/" refs
 '
 
 test_expect_success 'clone with path bundle and non-default hash' '
 	test_when_finished "rm -rf clone-path-non-default-hash" &&
 	GIT_DEFAULT_HASH=sha256 git clone --bundle-uri="clone-from/B.bundle" \
 		clone-from clone-path-non-default-hash &&
-	git -C clone-path-non-default-hash rev-parse refs/bundles/topic >actual &&
+	git -C clone-path-non-default-hash rev-parse refs/bundles/heads/topic >actual &&
 	git -C clone-from rev-parse topic >expect &&
 	test_cmp expect actual
 '
@@ -102,7 +102,7 @@ test_expect_success 'clone with path bundle and non-default hash' '
 test_expect_success 'clone with file:// bundle' '
 	git clone --bundle-uri="file://$(pwd)/clone-from/B.bundle" \
 		clone-from clone-file &&
-	git -C clone-file rev-parse refs/bundles/topic >actual &&
+	git -C clone-file rev-parse refs/bundles/heads/topic >actual &&
 	git -C clone-from rev-parse topic >expect &&
 	test_cmp expect actual
 '
@@ -173,12 +173,12 @@ test_expect_success 'clone bundle list (file, no heuristic)' '
 	git -C clone-list-file cat-file --batch-check <oids &&
 
 	git -C clone-list-file for-each-ref --format="%(refname)" >refs &&
-	grep "refs/bundles/" refs >actual &&
+	grep "refs/bundles/heads/" refs >actual &&
 	cat >expect <<-\EOF &&
-	refs/bundles/base
-	refs/bundles/left
-	refs/bundles/merge
-	refs/bundles/right
+	refs/bundles/heads/base
+	refs/bundles/heads/left
+	refs/bundles/heads/merge
+	refs/bundles/heads/right
 	EOF
 	test_cmp expect actual
 '
@@ -220,10 +220,10 @@ test_expect_success 'clone bundle list (file, all mode, some failures)' '
 	git -C clone-all-some cat-file --batch-check <oids &&
 
 	git -C clone-all-some for-each-ref --format="%(refname)" >refs &&
-	grep "refs/bundles/" refs >actual &&
+	grep "refs/bundles/heads/" refs >actual &&
 	cat >expect <<-\EOF &&
-	refs/bundles/base
-	refs/bundles/left
+	refs/bundles/heads/base
+	refs/bundles/heads/left
 	EOF
 	test_cmp expect actual
 '
@@ -253,7 +253,7 @@ test_expect_success 'clone bundle list (file, all mode, all failures)' '
 	git -C clone-all-fail cat-file --batch-check <oids &&
 
 	git -C clone-all-fail for-each-ref --format="%(refname)" >refs &&
-	! grep "refs/bundles/" refs
+	! grep "refs/bundles/heads/" refs
 '
 
 test_expect_success 'clone bundle list (file, any mode)' '
@@ -282,9 +282,9 @@ test_expect_success 'clone bundle list (file, any mode)' '
 	git -C clone-any-file cat-file --batch-check <oids &&
 
 	git -C clone-any-file for-each-ref --format="%(refname)" >refs &&
-	grep "refs/bundles/" refs >actual &&
+	grep "refs/bundles/heads/" refs >actual &&
 	cat >expect <<-\EOF &&
-	refs/bundles/base
+	refs/bundles/heads/base
 	EOF
 	test_cmp expect actual
 '
@@ -313,7 +313,7 @@ test_expect_success 'clone bundle list (file, any mode, all failures)' '
 	git -C clone-any-fail cat-file --batch-check <oids &&
 
 	git -C clone-any-fail for-each-ref --format="%(refname)" >refs &&
-	! grep "refs/bundles/" refs
+	! grep "refs/bundles/heads/" refs
 '
 
 test_expect_success 'negotiation: bundle with part of wanted commits' '
@@ -322,10 +322,10 @@ test_expect_success 'negotiation: bundle with part of wanted commits' '
 	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 &&
-	test_write_lines refs/bundles/topic >expect &&
+	grep "refs/bundles/heads/" refs >actual &&
+	test_write_lines refs/bundles/heads/topic >expect &&
 	test_cmp expect actual &&
-	# Ensure that refs/bundles/topic are sent as "have".
+	# Ensure that refs/bundles/heads/topic are sent as "have".
 	tip=$(git -C clone-from rev-parse A) &&
 	test_grep "clone> have $tip" trace-packet.txt
 '
@@ -337,8 +337,8 @@ test_expect_success 'negotiation: bundle with all wanted commits' '
 		--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 &&
-	test_write_lines refs/bundles/topic >expect &&
+	grep "refs/bundles/heads/" refs >actual &&
+	test_write_lines refs/bundles/heads/topic >expect &&
 	test_cmp expect actual &&
 	# We already have all needed commits so no "want" needed.
 	test_grep ! "clone> want " trace-packet.txt
@@ -363,13 +363,13 @@ test_expect_success 'negotiation: bundle list (no heuristic)' '
 		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 &&
+	grep "refs/bundles/heads/" refs >actual &&
 	cat >expect <<-\EOF &&
-	refs/bundles/base
-	refs/bundles/left
+	refs/bundles/heads/base
+	refs/bundles/heads/left
 	EOF
 	test_cmp expect actual &&
-	tip=$(git -C nego-bundle-list-no-heuristic rev-parse refs/bundles/left) &&
+	tip=$(git -C nego-bundle-list-no-heuristic rev-parse refs/bundles/heads/left) &&
 	test_grep "clone> have $tip" trace-packet.txt
 '
 
@@ -395,13 +395,13 @@ test_expect_success 'negotiation: bundle list (creationToken)' '
 		clone-from nego-bundle-list-heuristic &&
 
 	git -C nego-bundle-list-heuristic for-each-ref --format="%(refname)" >refs &&
-	grep "refs/bundles/" refs >actual &&
+	grep "refs/bundles/heads/" refs >actual &&
 	cat >expect <<-\EOF &&
-	refs/bundles/base
-	refs/bundles/left
+	refs/bundles/heads/base
+	refs/bundles/heads/left
 	EOF
 	test_cmp expect actual &&
-	tip=$(git -C nego-bundle-list-heuristic rev-parse refs/bundles/left) &&
+	tip=$(git -C nego-bundle-list-heuristic rev-parse refs/bundles/heads/left) &&
 	test_grep "clone> have $tip" trace-packet.txt
 '
 
@@ -428,10 +428,10 @@ test_expect_success 'negotiation: bundle list with all wanted commits' '
 		clone-from nego-bundle-list-all &&
 
 	git -C nego-bundle-list-all for-each-ref --format="%(refname)" >refs &&
-	grep "refs/bundles/" refs >actual &&
+	grep "refs/bundles/heads/" refs >actual &&
 	cat >expect <<-\EOF &&
-	refs/bundles/base
-	refs/bundles/left
+	refs/bundles/heads/base
+	refs/bundles/heads/left
 	EOF
 	test_cmp expect actual &&
 	# We already have all needed commits so no "want" needed.
@@ -465,7 +465,7 @@ test_expect_success 'clone HTTP bundle' '
 
 	git clone --bundle-uri="$HTTPD_URL/B.bundle" \
 		"$HTTPD_URL/smart/fetch.git" clone-http &&
-	git -C clone-http rev-parse refs/bundles/topic >actual &&
+	git -C clone-http rev-parse refs/bundles/heads/topic >actual &&
 	git -C clone-from rev-parse topic >expect &&
 	test_cmp expect actual &&
 
@@ -476,7 +476,7 @@ test_expect_success 'clone HTTP bundle with non-default hash' '
 	test_when_finished "rm -rf clone-http-non-default-hash" &&
 	GIT_DEFAULT_HASH=sha256 git clone --bundle-uri="$HTTPD_URL/B.bundle" \
 		"$HTTPD_URL/smart/fetch.git" clone-http-non-default-hash &&
-	git -C clone-http-non-default-hash rev-parse refs/bundles/topic >actual &&
+	git -C clone-http-non-default-hash rev-parse refs/bundles/heads/topic >actual &&
 	git -C clone-from rev-parse topic >expect &&
 	test_cmp expect actual
 '
@@ -553,12 +553,12 @@ test_expect_success 'clone bundle list (HTTP, any mode)' '
 	git -C clone-any-http cat-file --batch-check <oids &&
 
 	git -C clone-list-file for-each-ref --format="%(refname)" >refs &&
-	grep "refs/bundles/" refs >actual &&
+	grep "refs/bundles/heads/" refs >actual &&
 	cat >expect <<-\EOF &&
-	refs/bundles/base
-	refs/bundles/left
-	refs/bundles/merge
-	refs/bundles/right
+	refs/bundles/heads/base
+	refs/bundles/heads/left
+	refs/bundles/heads/merge
+	refs/bundles/heads/right
 	EOF
 	test_cmp expect actual
 '
@@ -641,9 +641,9 @@ test_expect_success 'clone incomplete bundle list (http, creationToken)' '
 	test_cmp expect actual &&
 
 	# We now have only one bundle ref.
-	git -C clone-token-http for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+	git -C clone-token-http for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
 	cat >expect <<-\EOF &&
-	refs/bundles/base
+	refs/bundles/heads/base
 	EOF
 	test_cmp expect refs &&
 
@@ -679,13 +679,13 @@ test_expect_success 'clone incomplete bundle list (http, creationToken)' '
 	test_cmp expect actual &&
 
 	# We now have all bundle refs.
-	git -C clone-token-http for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+	git -C clone-token-http for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
 
 	cat >expect <<-\EOF &&
-	refs/bundles/base
-	refs/bundles/left
-	refs/bundles/merge
-	refs/bundles/right
+	refs/bundles/heads/base
+	refs/bundles/heads/left
+	refs/bundles/heads/merge
+	refs/bundles/heads/right
 	EOF
 	test_cmp expect refs
 '
@@ -721,9 +721,9 @@ test_expect_success 'http clone with bundle.heuristic creates fetch.bundleURI' '
 	test_cmp expect actual &&
 
 	# only received base ref from bundle-1
-	git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+	git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
 	cat >expect <<-\EOF &&
-	refs/bundles/base
+	refs/bundles/heads/base
 	EOF
 	test_cmp expect refs &&
 
@@ -749,10 +749,10 @@ test_expect_success 'http clone with bundle.heuristic creates fetch.bundleURI' '
 	test_cmp expect actual &&
 
 	# received left from bundle-2
-	git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+	git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
 	cat >expect <<-\EOF &&
-	refs/bundles/base
-	refs/bundles/left
+	refs/bundles/heads/base
+	refs/bundles/heads/left
 	EOF
 	test_cmp expect refs &&
 
@@ -795,12 +795,12 @@ test_expect_success 'http clone with bundle.heuristic creates fetch.bundleURI' '
 
 	# received merge ref from bundle-4, but right is missing
 	# because we did not download bundle-3.
-	git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+	git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
 
 	cat >expect <<-\EOF &&
-	refs/bundles/base
-	refs/bundles/left
-	refs/bundles/merge
+	refs/bundles/heads/base
+	refs/bundles/heads/left
+	refs/bundles/heads/merge
 	EOF
 	test_cmp expect refs &&
 
@@ -862,7 +862,7 @@ test_expect_success 'creationToken heuristic with failed downloads (clone)' '
 	test_cmp expect actual &&
 
 	# All bundles failed to unbundle
-	git -C download-1 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+	git -C download-1 for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
 	test_must_be_empty refs &&
 
 	# Case 2: middle bundle does not exist, only two bundles can unbundle
@@ -909,10 +909,10 @@ test_expect_success 'creationToken heuristic with failed downloads (clone)' '
 	test_cmp expect actual &&
 
 	# bundle-1 and bundle-3 could unbundle, but bundle-4 could not
-	git -C download-2 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+	git -C download-2 for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
 	cat >expect <<-EOF &&
-	refs/bundles/base
-	refs/bundles/right
+	refs/bundles/heads/base
+	refs/bundles/heads/right
 	EOF
 	test_cmp expect refs &&
 
@@ -961,11 +961,11 @@ test_expect_success 'creationToken heuristic with failed downloads (clone)' '
 	test_cmp expect actual &&
 
 	# fake.bundle did not unbundle, but the others did.
-	git -C download-3 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+	git -C download-3 for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
 	cat >expect <<-EOF &&
-	refs/bundles/base
-	refs/bundles/left
-	refs/bundles/right
+	refs/bundles/heads/base
+	refs/bundles/heads/left
+	refs/bundles/heads/right
 	EOF
 	test_cmp expect refs
 '
@@ -1083,15 +1083,15 @@ test_expect_success 'creationToken heuristic with failed downloads (fetch)' '
 	test_cmp expect actual &&
 
 	# Check which bundles have unbundled by refs
-	git -C fetch-1 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+	git -C fetch-1 for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
 	cat >expect <<-EOF &&
-	refs/bundles/base
-	refs/bundles/left
-	refs/bundles/lefter
-	refs/bundles/merge
-	refs/bundles/right
-	refs/bundles/righter
-	refs/bundles/top
+	refs/bundles/heads/base
+	refs/bundles/heads/left
+	refs/bundles/heads/lefter
+	refs/bundles/heads/merge
+	refs/bundles/heads/right
+	refs/bundles/heads/righter
+	refs/bundles/heads/top
 	EOF
 	test_cmp expect refs &&
 
@@ -1144,12 +1144,12 @@ test_expect_success 'creationToken heuristic with failed downloads (fetch)' '
 	test_cmp expect actual &&
 
 	# Check which bundles have unbundled by refs
-	git -C fetch-2 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+	git -C fetch-2 for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
 	cat >expect <<-EOF &&
-	refs/bundles/base
-	refs/bundles/left
-	refs/bundles/merge
-	refs/bundles/right
+	refs/bundles/heads/base
+	refs/bundles/heads/left
+	refs/bundles/heads/merge
+	refs/bundles/heads/right
 	EOF
 	test_cmp expect refs &&
 
@@ -1204,13 +1204,13 @@ test_expect_success 'creationToken heuristic with failed downloads (fetch)' '
 	test_cmp expect actual &&
 
 	# Check which bundles have unbundled by refs
-	git -C fetch-3 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+	git -C fetch-3 for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
 	cat >expect <<-EOF &&
-	refs/bundles/base
-	refs/bundles/left
-	refs/bundles/lefter
-	refs/bundles/right
-	refs/bundles/righter
+	refs/bundles/heads/base
+	refs/bundles/heads/left
+	refs/bundles/heads/lefter
+	refs/bundles/heads/right
+	refs/bundles/heads/righter
 	EOF
 	test_cmp expect refs
 '
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v3 2/2] bundle-uri: add test for bundle-uri clones with tags
  2025-03-18 15:36   ` [PATCH v3 0/2] " Scott Chacon via GitGitGadget
  2025-03-18 15:36     ` [PATCH v3 1/2] " Scott Chacon via GitGitGadget
@ 2025-03-18 15:36     ` Scott Chacon via GitGitGadget
  2025-03-19 10:33       ` Phillip Wood
  1 sibling, 1 reply; 19+ messages in thread
From: Scott Chacon via GitGitGadget @ 2025-03-18 15:36 UTC (permalink / raw)
  To: git; +Cc: Derrick Stolee, Scott Chacon, Scott Chacon

From: Scott Chacon <schacon@gmail.com>

The change to the bundle-uri unbundling refspec now includes tags, so this
adds a simple test to make sure that tags in a bundle are properly added to
the cloned repository and will be included in ref negotiation with the
subsequent fetch.

Signed-off-by: Scott Chacon <schacon@gmail.com>
---
 t/t5558-clone-bundle-uri.sh | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/t/t5558-clone-bundle-uri.sh b/t/t5558-clone-bundle-uri.sh
index 33a7009e9a2..b1276ba295c 100755
--- a/t/t5558-clone-bundle-uri.sh
+++ b/t/t5558-clone-bundle-uri.sh
@@ -107,6 +107,37 @@ test_expect_success 'clone with file:// bundle' '
 	test_cmp expect actual
 '
 
+test_expect_success 'create bundle with tags' '
+	git init clone-from-tags &&
+	(
+		cd clone-from-tags &&
+		git checkout -b base &&
+		git checkout -b topic &&
+
+		test_commit A &&
+		git tag tag-A &&
+		git checkout -b base &&
+		git branch -d topic &&
+		test_commit B &&
+
+		git bundle create ALL.bundle --all &&
+		git bundle verify ALL.bundle
+	)
+'
+
+test_expect_success 'clone with tags bundle' '
+	git clone --bundle-uri="clone-from-tags/ALL.bundle" \
+		clone-from-tags clone-tags-path &&
+	git -C clone-tags-path for-each-ref --format="%(refname)" >refs &&
+	grep "refs/bundles/tags/" refs >actual &&
+	cat >expect <<-\EOF &&
+	refs/bundles/tags/A
+	refs/bundles/tags/B
+	refs/bundles/tags/tag-A
+	EOF
+	test_cmp expect actual
+'
+
 # To get interesting tests for bundle lists, we need to construct a
 # somewhat-interesting commit history.
 #
-- 
gitgitgadget

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH v3 1/2] bundle-uri: copy all bundle references ino the refs/bundle space
  2025-03-18 15:36     ` [PATCH v3 1/2] " Scott Chacon via GitGitGadget
@ 2025-03-19 10:24       ` Phillip Wood
  0 siblings, 0 replies; 19+ messages in thread
From: Phillip Wood @ 2025-03-19 10:24 UTC (permalink / raw)
  To: Scott Chacon via GitGitGadget, git; +Cc: Derrick Stolee, Scott Chacon

Hi Scott

On 18/03/2025 15:36, Scott Chacon via GitGitGadget wrote:
> From: Scott Chacon <schacon@gmail.com>
> 
> When downloading bundles via the bundle-uri functionality, we only copy the
> references from refs/heads into the refs/bundle space. I'm not sure why this
> refspec is hardcoded to be so limited, but it makes the ref negotiation on
> the subsequent fetch suboptimal, since it won't use objects that are
> referenced outside of the current heads of the bundled repository.
> 
> This change to copy everything in refs/ in the bundle to refs/bundles/
> significantly helps the subsequent fetch, since nearly all the references
> are now included in the negotiation.
> 
> The update to the bundle-uri unbundling refspec puts all the heads from a
> bundle file into refs/bundle/heads instead of directly into refs/bundle/ so
> the tests also need to be updated to look in the new heirarchy.

I've not used the bundle-uri feature but this change sounds sensible. 
The refspec that's changed here is documented in 
Documentation/technical/bundle-uri.adoc (see item 2 in the list under 
"Cloning with Bundle URIs") so we should update that. That document as 
says this refspec is configurable but it seems that was never 
implemented so it would be nice to fix that at the same time.

Thanks

Phillip

> Signed-off-by: Scott Chacon <schacon@gmail.com>
> ---
>   bundle-uri.c                |   2 +-
>   t/t5558-clone-bundle-uri.sh | 172 ++++++++++++++++++------------------
>   2 files changed, 87 insertions(+), 87 deletions(-)
> 
> diff --git a/bundle-uri.c b/bundle-uri.c
> index 744257c49c1..3371d56f4ce 100644
> --- a/bundle-uri.c
> +++ b/bundle-uri.c
> @@ -403,7 +403,7 @@ static int unbundle_from_file(struct repository *r, const char *file)
>   		const char *branch_name;
>   		int has_old;
>   
> -		if (!skip_prefix(refname->string, "refs/heads/", &branch_name))
> +		if (!skip_prefix(refname->string, "refs/", &branch_name))
>   			continue;
>   
>   		strbuf_setlen(&bundle_ref, bundle_prefix_len);
> diff --git a/t/t5558-clone-bundle-uri.sh b/t/t5558-clone-bundle-uri.sh
> index 3816ed5058d..33a7009e9a2 100755
> --- a/t/t5558-clone-bundle-uri.sh
> +++ b/t/t5558-clone-bundle-uri.sh
> @@ -58,7 +58,7 @@ test_expect_success 'create bundle' '
>   test_expect_success 'clone with path bundle' '
>   	git clone --bundle-uri="clone-from/B.bundle" \
>   		clone-from clone-path &&
> -	git -C clone-path rev-parse refs/bundles/topic >actual &&
> +	git -C clone-path rev-parse refs/bundles/heads/topic >actual &&
>   	git -C clone-from rev-parse topic >expect &&
>   	test_cmp expect actual
>   '
> @@ -68,9 +68,9 @@ test_expect_success 'clone with bundle that has bad header' '
>   	git clone --bundle-uri="clone-from/bad-header.bundle" \
>   		clone-from clone-bad-header 2>err &&
>   	commit_b=$(git -C clone-from rev-parse B) &&
> -	test_grep "trying to write ref '\''refs/bundles/topic'\'' with nonexistent object $commit_b" err &&
> +	test_grep "trying to write ref '\''refs/bundles/heads/topic'\'' with nonexistent object $commit_b" err &&
>   	git -C clone-bad-header for-each-ref --format="%(refname)" >refs &&
> -	test_grep ! "refs/bundles/" refs
> +	test_grep ! "refs/bundles/heads/" refs
>   '
>   
>   test_expect_success 'clone with bundle that has bad object' '
> @@ -78,8 +78,8 @@ test_expect_success 'clone with bundle that has bad object' '
>   	git clone --bundle-uri="clone-from/bad-object.bundle" \
>   		clone-from clone-bad-object-no-fsck &&
>   	git -C clone-bad-object-no-fsck for-each-ref --format="%(refname)" >refs &&
> -	grep "refs/bundles/" refs >actual &&
> -	test_write_lines refs/bundles/bad >expect &&
> +	grep "refs/bundles/heads/" refs >actual &&
> +	test_write_lines refs/bundles/heads/bad >expect &&
>   	test_cmp expect actual &&
>   
>   	# Unbundle fails with fsckObjects set true, but clone can still proceed.
> @@ -87,14 +87,14 @@ test_expect_success 'clone with bundle that has bad object' '
>   		clone-from clone-bad-object-fsck 2>err &&
>   	test_grep "missingEmail" err &&
>   	git -C clone-bad-object-fsck for-each-ref --format="%(refname)" >refs &&
> -	test_grep ! "refs/bundles/" refs
> +	test_grep ! "refs/bundles/heads/" refs
>   '
>   
>   test_expect_success 'clone with path bundle and non-default hash' '
>   	test_when_finished "rm -rf clone-path-non-default-hash" &&
>   	GIT_DEFAULT_HASH=sha256 git clone --bundle-uri="clone-from/B.bundle" \
>   		clone-from clone-path-non-default-hash &&
> -	git -C clone-path-non-default-hash rev-parse refs/bundles/topic >actual &&
> +	git -C clone-path-non-default-hash rev-parse refs/bundles/heads/topic >actual &&
>   	git -C clone-from rev-parse topic >expect &&
>   	test_cmp expect actual
>   '
> @@ -102,7 +102,7 @@ test_expect_success 'clone with path bundle and non-default hash' '
>   test_expect_success 'clone with file:// bundle' '
>   	git clone --bundle-uri="file://$(pwd)/clone-from/B.bundle" \
>   		clone-from clone-file &&
> -	git -C clone-file rev-parse refs/bundles/topic >actual &&
> +	git -C clone-file rev-parse refs/bundles/heads/topic >actual &&
>   	git -C clone-from rev-parse topic >expect &&
>   	test_cmp expect actual
>   '
> @@ -173,12 +173,12 @@ test_expect_success 'clone bundle list (file, no heuristic)' '
>   	git -C clone-list-file cat-file --batch-check <oids &&
>   
>   	git -C clone-list-file for-each-ref --format="%(refname)" >refs &&
> -	grep "refs/bundles/" refs >actual &&
> +	grep "refs/bundles/heads/" refs >actual &&
>   	cat >expect <<-\EOF &&
> -	refs/bundles/base
> -	refs/bundles/left
> -	refs/bundles/merge
> -	refs/bundles/right
> +	refs/bundles/heads/base
> +	refs/bundles/heads/left
> +	refs/bundles/heads/merge
> +	refs/bundles/heads/right
>   	EOF
>   	test_cmp expect actual
>   '
> @@ -220,10 +220,10 @@ test_expect_success 'clone bundle list (file, all mode, some failures)' '
>   	git -C clone-all-some cat-file --batch-check <oids &&
>   
>   	git -C clone-all-some for-each-ref --format="%(refname)" >refs &&
> -	grep "refs/bundles/" refs >actual &&
> +	grep "refs/bundles/heads/" refs >actual &&
>   	cat >expect <<-\EOF &&
> -	refs/bundles/base
> -	refs/bundles/left
> +	refs/bundles/heads/base
> +	refs/bundles/heads/left
>   	EOF
>   	test_cmp expect actual
>   '
> @@ -253,7 +253,7 @@ test_expect_success 'clone bundle list (file, all mode, all failures)' '
>   	git -C clone-all-fail cat-file --batch-check <oids &&
>   
>   	git -C clone-all-fail for-each-ref --format="%(refname)" >refs &&
> -	! grep "refs/bundles/" refs
> +	! grep "refs/bundles/heads/" refs
>   '
>   
>   test_expect_success 'clone bundle list (file, any mode)' '
> @@ -282,9 +282,9 @@ test_expect_success 'clone bundle list (file, any mode)' '
>   	git -C clone-any-file cat-file --batch-check <oids &&
>   
>   	git -C clone-any-file for-each-ref --format="%(refname)" >refs &&
> -	grep "refs/bundles/" refs >actual &&
> +	grep "refs/bundles/heads/" refs >actual &&
>   	cat >expect <<-\EOF &&
> -	refs/bundles/base
> +	refs/bundles/heads/base
>   	EOF
>   	test_cmp expect actual
>   '
> @@ -313,7 +313,7 @@ test_expect_success 'clone bundle list (file, any mode, all failures)' '
>   	git -C clone-any-fail cat-file --batch-check <oids &&
>   
>   	git -C clone-any-fail for-each-ref --format="%(refname)" >refs &&
> -	! grep "refs/bundles/" refs
> +	! grep "refs/bundles/heads/" refs
>   '
>   
>   test_expect_success 'negotiation: bundle with part of wanted commits' '
> @@ -322,10 +322,10 @@ test_expect_success 'negotiation: bundle with part of wanted commits' '
>   	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 &&
> -	test_write_lines refs/bundles/topic >expect &&
> +	grep "refs/bundles/heads/" refs >actual &&
> +	test_write_lines refs/bundles/heads/topic >expect &&
>   	test_cmp expect actual &&
> -	# Ensure that refs/bundles/topic are sent as "have".
> +	# Ensure that refs/bundles/heads/topic are sent as "have".
>   	tip=$(git -C clone-from rev-parse A) &&
>   	test_grep "clone> have $tip" trace-packet.txt
>   '
> @@ -337,8 +337,8 @@ test_expect_success 'negotiation: bundle with all wanted commits' '
>   		--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 &&
> -	test_write_lines refs/bundles/topic >expect &&
> +	grep "refs/bundles/heads/" refs >actual &&
> +	test_write_lines refs/bundles/heads/topic >expect &&
>   	test_cmp expect actual &&
>   	# We already have all needed commits so no "want" needed.
>   	test_grep ! "clone> want " trace-packet.txt
> @@ -363,13 +363,13 @@ test_expect_success 'negotiation: bundle list (no heuristic)' '
>   		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 &&
> +	grep "refs/bundles/heads/" refs >actual &&
>   	cat >expect <<-\EOF &&
> -	refs/bundles/base
> -	refs/bundles/left
> +	refs/bundles/heads/base
> +	refs/bundles/heads/left
>   	EOF
>   	test_cmp expect actual &&
> -	tip=$(git -C nego-bundle-list-no-heuristic rev-parse refs/bundles/left) &&
> +	tip=$(git -C nego-bundle-list-no-heuristic rev-parse refs/bundles/heads/left) &&
>   	test_grep "clone> have $tip" trace-packet.txt
>   '
>   
> @@ -395,13 +395,13 @@ test_expect_success 'negotiation: bundle list (creationToken)' '
>   		clone-from nego-bundle-list-heuristic &&
>   
>   	git -C nego-bundle-list-heuristic for-each-ref --format="%(refname)" >refs &&
> -	grep "refs/bundles/" refs >actual &&
> +	grep "refs/bundles/heads/" refs >actual &&
>   	cat >expect <<-\EOF &&
> -	refs/bundles/base
> -	refs/bundles/left
> +	refs/bundles/heads/base
> +	refs/bundles/heads/left
>   	EOF
>   	test_cmp expect actual &&
> -	tip=$(git -C nego-bundle-list-heuristic rev-parse refs/bundles/left) &&
> +	tip=$(git -C nego-bundle-list-heuristic rev-parse refs/bundles/heads/left) &&
>   	test_grep "clone> have $tip" trace-packet.txt
>   '
>   
> @@ -428,10 +428,10 @@ test_expect_success 'negotiation: bundle list with all wanted commits' '
>   		clone-from nego-bundle-list-all &&
>   
>   	git -C nego-bundle-list-all for-each-ref --format="%(refname)" >refs &&
> -	grep "refs/bundles/" refs >actual &&
> +	grep "refs/bundles/heads/" refs >actual &&
>   	cat >expect <<-\EOF &&
> -	refs/bundles/base
> -	refs/bundles/left
> +	refs/bundles/heads/base
> +	refs/bundles/heads/left
>   	EOF
>   	test_cmp expect actual &&
>   	# We already have all needed commits so no "want" needed.
> @@ -465,7 +465,7 @@ test_expect_success 'clone HTTP bundle' '
>   
>   	git clone --bundle-uri="$HTTPD_URL/B.bundle" \
>   		"$HTTPD_URL/smart/fetch.git" clone-http &&
> -	git -C clone-http rev-parse refs/bundles/topic >actual &&
> +	git -C clone-http rev-parse refs/bundles/heads/topic >actual &&
>   	git -C clone-from rev-parse topic >expect &&
>   	test_cmp expect actual &&
>   
> @@ -476,7 +476,7 @@ test_expect_success 'clone HTTP bundle with non-default hash' '
>   	test_when_finished "rm -rf clone-http-non-default-hash" &&
>   	GIT_DEFAULT_HASH=sha256 git clone --bundle-uri="$HTTPD_URL/B.bundle" \
>   		"$HTTPD_URL/smart/fetch.git" clone-http-non-default-hash &&
> -	git -C clone-http-non-default-hash rev-parse refs/bundles/topic >actual &&
> +	git -C clone-http-non-default-hash rev-parse refs/bundles/heads/topic >actual &&
>   	git -C clone-from rev-parse topic >expect &&
>   	test_cmp expect actual
>   '
> @@ -553,12 +553,12 @@ test_expect_success 'clone bundle list (HTTP, any mode)' '
>   	git -C clone-any-http cat-file --batch-check <oids &&
>   
>   	git -C clone-list-file for-each-ref --format="%(refname)" >refs &&
> -	grep "refs/bundles/" refs >actual &&
> +	grep "refs/bundles/heads/" refs >actual &&
>   	cat >expect <<-\EOF &&
> -	refs/bundles/base
> -	refs/bundles/left
> -	refs/bundles/merge
> -	refs/bundles/right
> +	refs/bundles/heads/base
> +	refs/bundles/heads/left
> +	refs/bundles/heads/merge
> +	refs/bundles/heads/right
>   	EOF
>   	test_cmp expect actual
>   '
> @@ -641,9 +641,9 @@ test_expect_success 'clone incomplete bundle list (http, creationToken)' '
>   	test_cmp expect actual &&
>   
>   	# We now have only one bundle ref.
> -	git -C clone-token-http for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
> +	git -C clone-token-http for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
>   	cat >expect <<-\EOF &&
> -	refs/bundles/base
> +	refs/bundles/heads/base
>   	EOF
>   	test_cmp expect refs &&
>   
> @@ -679,13 +679,13 @@ test_expect_success 'clone incomplete bundle list (http, creationToken)' '
>   	test_cmp expect actual &&
>   
>   	# We now have all bundle refs.
> -	git -C clone-token-http for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
> +	git -C clone-token-http for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
>   
>   	cat >expect <<-\EOF &&
> -	refs/bundles/base
> -	refs/bundles/left
> -	refs/bundles/merge
> -	refs/bundles/right
> +	refs/bundles/heads/base
> +	refs/bundles/heads/left
> +	refs/bundles/heads/merge
> +	refs/bundles/heads/right
>   	EOF
>   	test_cmp expect refs
>   '
> @@ -721,9 +721,9 @@ test_expect_success 'http clone with bundle.heuristic creates fetch.bundleURI' '
>   	test_cmp expect actual &&
>   
>   	# only received base ref from bundle-1
> -	git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
> +	git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
>   	cat >expect <<-\EOF &&
> -	refs/bundles/base
> +	refs/bundles/heads/base
>   	EOF
>   	test_cmp expect refs &&
>   
> @@ -749,10 +749,10 @@ test_expect_success 'http clone with bundle.heuristic creates fetch.bundleURI' '
>   	test_cmp expect actual &&
>   
>   	# received left from bundle-2
> -	git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
> +	git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
>   	cat >expect <<-\EOF &&
> -	refs/bundles/base
> -	refs/bundles/left
> +	refs/bundles/heads/base
> +	refs/bundles/heads/left
>   	EOF
>   	test_cmp expect refs &&
>   
> @@ -795,12 +795,12 @@ test_expect_success 'http clone with bundle.heuristic creates fetch.bundleURI' '
>   
>   	# received merge ref from bundle-4, but right is missing
>   	# because we did not download bundle-3.
> -	git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
> +	git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
>   
>   	cat >expect <<-\EOF &&
> -	refs/bundles/base
> -	refs/bundles/left
> -	refs/bundles/merge
> +	refs/bundles/heads/base
> +	refs/bundles/heads/left
> +	refs/bundles/heads/merge
>   	EOF
>   	test_cmp expect refs &&
>   
> @@ -862,7 +862,7 @@ test_expect_success 'creationToken heuristic with failed downloads (clone)' '
>   	test_cmp expect actual &&
>   
>   	# All bundles failed to unbundle
> -	git -C download-1 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
> +	git -C download-1 for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
>   	test_must_be_empty refs &&
>   
>   	# Case 2: middle bundle does not exist, only two bundles can unbundle
> @@ -909,10 +909,10 @@ test_expect_success 'creationToken heuristic with failed downloads (clone)' '
>   	test_cmp expect actual &&
>   
>   	# bundle-1 and bundle-3 could unbundle, but bundle-4 could not
> -	git -C download-2 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
> +	git -C download-2 for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
>   	cat >expect <<-EOF &&
> -	refs/bundles/base
> -	refs/bundles/right
> +	refs/bundles/heads/base
> +	refs/bundles/heads/right
>   	EOF
>   	test_cmp expect refs &&
>   
> @@ -961,11 +961,11 @@ test_expect_success 'creationToken heuristic with failed downloads (clone)' '
>   	test_cmp expect actual &&
>   
>   	# fake.bundle did not unbundle, but the others did.
> -	git -C download-3 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
> +	git -C download-3 for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
>   	cat >expect <<-EOF &&
> -	refs/bundles/base
> -	refs/bundles/left
> -	refs/bundles/right
> +	refs/bundles/heads/base
> +	refs/bundles/heads/left
> +	refs/bundles/heads/right
>   	EOF
>   	test_cmp expect refs
>   '
> @@ -1083,15 +1083,15 @@ test_expect_success 'creationToken heuristic with failed downloads (fetch)' '
>   	test_cmp expect actual &&
>   
>   	# Check which bundles have unbundled by refs
> -	git -C fetch-1 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
> +	git -C fetch-1 for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
>   	cat >expect <<-EOF &&
> -	refs/bundles/base
> -	refs/bundles/left
> -	refs/bundles/lefter
> -	refs/bundles/merge
> -	refs/bundles/right
> -	refs/bundles/righter
> -	refs/bundles/top
> +	refs/bundles/heads/base
> +	refs/bundles/heads/left
> +	refs/bundles/heads/lefter
> +	refs/bundles/heads/merge
> +	refs/bundles/heads/right
> +	refs/bundles/heads/righter
> +	refs/bundles/heads/top
>   	EOF
>   	test_cmp expect refs &&
>   
> @@ -1144,12 +1144,12 @@ test_expect_success 'creationToken heuristic with failed downloads (fetch)' '
>   	test_cmp expect actual &&
>   
>   	# Check which bundles have unbundled by refs
> -	git -C fetch-2 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
> +	git -C fetch-2 for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
>   	cat >expect <<-EOF &&
> -	refs/bundles/base
> -	refs/bundles/left
> -	refs/bundles/merge
> -	refs/bundles/right
> +	refs/bundles/heads/base
> +	refs/bundles/heads/left
> +	refs/bundles/heads/merge
> +	refs/bundles/heads/right
>   	EOF
>   	test_cmp expect refs &&
>   
> @@ -1204,13 +1204,13 @@ test_expect_success 'creationToken heuristic with failed downloads (fetch)' '
>   	test_cmp expect actual &&
>   
>   	# Check which bundles have unbundled by refs
> -	git -C fetch-3 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
> +	git -C fetch-3 for-each-ref --format="%(refname)" "refs/bundles/heads/*" >refs &&
>   	cat >expect <<-EOF &&
> -	refs/bundles/base
> -	refs/bundles/left
> -	refs/bundles/lefter
> -	refs/bundles/right
> -	refs/bundles/righter
> +	refs/bundles/heads/base
> +	refs/bundles/heads/left
> +	refs/bundles/heads/lefter
> +	refs/bundles/heads/right
> +	refs/bundles/heads/righter
>   	EOF
>   	test_cmp expect refs
>   '


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v3 2/2] bundle-uri: add test for bundle-uri clones with tags
  2025-03-18 15:36     ` [PATCH v3 2/2] bundle-uri: add test for bundle-uri clones with tags Scott Chacon via GitGitGadget
@ 2025-03-19 10:33       ` Phillip Wood
  2025-03-19 17:50         ` Taylor Blau
  2025-03-21  6:31         ` Junio C Hamano
  0 siblings, 2 replies; 19+ messages in thread
From: Phillip Wood @ 2025-03-19 10:33 UTC (permalink / raw)
  To: Scott Chacon via GitGitGadget, git; +Cc: Derrick Stolee, Scott Chacon

Hi Scott

On 18/03/2025 15:36, Scott Chacon via GitGitGadget wrote:
> From: Scott Chacon <schacon@gmail.com>
> 
> +test_expect_success 'clone with tags bundle' '
> +	git clone --bundle-uri="clone-from-tags/ALL.bundle" \
> +		clone-from-tags clone-tags-path &&
> +	git -C clone-tags-path for-each-ref --format="%(refname)" >refs &&
> +	grep "refs/bundles/tags/" refs >actual &&

Thanks for adding this test. Calling "git for-each-ref" followed by 
"grep" follows the pattern of the existing tests but I'm not sure why 
they don't just pass the pattern to "for-each-ref" and avoid the extra 
process.

Do we want to just test for tags or are we really interested to see all 
the bundle refs created when cloning? This applies to the previous patch 
as well - we obviously need to change the expected output but I'm not 
sure changing the ref pattern is necessarily a good idea. After all the 
point of this series is to create refs under refs/bundles for all the 
refs in the bundle.

Best Wishes

Phillip

> +	cat >expect <<-\EOF &&
> +	refs/bundles/tags/A
> +	refs/bundles/tags/B
> +	refs/bundles/tags/tag-A
> +	EOF
> +	test_cmp expect actual
> +'
> +
>   # To get interesting tests for bundle lists, we need to construct a
>   # somewhat-interesting commit history.
>   #


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v3 2/2] bundle-uri: add test for bundle-uri clones with tags
  2025-03-19 10:33       ` Phillip Wood
@ 2025-03-19 17:50         ` Taylor Blau
  2025-04-14 12:19           ` Toon Claes
  2025-03-21  6:31         ` Junio C Hamano
  1 sibling, 1 reply; 19+ messages in thread
From: Taylor Blau @ 2025-03-19 17:50 UTC (permalink / raw)
  To: phillip.wood
  Cc: Scott Chacon via GitGitGadget, git, Derrick Stolee, Scott Chacon

On Wed, Mar 19, 2025 at 10:33:48AM +0000, Phillip Wood wrote:
> Hi Scott
>
> On 18/03/2025 15:36, Scott Chacon via GitGitGadget wrote:
> > From: Scott Chacon <schacon@gmail.com>
> >
> > +test_expect_success 'clone with tags bundle' '
> > +	git clone --bundle-uri="clone-from-tags/ALL.bundle" \
> > +		clone-from-tags clone-tags-path &&
> > +	git -C clone-tags-path for-each-ref --format="%(refname)" >refs &&
> > +	grep "refs/bundles/tags/" refs >actual &&
>
> Thanks for adding this test. Calling "git for-each-ref" followed by "grep"
> follows the pattern of the existing tests but I'm not sure why they don't
> just pass the pattern to "for-each-ref" and avoid the extra process.

Indeed.

> Do we want to just test for tags or are we really interested to see all the
> bundle refs created when cloning? This applies to the previous patch as well
> - we obviously need to change the expected output but I'm not sure changing
> the ref pattern is necessarily a good idea. After all the point of this
> series is to create refs under refs/bundles for all the refs in the bundle.

I think we should be testing that all of the refs we expect to have made
it over actually did so. This diff (applied on top of your series) does
that:

--- 8< ---
diff --git a/t/t5558-clone-bundle-uri.sh b/t/t5558-clone-bundle-uri.sh
index b1276ba295..9b211a626b 100755
--- a/t/t5558-clone-bundle-uri.sh
+++ b/t/t5558-clone-bundle-uri.sh
@@ -128,13 +128,12 @@ test_expect_success 'create bundle with tags' '
 test_expect_success 'clone with tags bundle' '
 	git clone --bundle-uri="clone-from-tags/ALL.bundle" \
 		clone-from-tags clone-tags-path &&
-	git -C clone-tags-path for-each-ref --format="%(refname)" >refs &&
-	grep "refs/bundles/tags/" refs >actual &&
-	cat >expect <<-\EOF &&
-	refs/bundles/tags/A
-	refs/bundles/tags/B
-	refs/bundles/tags/tag-A
-	EOF
+
+	git -C clone-from-tags for-each-ref --format="%(refname:lstrip=1)" \
+		>expect &&
+	git -C clone-tags-path for-each-ref --format="%(refname:lstrip=2)" \
+		refs/bundles >actual &&
+
 	test_cmp expect actual
 '
--- >8 ---

While writing the above, I wasn't quite sure how to follow the test
setup. It looks like it creates the following structure:

    $ git log --oneline --graph
    * d9df450 (HEAD -> base, tag: B) B
    * 0ddfaf1 (tag: tag-A, tag: A) A

, which we could do with just:

    test_commit A &&
    test_commit B

But even then, I don't think we really need to have more than one tag
here to exercise this functionality. So I think it would be fine to
simplify the test to just create a single tag, which a simple
"test_commit A" should do.

Thanks,
Taylor

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH v3 2/2] bundle-uri: add test for bundle-uri clones with tags
  2025-03-19 10:33       ` Phillip Wood
  2025-03-19 17:50         ` Taylor Blau
@ 2025-03-21  6:31         ` Junio C Hamano
  1 sibling, 0 replies; 19+ messages in thread
From: Junio C Hamano @ 2025-03-21  6:31 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Scott Chacon via GitGitGadget, git, Derrick Stolee, Scott Chacon

Phillip Wood <phillip.wood123@gmail.com> writes:

> Hi Scott
>
> On 18/03/2025 15:36, Scott Chacon via GitGitGadget wrote:
>> From: Scott Chacon <schacon@gmail.com>
>> +test_expect_success 'clone with tags bundle' '
>> +	git clone --bundle-uri="clone-from-tags/ALL.bundle" \
>> +		clone-from-tags clone-tags-path &&
>> +	git -C clone-tags-path for-each-ref --format="%(refname)" >refs &&
>> +	grep "refs/bundles/tags/" refs >actual &&
>
> Thanks for adding this test. Calling "git for-each-ref" followed by
> "grep" follows the pattern of the existing tests but I'm not sure why
> they don't just pass the pattern to "for-each-ref" and avoid the extra
> process.

Yup, if we really care about a single ref, we should just go in the
clone and check with 'git show-ref'.

> Do we want to just test for tags or are we really interested to see
> all the bundle refs created when cloning? This applies to the previous
> patch as well - we obviously need to change the expected output but
> I'm not sure changing the ref pattern is necessarily a good
> idea. After all the point of this series is to create refs under
> refs/bundles for all the refs in the bundle.

The really interesting case that the original behaviour could have
been "working around existing bugs" would be

 - prepare an annotated tag.

 - include that annotated tag in the bundle specified by the
   bundle-uri feature (whose SOLE purpose is to speed up the main
   part of the tranfer, WITHOUT affecting the resulting remote
   tracking refs and global tag namespace).

 - prepare a repository that uses that bundle as bundle-uri when
   getting cloned (let's call it the 'origin' repository).  It
   probably is convenient if you make this a repository  separate
   from where you prepared the bundle above.  Make sure that this
   repository does *not* (yet) have that annotated tag.

 - clone from the 'origin' repository with bundle-uri feature.  The
   annotated tag would be held under refs/bundles/tags/ hierarchy but
   that is *not* the interesting part.  Make sure that the annotated
   tag does *not* appear in refs/tags/ hierarchy of the clone, since
   it does not exist (yet) at the 'origin' repository.

 - Now add that annotated tag to the 'origin' repository.

 - fetch from the 'origin' repository again, with the default
   configuration (i.e. allowing "tags follow when commits they
   reference are fetched" feature to kick in).  As the annotated tag
   appears in refs/tags/ of the 'origin' repository, the commit
   pointed at by that annotated tag now appears in one of its
   branches, and the history leading to that commit (and possibly
   others) are transferred to refs/remotes/origin/* remote-tracking
   branches, the tag-following feature should kick in and copy the
   annotated tag in refs/tags/ hierarchy as well.

The interesting part to verify is that in the cloned repository the
annotated tag does not appear in refs/tags/ immediately after
cloneing, but does appear there after the 'origin' is updated to
have the tag under refs/tags/ and then fetch


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v3 2/2] bundle-uri: add test for bundle-uri clones with tags
  2025-03-19 17:50         ` Taylor Blau
@ 2025-04-14 12:19           ` Toon Claes
  0 siblings, 0 replies; 19+ messages in thread
From: Toon Claes @ 2025-04-14 12:19 UTC (permalink / raw)
  To: Scott Chacon via GitGitGadget
  Cc: Taylor Blau, phillip.wood, git, Derrick Stolee, Scott Chacon

Taylor Blau <me@ttaylorr.com> writes:

> I think we should be testing that all of the refs we expect to have made
> it over actually did so. This diff (applied on top of your series) does
> that:
>
> --- 8< ---
> diff --git a/t/t5558-clone-bundle-uri.sh b/t/t5558-clone-bundle-uri.sh
> index b1276ba295..9b211a626b 100755
> --- a/t/t5558-clone-bundle-uri.sh
> +++ b/t/t5558-clone-bundle-uri.sh
> @@ -128,13 +128,12 @@ test_expect_success 'create bundle with tags' '
>  test_expect_success 'clone with tags bundle' '
>  	git clone --bundle-uri="clone-from-tags/ALL.bundle" \
>  		clone-from-tags clone-tags-path &&
> -	git -C clone-tags-path for-each-ref --format="%(refname)" >refs &&
> -	grep "refs/bundles/tags/" refs >actual &&
> -	cat >expect <<-\EOF &&
> -	refs/bundles/tags/A
> -	refs/bundles/tags/B
> -	refs/bundles/tags/tag-A
> -	EOF
> +
> +	git -C clone-from-tags for-each-ref --format="%(refname:lstrip=1)" \
> +		>expect &&
> +	git -C clone-tags-path for-each-ref --format="%(refname:lstrip=2)" \
> +		refs/bundles >actual &&
> +
>  	test_cmp expect actual
>  '
> --- >8 ---
>
> While writing the above, I wasn't quite sure how to follow the test
> setup. It looks like it creates the following structure:
>
>     $ git log --oneline --graph
>     * d9df450 (HEAD -> base, tag: B) B
>     * 0ddfaf1 (tag: tag-A, tag: A) A
>
> , which we could do with just:
>
>     test_commit A &&
>     test_commit B
>
> But even then, I don't think we really need to have more than one tag
> here to exercise this functionality. So I think it would be fine to
> simplify the test to just create a single tag, which a simple
> "test_commit A" should do.

Hi Scott,

Are you planning to pick up this patch series again? I think it would be
really valuable to get this merged. The patch by Taylor above might be
worth integrating, other than that I think it should be good to go.

Let me know if I can provide any help.

-- 
Toon

^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2025-04-14 12:19 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-02-25 13:19 [PATCH] bundle-uri: copy all bundle references ino the refs/bundle space Scott Chacon via GitGitGadget
2025-02-25 18:14 ` Junio C Hamano
2025-02-25 23:36   ` Derrick Stolee
2025-03-01 10:23     ` Scott Chacon
2025-03-03 17:12       ` Junio C Hamano
2025-03-03 18:46         ` Derrick Stolee
2025-03-01 10:33 ` [PATCH v2 0/3] " Scott Chacon via GitGitGadget
2025-03-01 10:33   ` [PATCH v2 1/3] " Scott Chacon via GitGitGadget
2025-03-01 10:33   ` [PATCH v2 2/3] bundle-uri: update bundle clone tests with new refspec path Scott Chacon via GitGitGadget
2025-03-01 10:33   ` [PATCH v2 3/3] bundle-uri: add test for bundle-uri clones with tags Scott Chacon via GitGitGadget
2025-03-03 18:49   ` [PATCH v2 0/3] bundle-uri: copy all bundle references ino the refs/bundle space Derrick Stolee
2025-03-18 15:36   ` [PATCH v3 0/2] " Scott Chacon via GitGitGadget
2025-03-18 15:36     ` [PATCH v3 1/2] " Scott Chacon via GitGitGadget
2025-03-19 10:24       ` Phillip Wood
2025-03-18 15:36     ` [PATCH v3 2/2] bundle-uri: add test for bundle-uri clones with tags Scott Chacon via GitGitGadget
2025-03-19 10:33       ` Phillip Wood
2025-03-19 17:50         ` Taylor Blau
2025-04-14 12:19           ` Toon Claes
2025-03-21  6:31         ` Junio C Hamano

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).