Skip to content

fix: shallow clone with tag ref_name now uses correct refspec#2553

Closed
sapatrjv wants to merge 1 commit into
GitoxideLabs:mainfrom
sapatrjv:fix-shallow-clone-tag-refspec
Closed

fix: shallow clone with tag ref_name now uses correct refspec#2553
sapatrjv wants to merge 1 commit into
GitoxideLabs:mainfrom
sapatrjv:fix-shallow-clone-tag-refspec

Conversation

@sapatrjv
Copy link
Copy Markdown

@sapatrjv sapatrjv commented Apr 28, 2026

Summary

Fixes a bug where shallow clones with a tag-based fail with:

\
None of the refspec(s) +refs/heads/v1.30.0:refs/remotes/origin/v1.30.0, ... matched any of the 23 refs on the remote
\\

Fix for issue: #2554

Root Cause

The shallow clone path unconditionally assumed ref_name was a branch via Category::LocalBranch.to_full_name(), generating refs/heads/* even when the remote only had refs/tags/*.

Fix

The shallow clone path now always connects to the remote to discover the actual ref type before constructing the refspec:

Testing

  • Added shallow_clone_with_tag_ref_name_uses_tag_refspec test
  • All 15 existing clone::blocking_io tests pass
  • Both blocking-network-client and sync-network-client feature builds compile cleanly

Related Issues

Copilot AI review requested due to automatic review settings April 28, 2026 20:46
@sapatrjv sapatrjv force-pushed the fix-shallow-clone-tag-refspec branch from f85cbbe to 653d073 Compare April 28, 2026 21:12
When performing a shallow clone with a tag-based ref_name (e.g.,
\�1.30.0\), the code unconditionally assumed the ref was a branch,
generating \
efs/heads/v1.30.0\ even when the remote only had
\
efs/tags/v1.30.0\. This caused the refspec to match nothing,
failing with a 'None of the refspec(s) matched' error.

Now the shallow clone path connects to the remote to discover what the
ref actually is before constructing the refspec. For tags, the refspec
maps as \+refs/tags/X:refs/tags/X\ (matching git's behavior). For
branches, the existing \+refs/heads/X:refs/remotes/origin/X\ mapping
is preserved.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sapatrjv sapatrjv force-pushed the fix-shallow-clone-tag-refspec branch from 653d073 to 57cf3ed Compare April 28, 2026 21:16
@sapatrjv sapatrjv closed this Apr 28, 2026
@sapatrjv sapatrjv reopened this Apr 28, 2026
@Byron Sebastian Thiel (Byron) requested review from Copilot and removed request for Copilot April 29, 2026 01:27
@Byron
Copy link
Copy Markdown
Member

Codex (@codex) review

Copy link
Copy Markdown
Member

@Byron Sebastian Thiel (Byron) left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs a test.

@sapatrjv
Copy link
Copy Markdown
Author

Needs a test.

Needs a test.

Added followng test case: shallow_clone_with_tag_ref_name_uses_tag_refspec

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes shallow-clone behavior in gix when the user provides a tag name via with_ref_name(), ensuring the generated persisted fetch refspec matches the remote’s actual ref type (tag vs branch), addressing #2554.

Changes:

  • For shallow clones, connect to the remote to resolve ref_name (or HEAD) to a concrete remote full ref name before constructing the single-ref refspec.
  • Persist a tag-style refspec (+refs/tags/X:refs/tags/X) when the resolved target is a tag; otherwise keep branch-style mapping to refs/remotes/<remote>/....
  • Add a regression test asserting a tag-based ref_name results in a tag-style persisted fetch refspec.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
gix/src/clone/fetch/mod.rs Resolve shallow-clone target ref via remote ref discovery, then build tag-vs-branch appropriate single-ref refspec.
gix/tests/gix/clone.rs Add regression test for shallow clone with tag ref_name producing a tag-style persisted refspec.

Comment on lines +125 to +133
let extra_refspec = if let Some(ref_name) = &self.ref_name {
// Use the user-specified ref_name as the refspec to discover what it resolves to.
gix_refspec::parse(ref_name.as_ref().as_bstr(), gix_refspec::parse::Operation::Fetch)
.expect("partial names are valid refspecs")
.to_owned()
} else {
// For shallow clones without a specified ref, we need to determine the ref to clone.
// Just fetch HEAD for that.
let prev_tags = std::mem::replace(&mut remote.fetch_tags, remote::fetch::Tags::None);
let mut connection = remote.connect(remote::Direction::Fetch).await?;
if let Some(f) = self.configure_connection.as_mut() {
f(&mut connection).map_err(Error::RemoteConnection)?;
}
let refmap = connection
.ref_map_by_ref(
&mut progress,
remote::ref_map::Options {
extra_refspecs: vec![gix_refspec::parse(
"HEAD".into(),
gix_refspec::parse::Operation::Fetch,
)
.expect("valid")
.to_owned()],
..Default::default()
},
)
.await?;
gix_refspec::parse("HEAD".into(), gix_refspec::parse::Operation::Fetch)
.expect("valid")
.to_owned()
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid expect() when parsing the user-provided ref_name into a refspec. Even though with_ref_name() validates PartialName, a parse failure here would still panic inside fetch_only() (production code). Prefer propagating the parse error (e.g., add an Error::RefSpecParse variant and use map_err(...) + ?) so invalid inputs never crash the process.

Copilot uses AI. Check for mistakes.
@Byron
Copy link
Copy Markdown
Member

Closing in favor of #2556.

Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 57cf3ededd

ℹ️ 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".

Comment on lines +203 to +205
Some(Category::Tag) => {
format!("+{target_ref}:{target_ref}")
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid fetching remote HEAD for shallow tag clones

When target_ref is a tag, this new tag-style refspec now allows shallow --ref <tag> clones to proceed, but the flow still appends the implicit HEAD:refs/remotes/<remote>/HEAD extra refspec later, so the fetch pulls the default-branch tip in addition to the tag. In practice, a --depth 1 --ref <tag> clone can end up with multiple commits/shallow boundaries (unlike git clone, which keeps only the tagged commit), increasing transfer size and breaking expected shallow semantics for tag-based clones.

Useful? React with 👍 / 👎.

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.

3 participants