@@ -138,15 +138,17 @@ mod remote {
138138 use crate :: bstr:: BStr ;
139139 use std:: { borrow:: Cow , collections:: BTreeSet } ;
140140
141+ use crate :: config:: tree:: { Remote , Section } ;
141142 use crate :: remote;
142143
144+ /// Query configuration related to remotes.
143145 impl crate :: Repository {
144146 /// Returns a sorted list unique of symbolic names of remotes that
145147 /// we deem [trustworthy][crate::open::Options::filter_config_section()].
146148 pub fn remote_names ( & self ) -> BTreeSet < Cow < ' _ , BStr > > {
147149 self . config
148150 . resolved
149- . sections_by_name ( "remote" )
151+ . sections_by_name ( Remote . name ( ) )
150152 . map ( |it| {
151153 let filter = self . filter_config_section ( ) ;
152154 it. filter ( move |s| filter ( s. meta ( ) ) )
@@ -167,9 +169,12 @@ mod remote {
167169 pub fn remote_default_name ( & self , direction : remote:: Direction ) -> Option < Cow < ' _ , BStr > > {
168170 let name = ( direction == remote:: Direction :: Push )
169171 . 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+ )
173178 } )
174179 . flatten ( ) ;
175180 name. or_else ( || {
@@ -190,11 +195,15 @@ mod remote {
190195mod branch {
191196 use std:: { borrow:: Cow , collections:: BTreeSet , convert:: TryInto } ;
192197
193- use gix_ref:: FullNameRef ;
194- use gix_validate:: reference:: name:: Error as ValidateNameError ;
198+ use gix_ref:: { FullName , FullNameRef } ;
195199
196200 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} ;
197205
206+ /// Query configuration related to branches.
198207 impl crate :: Repository {
199208 /// Return a set of unique short branch names for which custom configuration exists in the configuration,
200209 /// if we deem them [trustworthy][crate::open::Options::filter_config_section()].
@@ -206,38 +215,143 @@ mod branch {
206215 self . subsection_str_names_of ( "branch" )
207216 }
208217
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.
211224 ///
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 (
216230 & 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+ }
223299 }
224300
225301 /// Returns the unvalidated name of the remote associated with the given `short_branch_name`,
226302 /// typically `main` instead of `refs/heads/main`.
227303 /// In some cases, the returned name will be an URL.
228304 /// Returns `None` if the remote was not found or if the name contained illformed UTF-8.
229305 ///
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+ ///
230310 /// See also [`Reference::remote_name()`][crate::Reference::remote_name()] for a more typesafe version
231311 /// 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" ) ]
232315 pub fn branch_remote_name < ' a > (
233316 & self ,
234317 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 ) )
239330 . and_then ( |name| name. try_into ( ) . ok ( ) )
240331 }
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+ }
241355 }
242356}
243357
0 commit comments