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 }