@@ -200,7 +200,7 @@ mod branch {
200
200
use crate :: bstr:: BStr ;
201
201
use crate :: config:: cache:: util:: ApplyLeniencyDefault ;
202
202
use crate :: config:: tree:: { Branch , Push , Section } ;
203
- use crate :: repository:: branch_remote_ref_name;
203
+ use crate :: repository:: { branch_remote_ref_name, branch_remote_tracking_ref_name } ;
204
204
use crate :: { push, remote} ;
205
205
206
206
/// Query configuration related to branches.
@@ -225,6 +225,9 @@ mod branch {
225
225
/// ### Note
226
226
///
227
227
/// This name refers to what Git calls upstream branch (as opposed to upstream *tracking* branch).
228
+ /// The value is also fast to retrieve compared to its tracking branch.
229
+ /// Also note that a [remote::Direction] isn't used here as Git only supports (and requires) configuring
230
+ /// the remote to fetch from, not the one to push to.
228
231
#[ doc( alias = "branch_upstream_name" , alias = "git2" ) ]
229
232
pub fn branch_remote_ref_name (
230
233
& self ,
@@ -270,34 +273,50 @@ mod branch {
270
273
}
271
274
}
272
275
} 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
- } )
276
+ matching_remote ( name, remote. push_specs . iter ( ) , self . object_hash ( ) )
277
+ . map ( |res| res. map_err ( Into :: into) )
296
278
}
297
279
}
298
280
}
299
281
}
300
282
283
+ /// Return the validated name of the reference that tracks the corresponding reference of `name` on the remote for
284
+ /// `direction`. Note that a branch with that name might not actually exist.
285
+ ///
286
+ /// * with `remote` being [remote::Direction::Fetch], we return the tracking branch that is on the destination
287
+ /// side of a `src:dest` refspec. For instance, with `name` being `main` and the default refspec
288
+ /// `refs/heads/*:refs/remotes/origin/*`, `refs/heads/main` would match and produce `refs/remotes/origin/main`.
289
+ /// * with `remote` being [remote::Direction::Push], we return the tracking branch that corresponds to the remote
290
+ /// branch that we would push to. For instance, with `name` being `main` and no setup at all, we
291
+ /// would push to `refs/heads/main` on the remote. And that one would be fetched matching the
292
+ /// `refs/heads/*:refs/remotes/origin/*` fetch refspec, hence `refs/remotes/origin/main` is returned.
293
+ /// Note that `push` refspecs can be used to map `main` to `other` (using a push refspec `refs/heads/main:refs/heads/other`),
294
+ /// which would then lead to `refs/remotes/origin/other` to be returned instead.
295
+ ///
296
+ /// Note that if there is an ambiguity, that is if `name` maps to multiple tracking branches, the first matching mapping
297
+ /// is returned, according to the order in which the fetch or push refspecs occour in the configuration file.
298
+ #[ doc( alias = "branch_upstream_name" , alias = "git2" ) ]
299
+ pub fn branch_remote_tracking_ref_name (
300
+ & self ,
301
+ name : & FullNameRef ,
302
+ direction : remote:: Direction ,
303
+ ) -> Option < Result < Cow < ' _ , FullNameRef > , branch_remote_tracking_ref_name:: Error > > {
304
+ let remote_ref = match self . branch_remote_ref_name ( name, direction) ? {
305
+ Ok ( r) => r,
306
+ Err ( err) => return Some ( Err ( err. into ( ) ) ) ,
307
+ } ;
308
+ let remote = match self . branch_remote ( name. shorten ( ) , direction) ? {
309
+ Ok ( r) => r,
310
+ Err ( err) => return Some ( Err ( err. into ( ) ) ) ,
311
+ } ;
312
+
313
+ if remote. fetch_specs . is_empty ( ) {
314
+ return None ;
315
+ }
316
+ matching_remote ( remote_ref. as_ref ( ) , remote. fetch_specs . iter ( ) , self . object_hash ( ) )
317
+ . map ( |res| res. map_err ( Into :: into) )
318
+ }
319
+
301
320
/// Returns the unvalidated name of the remote associated with the given `short_branch_name`,
302
321
/// typically `main` instead of `refs/heads/main`.
303
322
/// In some cases, the returned name will be an URL.
@@ -353,6 +372,36 @@ mod branch {
353
372
} )
354
373
}
355
374
}
375
+
376
+ fn matching_remote < ' a > (
377
+ lhs : & FullNameRef ,
378
+ specs : impl IntoIterator < Item = & ' a gix_refspec:: RefSpec > ,
379
+ object_hash : gix_hash:: Kind ,
380
+ ) -> Option < Result < Cow < ' static , FullNameRef > , gix_validate:: reference:: name:: Error > > {
381
+ let search = gix_refspec:: MatchGroup {
382
+ specs : specs
383
+ . into_iter ( )
384
+ . map ( gix_refspec:: RefSpec :: to_ref)
385
+ . filter ( |spec| spec. source ( ) . is_some ( ) && spec. destination ( ) . is_some ( ) )
386
+ . collect ( ) ,
387
+ } ;
388
+ let null_id = object_hash. null ( ) ;
389
+ let out = search. match_remotes (
390
+ Some ( gix_refspec:: match_group:: Item {
391
+ full_ref_name : lhs. as_bstr ( ) ,
392
+ target : & null_id,
393
+ object : None ,
394
+ } )
395
+ . into_iter ( ) ,
396
+ ) ;
397
+ out. mappings . into_iter ( ) . next ( ) . and_then ( |m| {
398
+ m. rhs . map ( |name| {
399
+ FullName :: try_from ( name. into_owned ( ) )
400
+ . map ( Cow :: Owned )
401
+ . map_err ( Into :: into)
402
+ } )
403
+ } )
404
+ }
356
405
}
357
406
358
407
impl crate :: Repository {
0 commit comments