@@ -376,20 +376,20 @@ func (c *Client) lookupCommit(ctx context.Context, sha, format string) ([]byte,
376376 return out , nil
377377}
378378
379- // ReadBranchConfig parses the `branch.BRANCH.(remote|merge|gh-merge-base)` part of git config.
379+ // ReadBranchConfig parses the `branch.BRANCH.(remote|merge|pushremote| gh-merge-base)` part of git config.
380380// If no branch config is found or there is an error in the command, it returns an empty BranchConfig.
381381// Downstream consumers of ReadBranchConfig should consider the behavior they desire if this errors,
382382// as an empty config is not necessarily breaking.
383383func (c * Client ) ReadBranchConfig (ctx context.Context , branch string ) (BranchConfig , error ) {
384384
385385 prefix := regexp .QuoteMeta (fmt .Sprintf ("branch.%s." , branch ))
386- args := []string {"config" , "--get-regexp" , fmt .Sprintf ("^%s(remote|merge|%s)$" , prefix , MergeBaseConfig )}
386+ args := []string {"config" , "--get-regexp" , fmt .Sprintf ("^%s(remote|merge|pushremote| %s)$" , prefix , MergeBaseConfig )}
387387 cmd , err := c .Command (ctx , args ... )
388388 if err != nil {
389389 return BranchConfig {}, err
390390 }
391391
392- out , err := cmd .Output ()
392+ branchCfgOut , err := cmd .Output ()
393393 if err != nil {
394394 // This is the error we expect if the git command does not run successfully.
395395 // If the ExitCode is 1, then we just didn't find any config for the branch.
@@ -400,35 +400,31 @@ func (c *Client) ReadBranchConfig(ctx context.Context, branch string) (BranchCon
400400 return BranchConfig {}, nil
401401 }
402402
403- return parseBranchConfig (outputLines (out )), nil
403+ return parseBranchConfig (outputLines (branchCfgOut )), nil
404404}
405405
406- func parseBranchConfig (configLines []string ) BranchConfig {
406+ func parseBranchConfig (branchConfigLines []string ) BranchConfig {
407407 var cfg BranchConfig
408408
409- for _ , line := range configLines {
409+ // Read the config lines for the specific branch
410+ for _ , line := range branchConfigLines {
410411 parts := strings .SplitN (line , " " , 2 )
411412 if len (parts ) < 2 {
412413 continue
413414 }
414415 keys := strings .Split (parts [0 ], "." )
415416 switch keys [len (keys )- 1 ] {
416417 case "remote" :
417- if strings .Contains (parts [1 ], ":" ) {
418- u , err := ParseURL (parts [1 ])
419- if err != nil {
420- continue
421- }
422- cfg .RemoteURL = u
423- } else if ! isFilesystemPath (parts [1 ]) {
424- cfg .RemoteName = parts [1 ]
425- }
418+ cfg .RemoteURL , cfg .RemoteName = parseRemoteURLOrName (parts [1 ])
419+ case "pushremote" :
420+ cfg .PushRemoteURL , cfg .PushRemoteName = parseRemoteURLOrName (parts [1 ])
426421 case "merge" :
427422 cfg .MergeRef = parts [1 ]
428423 case MergeBaseConfig :
429424 cfg .MergeBase = parts [1 ]
430425 }
431426 }
427+
432428 return cfg
433429}
434430
@@ -445,6 +441,47 @@ func (c *Client) SetBranchConfig(ctx context.Context, branch, name, value string
445441 return err
446442}
447443
444+ // PushDefault returns the value of push.default in the config. If the value
445+ // is not set, it returns "simple" (the default git value). See
446+ // https://git-scm.com/docs/git-config#Documentation/git-config.txt-pushdefault
447+ func (c * Client ) PushDefault (ctx context.Context ) (string , error ) {
448+ pushDefault , err := c .Config (ctx , "push.default" )
449+ if err == nil {
450+ return pushDefault , nil
451+ }
452+
453+ var gitError * GitError
454+ if ok := errors .As (err , & gitError ); ok && gitError .ExitCode == 1 {
455+ return "simple" , nil
456+ }
457+ return "" , err
458+ }
459+
460+ // RemotePushDefault returns the value of remote.pushDefault in the config. If
461+ // the value is not set, it returns an empty string.
462+ func (c * Client ) RemotePushDefault (ctx context.Context ) (string , error ) {
463+ remotePushDefault , err := c .Config (ctx , "remote.pushDefault" )
464+ if err == nil {
465+ return remotePushDefault , nil
466+ }
467+
468+ var gitError * GitError
469+ if ok := errors .As (err , & gitError ); ok && gitError .ExitCode == 1 {
470+ return "" , nil
471+ }
472+
473+ return "" , err
474+ }
475+
476+ // ParsePushRevision gets the value of the @{push} revision syntax
477+ // An error here doesn't necessarily mean something is broken, but may mean that the @{push}
478+ // revision syntax couldn't be resolved, such as in non-centralized workflows with
479+ // push.default = simple. Downstream consumers should consider how to handle this error.
480+ func (c * Client ) ParsePushRevision (ctx context.Context , branch string ) (string , error ) {
481+ revParseOut , err := c .revParse (ctx , "--abbrev-ref" , branch + "@{push}" )
482+ return firstLine (revParseOut ), err
483+ }
484+
448485func (c * Client ) DeleteLocalTag (ctx context.Context , tag string ) error {
449486 args := []string {"tag" , "-d" , tag }
450487 cmd , err := c .Command (ctx , args ... )
@@ -790,6 +827,17 @@ func parseRemotes(remotesStr []string) RemoteSet {
790827 return remotes
791828}
792829
830+ func parseRemoteURLOrName (value string ) (* url.URL , string ) {
831+ if strings .Contains (value , ":" ) {
832+ if u , err := ParseURL (value ); err == nil {
833+ return u , ""
834+ }
835+ } else if ! isFilesystemPath (value ) {
836+ return nil , value
837+ }
838+ return nil , ""
839+ }
840+
793841func populateResolvedRemotes (remotes RemoteSet , resolved []string ) {
794842 for _ , l := range resolved {
795843 parts := strings .SplitN (l , " " , 2 )
0 commit comments