-
Notifications
You must be signed in to change notification settings - Fork 53
/
Copy pathremote.go
154 lines (139 loc) · 2.9 KB
/
remote.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package git
import (
"net/url"
"regexp"
"sort"
"strings"
)
var remoteRE = regexp.MustCompile(`(.+)\s+(.+)\s+\((push|fetch)\)`)
type RemoteSet []*Remote
type Remote struct {
Name string
FetchURL *url.URL
PushURL *url.URL
Resolved string
Host string
Owner string
Repo string
}
func (r RemoteSet) Len() int { return len(r) }
func (r RemoteSet) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
func (r RemoteSet) Less(i, j int) bool {
return remoteNameSortScore(r[i].Name) > remoteNameSortScore(r[j].Name)
}
func remoteNameSortScore(name string) int {
switch strings.ToLower(name) {
case "upstream":
return 3
case "github":
return 2
case "origin":
return 1
default:
return 0
}
}
func Remotes() (RemoteSet, error) {
list, err := listRemotes()
if err != nil {
return nil, err
}
remotes := parseRemotes(list)
setResolvedRemotes(remotes)
sort.Sort(remotes)
return remotes, nil
}
// Filter remotes by given hostnames, maintains original order.
func (rs RemoteSet) FilterByHosts(hosts []string) RemoteSet {
filtered := make(RemoteSet, 0)
for _, remote := range rs {
for _, host := range hosts {
if strings.EqualFold(remote.Host, host) {
filtered = append(filtered, remote)
break
}
}
}
return filtered
}
func listRemotes() ([]string, error) {
stdOut, _, err := Exec("remote", "-v")
if err != nil {
return nil, err
}
return toLines(stdOut.String()), nil
}
func parseRemotes(gitRemotes []string) RemoteSet {
remotes := RemoteSet{}
for _, r := range gitRemotes {
match := remoteRE.FindStringSubmatch(r)
if match == nil {
continue
}
name := strings.TrimSpace(match[1])
urlStr := strings.TrimSpace(match[2])
urlType := strings.TrimSpace(match[3])
url, err := ParseURL(urlStr)
if err != nil {
continue
}
host, owner, repo, _ := RepoInfoFromURL(url)
var rem *Remote
if len(remotes) > 0 {
rem = remotes[len(remotes)-1]
if name != rem.Name {
rem = nil
}
}
if rem == nil {
rem = &Remote{Name: name}
remotes = append(remotes, rem)
}
switch urlType {
case "fetch":
rem.FetchURL = url
rem.Host = host
rem.Owner = owner
rem.Repo = repo
case "push":
rem.PushURL = url
if rem.Host == "" {
rem.Host = host
}
if rem.Owner == "" {
rem.Owner = owner
}
if rem.Repo == "" {
rem.Repo = repo
}
}
}
return remotes
}
func setResolvedRemotes(remotes RemoteSet) {
stdOut, _, err := Exec("config", "--get-regexp", `^remote\..*\.gh-resolved$`)
if err != nil {
return
}
for _, l := range toLines(stdOut.String()) {
parts := strings.SplitN(l, " ", 2)
if len(parts) < 2 {
continue
}
rp := strings.SplitN(parts[0], ".", 3)
if len(rp) < 2 {
continue
}
name := rp[1]
for _, r := range remotes {
if r.Name == name {
r.Resolved = parts[1]
break
}
}
}
}
func toLines(output string) []string {
lines := strings.TrimSuffix(output, "\n")
return strings.Split(lines, "\n")
}