@@ -2,6 +2,7 @@ use crate::{
22 bstr:: { BString , ByteSlice } ,
33 clone:: PrepareFetch ,
44} ;
5+ use gix_ref:: Category ;
56
67/// The error returned by [`PrepareFetch::fetch_only()`].
78#[ derive( Debug , thiserror:: Error ) ]
@@ -47,6 +48,10 @@ pub enum Error {
4748 } ,
4849 #[ error( transparent) ]
4950 CommitterOrFallback ( #[ from] crate :: config:: time:: Error ) ,
51+ #[ error( transparent) ]
52+ RefMap ( #[ from] crate :: remote:: ref_map:: Error ) ,
53+ #[ error( transparent) ]
54+ ReferenceName ( #[ from] gix_validate:: reference:: name:: Error ) ,
5055}
5156
5257/// Modification
@@ -101,14 +106,81 @@ impl PrepareFetch {
101106 } ;
102107
103108 let mut remote = repo. remote_at ( self . url . clone ( ) ) ?;
109+
110+ // For shallow clones without custom configuration, we'll use a single-branch refspec
111+ // to match git's behavior (matching git's single-branch behavior for shallow clones).
112+ let use_single_branch_for_shallow = self . shallow != remote:: fetch:: Shallow :: NoChange
113+ && remote. fetch_specs . is_empty ( )
114+ && self . fetch_options . extra_refspecs . is_empty ( ) ;
115+
116+ let target_ref = if use_single_branch_for_shallow {
117+ // Determine target branch from user-specified ref_name or default branch
118+ if let Some ( ref_name) = & self . ref_name {
119+ Some ( Category :: LocalBranch . to_full_name ( ref_name. as_ref ( ) . as_bstr ( ) ) ?)
120+ } else {
121+ // For shallow clones without a specified ref, we need to determine the default branch.
122+ // We'll connect to get HEAD information. For Protocol V2, we need to explicitly list refs.
123+ let mut connection = remote. connect ( remote:: Direction :: Fetch ) . await ?;
124+
125+ // Perform handshake and try to get HEAD from it (works for Protocol V1)
126+ let _ = connection. ref_map_by_ref ( & mut progress, Default :: default ( ) ) . await ?;
127+
128+ let target = if let Some ( handshake) = & connection. handshake {
129+ // Protocol V1: refs are in handshake
130+ handshake. refs . as_ref ( ) . and_then ( |refs| {
131+ refs. iter ( ) . find_map ( |r| match r {
132+ gix_protocol:: handshake:: Ref :: Symbolic {
133+ full_ref_name, target, ..
134+ } if full_ref_name == "HEAD" => gix_ref:: FullName :: try_from ( target) . ok ( ) ,
135+ _ => None ,
136+ } )
137+ } )
138+ } else {
139+ None
140+ } ;
141+
142+ // For Protocol V2 or if we couldn't determine HEAD, use the configured default branch
143+ let fallback_branch = target
144+ . or_else ( || {
145+ repo. config
146+ . resolved
147+ . string ( crate :: config:: tree:: Init :: DEFAULT_BRANCH )
148+ . and_then ( |name| Category :: LocalBranch . to_full_name ( name. as_bstr ( ) ) . ok ( ) )
149+ } )
150+ . unwrap_or_else ( || gix_ref:: FullName :: try_from ( "refs/heads/main" ) . expect ( "known to be valid" ) ) ;
151+
152+ // Drop the connection explicitly to release the borrow on remote
153+ drop ( connection) ;
154+
155+ Some ( fallback_branch)
156+ }
157+ } else {
158+ None
159+ } ;
160+
161+ // Set up refspec based on whether we're doing a single-branch shallow clone,
162+ // which requires a single ref to match Git unless it's overridden.
104163 if remote. fetch_specs . is_empty ( ) {
105- remote = remote
106- . with_refspecs (
107- Some ( format ! ( "+refs/heads/*:refs/remotes/{remote_name}/*" ) . as_str ( ) ) ,
108- remote:: Direction :: Fetch ,
109- )
110- . expect ( "valid static spec" ) ;
164+ if let Some ( target_ref) = & target_ref {
165+ // Single-branch refspec for shallow clones
166+ let short_name = target_ref. shorten ( ) ;
167+ remote = remote
168+ . with_refspecs (
169+ Some ( format ! ( "+{target_ref}:refs/remotes/{remote_name}/{short_name}" ) . as_str ( ) ) ,
170+ remote:: Direction :: Fetch ,
171+ )
172+ . expect ( "valid refspec" ) ;
173+ } else {
174+ // Wildcard refspec for non-shallow clones or when target couldn't be determined
175+ remote = remote
176+ . with_refspecs (
177+ Some ( format ! ( "+refs/heads/*:refs/remotes/{remote_name}/*" ) . as_str ( ) ) ,
178+ remote:: Direction :: Fetch ,
179+ )
180+ . expect ( "valid static spec" ) ;
181+ }
111182 }
183+
112184 let mut clone_fetch_tags = None ;
113185 if let Some ( f) = self . configure_remote . as_mut ( ) {
114186 remote = f ( remote) . map_err ( Error :: RemoteConfiguration ) ?;
@@ -133,6 +205,7 @@ impl PrepareFetch {
133205 . expect ( "valid" )
134206 . to_owned ( ) ;
135207 let pending_pack: remote:: fetch:: Prepare < ' _ , ' _ , _ > = {
208+ // For shallow clones, we already connected once, so we need to connect again
136209 let mut connection = remote. connect ( remote:: Direction :: Fetch ) . await ?;
137210 if let Some ( f) = self . configure_connection . as_mut ( ) {
138211 f ( & mut connection) . map_err ( Error :: RemoteConnection ) ?;
0 commit comments