@@ -128,8 +128,9 @@ impl PrepareFetch {
128
128
129
129
// Add HEAD after the remote was written to config, we need it to know what to check out later, and assure
130
130
// the ref that HEAD points to is present no matter what.
131
+ let head_local_tracking_branch = format ! ( "refs/remotes/{remote_name}/HEAD" ) ;
131
132
let head_refspec = gix_refspec:: parse (
132
- format ! ( "HEAD:refs/remotes/{remote_name}/HEAD " ) . as_str ( ) . into ( ) ,
133
+ format ! ( "HEAD:{head_local_tracking_branch} " ) . as_str ( ) . into ( ) ,
133
134
gix_refspec:: parse:: Operation :: Fetch ,
134
135
)
135
136
. expect ( "valid" )
@@ -139,22 +140,48 @@ impl PrepareFetch {
139
140
if let Some ( f) = self . configure_connection . as_mut ( ) {
140
141
f ( & mut connection) . map_err ( Error :: RemoteConnection ) ?;
141
142
}
142
- connection
143
- . prepare_fetch ( & mut * progress, {
144
- let mut opts = self . fetch_options . clone ( ) ;
145
- if !opts. extra_refspecs . contains ( & head_refspec) {
146
- opts. extra_refspecs . push ( head_refspec)
147
- }
148
- if let Some ( ref_name) = & self . ref_name {
149
- opts. extra_refspecs . push (
150
- gix_refspec:: parse ( ref_name. as_ref ( ) . as_bstr ( ) , gix_refspec:: parse:: Operation :: Fetch )
151
- . expect ( "partial names are valid refspecs" )
152
- . to_owned ( ) ,
153
- ) ;
154
- }
155
- opts
156
- } )
157
- . await ?
143
+ let mut fetch_opts = {
144
+ let mut opts = self . fetch_options . clone ( ) ;
145
+ if !opts. extra_refspecs . contains ( & head_refspec) {
146
+ opts. extra_refspecs . push ( head_refspec. clone ( ) )
147
+ }
148
+ if let Some ( ref_name) = & self . ref_name {
149
+ opts. extra_refspecs . push (
150
+ gix_refspec:: parse ( ref_name. as_ref ( ) . as_bstr ( ) , gix_refspec:: parse:: Operation :: Fetch )
151
+ . expect ( "partial names are valid refspecs" )
152
+ . to_owned ( ) ,
153
+ ) ;
154
+ }
155
+ opts
156
+ } ;
157
+ match connection. prepare_fetch ( & mut * progress, fetch_opts. clone ( ) ) . await {
158
+ Ok ( prepare) => prepare,
159
+ Err ( remote:: fetch:: prepare:: Error :: RefMap ( remote:: ref_map:: Error :: MappingValidation ( err) ) )
160
+ if err. issues . len ( ) == 1
161
+ && fetch_opts. extra_refspecs . contains ( & head_refspec)
162
+ && matches ! (
163
+ err. issues. first( ) ,
164
+ Some ( gix_refspec:: match_group:: validate:: Issue :: Conflict {
165
+ destination_full_ref_name,
166
+ ..
167
+ } ) if * destination_full_ref_name == head_local_tracking_branch
168
+ ) =>
169
+ {
170
+ let head_refspec_idx = fetch_opts
171
+ . extra_refspecs
172
+ . iter ( )
173
+ . enumerate ( )
174
+ . find_map ( |( idx, spec) | ( * spec == head_refspec) . then_some ( idx) )
175
+ . expect ( "it's contained" ) ;
176
+ // On the very special occasion that we fail as there is a remote `refs/heads/HEAD` reference that clashes
177
+ // with our implicit refspec, retry without it. Maybe this tells us that we shouldn't have that implicit
178
+ // refspec, as git can do this without connecting twice.
179
+ let connection = remote. connect ( remote:: Direction :: Fetch ) . await ?;
180
+ fetch_opts. extra_refspecs . remove ( head_refspec_idx) ;
181
+ connection. prepare_fetch ( & mut * progress, fetch_opts) . await ?
182
+ }
183
+ Err ( err) => return Err ( err. into ( ) ) ,
184
+ }
158
185
} ;
159
186
160
187
// Assure problems with custom branch names fail early, not after getting the pack or during negotiation.
0 commit comments