@@ -138,15 +138,17 @@ mod remote {
138
138
use crate :: bstr:: BStr ;
139
139
use std:: { borrow:: Cow , collections:: BTreeSet } ;
140
140
141
+ use crate :: config:: tree:: { Remote , Section } ;
141
142
use crate :: remote;
142
143
144
+ /// Query configuration related to remotes.
143
145
impl crate :: Repository {
144
146
/// Returns a sorted list unique of symbolic names of remotes that
145
147
/// we deem [trustworthy][crate::open::Options::filter_config_section()].
146
148
pub fn remote_names ( & self ) -> BTreeSet < Cow < ' _ , BStr > > {
147
149
self . config
148
150
. resolved
149
- . sections_by_name ( "remote" )
151
+ . sections_by_name ( Remote . name ( ) )
150
152
. map ( |it| {
151
153
let filter = self . filter_config_section ( ) ;
152
154
it. filter ( move |s| filter ( s. meta ( ) ) )
@@ -167,9 +169,12 @@ mod remote {
167
169
pub fn remote_default_name ( & self , direction : remote:: Direction ) -> Option < Cow < ' _ , BStr > > {
168
170
let name = ( direction == remote:: Direction :: Push )
169
171
. then ( || {
170
- self . config
171
- . resolved
172
- . string_filter ( "remote" , None , "pushDefault" , & mut self . filter_config_section ( ) )
172
+ self . config . resolved . string_filter (
173
+ Remote . name ( ) ,
174
+ None ,
175
+ Remote :: PUSH_DEFAULT . name ,
176
+ & mut self . filter_config_section ( ) ,
177
+ )
173
178
} )
174
179
. flatten ( ) ;
175
180
name. or_else ( || {
@@ -190,11 +195,15 @@ mod remote {
190
195
mod branch {
191
196
use std:: { borrow:: Cow , collections:: BTreeSet , convert:: TryInto } ;
192
197
193
- use gix_ref:: FullNameRef ;
194
- use gix_validate:: reference:: name:: Error as ValidateNameError ;
198
+ use gix_ref:: { FullName , FullNameRef } ;
195
199
196
200
use crate :: bstr:: BStr ;
201
+ use crate :: config:: cache:: util:: ApplyLeniencyDefault ;
202
+ use crate :: config:: tree:: { Branch , Push , Section } ;
203
+ use crate :: repository:: branch_remote_ref_name;
204
+ use crate :: { push, remote} ;
197
205
206
+ /// Query configuration related to branches.
198
207
impl crate :: Repository {
199
208
/// Return a set of unique short branch names for which custom configuration exists in the configuration,
200
209
/// if we deem them [trustworthy][crate::open::Options::filter_config_section()].
@@ -206,38 +215,143 @@ mod branch {
206
215
self . subsection_str_names_of ( "branch" )
207
216
}
208
217
209
- /// Returns the validated reference on the remote associated with the given `short_branch_name`,
210
- /// always `main` instead of `refs/heads/main`.
218
+ /// Returns the validated reference on the remote associated with the given `name`,
219
+ /// which will be used when *merging*.
220
+ /// The returned value corresponds to the `branch.<short_branch_name>.merge` configuration key.
221
+ ///
222
+ /// Returns `None` if there is no value at the given key, or if no remote or remote ref is configured.
223
+ /// May return an error if the reference name to be returned is invalid.
211
224
///
212
- /// The returned reference is the one we track on the remote side for merging and pushing.
213
- /// Returns `None` if the remote reference was not found.
214
- /// May return an error if the reference is invalid.
215
- pub fn branch_remote_ref < ' a > (
225
+ /// ### Note
226
+ ///
227
+ /// This name refers to what Git calls upstream branch (as opposed to upstream *tracking* branch).
228
+ #[ doc( alias = "branch_upstream_name" , alias = "git2" ) ]
229
+ pub fn branch_remote_ref_name (
216
230
& self ,
217
- short_branch_name : impl Into < & ' a BStr > ,
218
- ) -> Option < Result < Cow < ' _ , FullNameRef > , ValidateNameError > > {
219
- self . config
220
- . resolved
221
- . string ( "branch" , Some ( short_branch_name. into ( ) ) , "merge" )
222
- . map ( crate :: config:: tree:: branch:: Merge :: try_into_fullrefname)
231
+ name : & FullNameRef ,
232
+ direction : remote:: Direction ,
233
+ ) -> Option < Result < Cow < ' _ , FullNameRef > , branch_remote_ref_name:: Error > > {
234
+ match direction {
235
+ remote:: Direction :: Fetch => {
236
+ let short_name = name. shorten ( ) ;
237
+ self . config
238
+ . resolved
239
+ . string ( "branch" , Some ( short_name) , Branch :: MERGE . name )
240
+ . map ( |name| crate :: config:: tree:: branch:: Merge :: try_into_fullrefname ( name) . map_err ( Into :: into) )
241
+ }
242
+ remote:: Direction :: Push => {
243
+ let remote = match self . branch_remote ( name. shorten ( ) , direction) ? {
244
+ Ok ( r) => r,
245
+ Err ( err) => return Some ( Err ( err. into ( ) ) ) ,
246
+ } ;
247
+ if remote. push_specs . is_empty ( ) {
248
+ let push_default = match self
249
+ . config
250
+ . resolved
251
+ . string ( Push . name ( ) , None , Push :: DEFAULT . name )
252
+ . map_or ( Ok ( Default :: default ( ) ) , |v| {
253
+ Push :: DEFAULT
254
+ . try_into_default ( v)
255
+ . with_lenient_default ( self . config . lenient_config )
256
+ } ) {
257
+ Ok ( v) => v,
258
+ Err ( err) => return Some ( Err ( err. into ( ) ) ) ,
259
+ } ;
260
+ match push_default {
261
+ push:: Default :: Nothing => None ,
262
+ push:: Default :: Current | push:: Default :: Matching => Some ( Ok ( Cow :: Owned ( name. to_owned ( ) ) ) ) ,
263
+ push:: Default :: Upstream => self . branch_remote_ref_name ( name, remote:: Direction :: Fetch ) ,
264
+ push:: Default :: Simple => {
265
+ match self . branch_remote_ref_name ( name, remote:: Direction :: Fetch ) ? {
266
+ Ok ( fetch_ref) if fetch_ref. as_ref ( ) == name => Some ( Ok ( fetch_ref) ) ,
267
+ Err ( err) => Some ( Err ( err) ) ,
268
+ Ok ( _different_fetch_ref) => None ,
269
+ }
270
+ }
271
+ }
272
+ } else {
273
+ let search = gix_refspec:: MatchGroup :: from_push_specs (
274
+ remote
275
+ . push_specs
276
+ . iter ( )
277
+ . map ( gix_refspec:: RefSpec :: to_ref)
278
+ . filter ( |spec| spec. destination ( ) . is_some ( ) ) ,
279
+ ) ;
280
+ let null_id = self . object_hash ( ) . null ( ) ;
281
+ let out = search. match_remotes (
282
+ Some ( gix_refspec:: match_group:: Item {
283
+ full_ref_name : name. as_bstr ( ) ,
284
+ target : & null_id,
285
+ object : None ,
286
+ } )
287
+ . into_iter ( ) ,
288
+ ) ;
289
+ out. mappings . into_iter ( ) . next ( ) . and_then ( |m| {
290
+ m. rhs . map ( |name| {
291
+ FullName :: try_from ( name. into_owned ( ) )
292
+ . map ( Cow :: Owned )
293
+ . map_err ( Into :: into)
294
+ } )
295
+ } )
296
+ }
297
+ }
298
+ }
223
299
}
224
300
225
301
/// Returns the unvalidated name of the remote associated with the given `short_branch_name`,
226
302
/// typically `main` instead of `refs/heads/main`.
227
303
/// In some cases, the returned name will be an URL.
228
304
/// Returns `None` if the remote was not found or if the name contained illformed UTF-8.
229
305
///
306
+ /// * if `direction` is [remote::Direction::Fetch], we will query the `branch.<short_name>.remote` configuration.
307
+ /// * if `direction` is [remote::Direction::Push], the push remote will be queried by means of `branch.<short_name>.pushRemote`
308
+ /// or `remote.pushDefault` as fallback.
309
+ ///
230
310
/// See also [`Reference::remote_name()`][crate::Reference::remote_name()] for a more typesafe version
231
311
/// to be used when a `Reference` is available.
312
+ ///
313
+ /// `short_branch_name` can typically be obtained by [shortening a full branch name](FullNameRef::shorten()).
314
+ #[ doc( alias = "branch_upstream_remote" , alias = "git2" ) ]
232
315
pub fn branch_remote_name < ' a > (
233
316
& self ,
234
317
short_branch_name : impl Into < & ' a BStr > ,
235
- ) -> Option < crate :: remote:: Name < ' _ > > {
236
- self . config
237
- . resolved
238
- . string ( "branch" , Some ( short_branch_name. into ( ) ) , "remote" )
318
+ direction : remote:: Direction ,
319
+ ) -> Option < remote:: Name < ' _ > > {
320
+ let name = short_branch_name. into ( ) ;
321
+ let config = & self . config . resolved ;
322
+ ( direction == remote:: Direction :: Push )
323
+ . then ( || {
324
+ config
325
+ . string ( "branch" , Some ( name) , Branch :: PUSH_REMOTE . name )
326
+ . or_else ( || config. string ( "remote" , None , crate :: config:: tree:: Remote :: PUSH_DEFAULT . name ) )
327
+ } )
328
+ . flatten ( )
329
+ . or_else ( || config. string ( "branch" , Some ( name) , Branch :: REMOTE . name ) )
239
330
. and_then ( |name| name. try_into ( ) . ok ( ) )
240
331
}
332
+
333
+ /// Like [`branch_remote_name(…)`](Self::branch_remote_name()), but returns a [Remote](crate::Remote).
334
+ /// `short_branch_name` is the name to use for looking up `branch.<short_branch_name>.*` values in the
335
+ /// configuration.
336
+ pub fn branch_remote < ' a > (
337
+ & self ,
338
+ short_branch_name : impl Into < & ' a BStr > ,
339
+ direction : remote:: Direction ,
340
+ ) -> Option < Result < crate :: Remote < ' _ > , remote:: find:: existing:: Error > > {
341
+ let name = self . branch_remote_name ( short_branch_name, direction) ?;
342
+ self . try_find_remote ( name. as_bstr ( ) )
343
+ . map ( |res| res. map_err ( Into :: into) )
344
+ . or_else ( || match name {
345
+ remote:: Name :: Url ( url) => gix_url:: parse ( url. as_ref ( ) )
346
+ . map_err ( Into :: into)
347
+ . and_then ( |url| {
348
+ self . remote_at ( url)
349
+ . map_err ( |err| remote:: find:: existing:: Error :: Find ( remote:: find:: Error :: Init ( err) ) )
350
+ } )
351
+ . into ( ) ,
352
+ remote:: Name :: Symbol ( _) => None ,
353
+ } )
354
+ }
241
355
}
242
356
}
243
357
0 commit comments