fix: fetching and cloning with refspecs that are tags (in shallow clones)#2556
Conversation
3928781 to
3abf658
Compare
3abf658 to
844a7f4
Compare
…nes) Fix shallow clone refspecs for explicit tag refs When a shallow clone was created with `with_ref_name()`, the clone setup treated the requested name as a branch and generated a refspec under `refs/heads/`. For tag names this produced an unmatched required mapping like `+refs/heads/<tag>:refs/remotes/origin/<tag>`. Resolve the requested ref name against the remote before constructing the shallow single-ref refspec. Branches continue to map to `refs/remotes/<remote>/*`, while tags and other non-branch refs map to themselves. Baseline Git behavior was checked with `/Users/byron/dev/github.com/git/git`: non-shallow `--branch <tag>` clones keep the normal branch wildcard fetch refspec, while shallow `--depth 1 --branch <tag>` clones store `+refs/tags/<tag>:refs/tags/<tag>`. Co-authored-by: Sebastian Thiel <[email protected]>
844a7f4 to
92c8130
Compare
There was a problem hiding this comment.
Pull request overview
Fixes shallow clones when PrepareFetch::with_shallow(...).with_ref_name(Some(<tag>)) is used by resolving the requested ref name against remote refs first, so the shallow single-ref refspec is constructed for the correct full ref (e.g. tags map to refs/tags/* instead of refs/heads/*). This aligns behavior with Git’s shallow --branch <tag> behavior and addresses #2554.
Changes:
- Resolve
with_ref_name()against the remote ref-map when using the shallow single-ref optimization, then use the resolved full ref name. - Adjust shallow refspec construction to map branches to
refs/remotes/<remote>/<name>and non-branch refs (including tags) to themselves. - Extend the annotated tag clone test to assert both full and shallow clone behavior, including persisted refspecs for shallow tag clones.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| gix/src/clone/fetch/mod.rs | Resolve shallow single-ref targets against remote refs and construct correct destination refspecs for tags vs branches. |
| gix/tests/gix/clone.rs | Expand annotated-tag clone test to cover shallow vs non-shallow behavior and assert shallow tag refspec persistence. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 92c8130d6d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "Codex (@codex) review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".
According to Codex, no priority system is needed here. Co-authored-by: Sebastian Thiel <[email protected]>
The new preflight `ref_map_by_ref()` call will implicitly add the tag refspec `refs/tags/*:refs/tags/*` based on the remote’s current `fetch_tags` setting, which can cause `ls-refs` to enumerate *all* tags just to resolve a single `ref_name`. This defeats the shallow single-ref optimization on repos with many tags. Consider temporarily setting `remote.fetch_tags` to `Tags::None` around this lookup (similar to the HEAD-lookup path below) so prefix filtering only requests the DWIM-expanded candidates for the requested name, rather than all tags. Co-authored-by: Sebastian Thiel <[email protected]>
Fixes #2554.
Tasks
Summary
Fix shallow clones with an explicit tag ref by resolving the requested ref name against the remote before constructing the shallow single-ref refspec.
This addresses #2554, where
PrepareFetch::with_shallow(...).with_ref_name(Some("tag"))incorrectly treated the requested name as a local branch and generated a refspec like:That refspec does not match tag-only refs and now fails once fetch refspec validation rejects unmatched required mappings.
Research
I checked baseline Git behavior using the local Git checkout at:
For a non-shallow clone with
--branch <tag>, Git keeps the normal wildcard fetch refspec:For a shallow clone with
--depth 1 --branch <tag>, Git writes a tag-to-tag refspec instead:The fix follows that behavior: shallow tag clones fetch the selected tag into
refs/tags/*, while shallow branch clones continue to fetch intorefs/remotes/<remote>/*.Changes
with_ref_name()against the remote refs when using the shallow single-ref clone optimization.refs/heads/<name>torefs/remotes/<remote>/<name>.