Skip to content

fix: fetching and cloning with refspecs that are tags (in shallow clones)#2556

Draft
Sebastian Thiel (Byron) wants to merge 1 commit intomainfrom
shallow-clone-tag-refspecs
Draft

fix: fetching and cloning with refspecs that are tags (in shallow clones)#2556
Sebastian Thiel (Byron) wants to merge 1 commit intomainfrom
shallow-clone-tag-refspecs

Conversation

@Byron
Copy link
Copy Markdown
Member

@Byron Sebastian Thiel (Byron) commented Apr 29, 2026

Fixes #2554.

Tasks

  • refackiew

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:

+refs/heads/<tag>:refs/remotes/origin/<tag>

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:

/Users/byron/dev/github.com/git/git

For a non-shallow clone with --branch <tag>, Git keeps the normal wildcard fetch refspec:

+refs/heads/*:refs/remotes/origin/*

For a shallow clone with --depth 1 --branch <tag>, Git writes a tag-to-tag refspec instead:

+refs/tags/<tag>:refs/tags/<tag>

The fix follows that behavior: shallow tag clones fetch the selected tag into refs/tags/*, while shallow branch clones continue to fetch into refs/remotes/<remote>/*.

Changes

  • Resolve with_ref_name() against the remote refs when using the shallow single-ref clone optimization.
  • Use the resolved full ref name when constructing the shallow refspec.
  • Preserve branch behavior by mapping refs/heads/<name> to refs/remotes/<remote>/<name>.
  • Map non-branch refs, including tags, to themselves.
  • Extend the annotated tag clone test to cover both non-shallow and shallow clones in one test.
  • Assert that the shallow tag clone stores a tag refspec of the form:
+refs/tags/<tag>:refs/tags/<tag>

…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>`.

Extend the annotated tag clone test to cover both full and shallow
lones, and assert the shallow tag refspec shape.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Shallow clone with tag refspec fails: Category::LocalBranch assumes ref is a branch

2 participants