github.com/abdfnx/gh-api@v0.0.0-20210414084727-f5432eec23b8/git/remote.go (about)

     1  package git
     2  
     3  import (
     4  	"fmt"
     5  	"net/url"
     6  	"regexp"
     7  	"strings"
     8  
     9  	"github.com/abdfnx/gh-api/internal/run"
    10  )
    11  
    12  var remoteRE = regexp.MustCompile(`(.+)\s+(.+)\s+\((push|fetch)\)`)
    13  
    14  // RemoteSet is a slice of git remotes
    15  type RemoteSet []*Remote
    16  
    17  func NewRemote(name string, u string) *Remote {
    18  	pu, _ := url.Parse(u)
    19  	return &Remote{
    20  		Name:     name,
    21  		FetchURL: pu,
    22  		PushURL:  pu,
    23  	}
    24  }
    25  
    26  // Remote is a parsed git remote
    27  type Remote struct {
    28  	Name     string
    29  	Resolved string
    30  	FetchURL *url.URL
    31  	PushURL  *url.URL
    32  }
    33  
    34  func (r *Remote) String() string {
    35  	return r.Name
    36  }
    37  
    38  // Remotes gets the git remotes set for the current repo
    39  func Remotes() (RemoteSet, error) {
    40  	list, err := listRemotes()
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  	remotes := parseRemotes(list)
    45  
    46  	// this is affected by SetRemoteResolution
    47  	remoteCmd, err := GitCommand("config", "--get-regexp", `^remote\..*\.gh-resolved$`)
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  	output, _ := run.PrepareCmd(remoteCmd).Output()
    52  	for _, l := range outputLines(output) {
    53  		parts := strings.SplitN(l, " ", 2)
    54  		if len(parts) < 2 {
    55  			continue
    56  		}
    57  		rp := strings.SplitN(parts[0], ".", 3)
    58  		if len(rp) < 2 {
    59  			continue
    60  		}
    61  		name := rp[1]
    62  		for _, r := range remotes {
    63  			if r.Name == name {
    64  				r.Resolved = parts[1]
    65  				break
    66  			}
    67  		}
    68  	}
    69  
    70  	return remotes, nil
    71  }
    72  
    73  func parseRemotes(gitRemotes []string) (remotes RemoteSet) {
    74  	for _, r := range gitRemotes {
    75  		match := remoteRE.FindStringSubmatch(r)
    76  		if match == nil {
    77  			continue
    78  		}
    79  		name := strings.TrimSpace(match[1])
    80  		urlStr := strings.TrimSpace(match[2])
    81  		urlType := strings.TrimSpace(match[3])
    82  
    83  		var rem *Remote
    84  		if len(remotes) > 0 {
    85  			rem = remotes[len(remotes)-1]
    86  			if name != rem.Name {
    87  				rem = nil
    88  			}
    89  		}
    90  		if rem == nil {
    91  			rem = &Remote{Name: name}
    92  			remotes = append(remotes, rem)
    93  		}
    94  
    95  		u, err := ParseURL(urlStr)
    96  		if err != nil {
    97  			continue
    98  		}
    99  
   100  		switch urlType {
   101  		case "fetch":
   102  			rem.FetchURL = u
   103  		case "push":
   104  			rem.PushURL = u
   105  		}
   106  	}
   107  	return
   108  }
   109  
   110  // AddRemote adds a new git remote and auto-fetches objects from it
   111  func AddRemote(name, u string) (*Remote, error) {
   112  	addCmd, err := GitCommand("remote", "add", "-f", name, u)
   113  	if err != nil {
   114  		return nil, err
   115  	}
   116  	err = run.PrepareCmd(addCmd).Run()
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  
   121  	var urlParsed *url.URL
   122  	if strings.HasPrefix(u, "https") {
   123  		urlParsed, err = url.Parse(u)
   124  		if err != nil {
   125  			return nil, err
   126  		}
   127  
   128  	} else {
   129  		urlParsed, err = ParseURL(u)
   130  		if err != nil {
   131  			return nil, err
   132  		}
   133  
   134  	}
   135  
   136  	return &Remote{
   137  		Name:     name,
   138  		FetchURL: urlParsed,
   139  		PushURL:  urlParsed,
   140  	}, nil
   141  }
   142  
   143  func SetRemoteResolution(name, resolution string) error {
   144  	addCmd, err := GitCommand("config", "--add", fmt.Sprintf("remote.%s.gh-resolved", name), resolution)
   145  	if err != nil {
   146  		return err
   147  	}
   148  	return run.PrepareCmd(addCmd).Run()
   149  }