Skip to content

Commit a7085b2

Browse files
authored
Merge pull request #65 from barrettj12/gitlab
Cloning supports non-GitHub repos
2 parents 04dfb17 + 7777d8c commit a7085b2

File tree

4 files changed

+156
-21
lines changed

4 files changed

+156
-21
lines changed

cmd/clone.go

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ package cmd
22

33
import (
44
"fmt"
5+
"strconv"
6+
57
"github.com/barrettj12/jit/common"
68
"github.com/barrettj12/jit/common/git"
79
"github.com/barrettj12/jit/common/path"
810
"github.com/barrettj12/jit/common/types"
911
"github.com/barrettj12/jit/common/url"
1012
"github.com/spf13/cobra"
11-
"strconv"
1213
)
1314

1415
var cloneDocs = `
@@ -45,12 +46,12 @@ func newCloneCmd() *cobra.Command {
4546
// Clone clones the provided repo, using the workflow described in
4647
// https://morgan.cugerone.com/blog/how-to-use-git-worktree-and-in-a-clean-way/
4748
func Clone(cmd *cobra.Command, args []string) error {
48-
githubRepo := url.GitHubURL(args...)
49-
user := githubRepo.Owner()
49+
repoURL := url.URL(args...)
50+
user := repoURL.Owner()
5051
if user == "" {
5152
return fmt.Errorf("must specify a user to clone repo from")
5253
}
53-
repo := githubRepo.RepoName()
54+
repo := repoURL.RepoName()
5455
if repo == "" {
5556
return fmt.Errorf("must specify a repo to clone")
5657
}
@@ -64,7 +65,7 @@ func Clone(cmd *cobra.Command, args []string) error {
6465
// Clone the repo
6566
remote := types.RemoteName(user)
6667
err = git.Clone(git.CloneArgs{
67-
Repo: githubRepo,
68+
Repo: repoURL,
6869
CloneDir: path.GitFolderPath(cloneDir),
6970
Bare: true,
7071
OriginName: remote,
@@ -97,11 +98,14 @@ Create new branches using
9798

9899
var shouldFork bool
99100
if forkFlagVal == "" {
100-
// The user did not specify when typing the command whether we should
101-
// fork the repo or not. Ask them.
102-
shouldFork, err = confirm("Create a fork")
103-
if err != nil {
104-
return err
101+
// Only ask to fork if this is a GitHub repo
102+
if repoURL.HostedBy() == url.GitHub {
103+
// The user did not specify when typing the command whether we should
104+
// fork the repo or not. Ask them.
105+
shouldFork, err = confirm("Create a fork")
106+
if err != nil {
107+
return err
108+
}
105109
}
106110
} else {
107111
shouldFork, err = strconv.ParseBool(forkFlagVal)
@@ -111,9 +115,13 @@ Create new branches using
111115
}
112116

113117
if shouldFork {
114-
err = fork(cloneDir, user, repo)
115-
if err != nil {
116-
return err
118+
if repoURL.HostedBy() == url.GitHub {
119+
err = fork(cloneDir, user, repo)
120+
if err != nil {
121+
return err
122+
}
123+
} else {
124+
fmt.Printf("WARNING: don't know how to fork for repo type %q, skipping\n", repoURL.HostedBy())
117125
}
118126
}
119127

common/git/git.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@ package git
33
import (
44
"bytes"
55
"fmt"
6-
"github.com/barrettj12/jit/common/env"
7-
"github.com/barrettj12/jit/common/path"
8-
"github.com/barrettj12/jit/common/types"
9-
"github.com/barrettj12/jit/common/url"
106
"io"
117
"os"
128
"os/exec"
139
"strings"
10+
11+
"github.com/barrettj12/jit/common/env"
12+
"github.com/barrettj12/jit/common/path"
13+
"github.com/barrettj12/jit/common/types"
14+
"github.com/barrettj12/jit/common/url"
1415
)
1516

1617
type CloneArgs struct {
@@ -33,7 +34,11 @@ func Clone(opts CloneArgs) error {
3334
args = append(args, out)
3435
}
3536

36-
_, err := internalExec(internalExecArgs{args: args})
37+
_, err := internalExec(internalExecArgs{
38+
args: args,
39+
attachStdout: true,
40+
attachStderr: true,
41+
})
3742
return err
3843
}
3944

@@ -150,8 +155,7 @@ var internalExec = func(opts internalExecArgs) (string, error) {
150155
fmt.Println(cmd.String())
151156
}
152157

153-
var runErr error
154-
runErr = cmd.Run() // this error contains the exit code
158+
runErr := cmd.Run() // this error contains the exit code
155159

156160
// handle errors
157161
if runErr != nil {

common/url/url.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ import (
99
// RemoteRepo represents a remote Git repository on GitHub, GitLab, etc
1010
type RemoteRepo interface {
1111
URL() string
12+
Owner() string
13+
RepoName() string
14+
HostedBy() RepoSource
1215
}
1316

1417
// Raw is a raw URL.
@@ -18,6 +21,41 @@ func (u Raw) URL() string {
1821
return string(u)
1922
}
2023

24+
func (u Raw) Owner() string {
25+
// Assume the first url component is the owner
26+
parsed, _ := url.Parse(string(u))
27+
split := strings.Split(parsed.Path, "/")
28+
if len(split) > 1 {
29+
return split[1]
30+
}
31+
return ""
32+
}
33+
34+
func (u Raw) RepoName() string {
35+
// Assume the second url component is the repo name
36+
parsed, _ := url.Parse(string(u))
37+
split := strings.Split(parsed.Path, "/")
38+
if len(split) > 2 {
39+
return split[2]
40+
}
41+
return ""
42+
}
43+
44+
func (u Raw) HostedBy() RepoSource {
45+
parsed, err := url.Parse(string(u))
46+
if err == nil {
47+
switch parsed.Host {
48+
case "github.com":
49+
return GitHub
50+
case "gitlab.com":
51+
return GitLab
52+
default:
53+
return UnknownSite
54+
}
55+
}
56+
return UnknownSite
57+
}
58+
2159
// Nil represents an unspecified URL.
2260
var Nil RemoteRepo = Raw("")
2361

@@ -69,3 +107,39 @@ func (r GitHubRepo) RepoName() string {
69107
}
70108
return ""
71109
}
110+
111+
func (r GitHubRepo) HostedBy() RepoSource {
112+
return GitHub
113+
}
114+
115+
// URL converts the given path components into a repo URL. If the website is
116+
// not specified, GitHub will be assumed.
117+
//
118+
// "user" -> "https://github.com/user"
119+
// "user/repo" -> "https://github.com/user/repo"
120+
// "user", "repo" -> "https://github.com/user/repo"
121+
// "https://server.com/user/repo" -> "https://server.com/user/repo"
122+
func URL(c ...string) RemoteRepo {
123+
if len(c) == 0 {
124+
return Nil
125+
}
126+
parsed, err := url.Parse(c[0])
127+
if err != nil {
128+
return GitHubURL(c...)
129+
}
130+
if parsed.Host == "" {
131+
// Assume GitHub
132+
return GitHubURL(c...)
133+
}
134+
return Raw(c[0])
135+
}
136+
137+
// RepoSource represents a possible hosting site for a Git repo, e.g. GitHub,
138+
// GitLab, private website, ...
139+
type RepoSource string
140+
141+
const (
142+
GitHub RepoSource = "github"
143+
GitLab RepoSource = "gitlab"
144+
UnknownSite RepoSource = "unknown"
145+
)

common/url/url_test.go

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
package url
22

33
import (
4-
"github.com/barrettj12/jit/common/testutil"
54
"testing"
5+
6+
"github.com/barrettj12/jit/common/testutil"
67
)
78

89
func TestGitHubRepo(t *testing.T) {
@@ -55,3 +56,51 @@ func TestIsNil(t *testing.T) {
5556
testutil.AssertEqual(t, IsNil(url), true)
5657
}
5758
}
59+
60+
func TestParseURL(t *testing.T) {
61+
tests := []struct {
62+
input []string
63+
url string
64+
owner string
65+
repoName string
66+
hostedBy RepoSource
67+
}{{
68+
input: []string{"user"},
69+
url: "https://github.com/user",
70+
owner: "user",
71+
repoName: "",
72+
hostedBy: GitHub,
73+
}, {
74+
input: []string{"user", "repo"},
75+
url: "https://github.com/user/repo",
76+
owner: "user",
77+
repoName: "repo",
78+
hostedBy: GitHub,
79+
}, {
80+
input: []string{"user/repo"},
81+
url: "https://github.com/user/repo",
82+
owner: "user",
83+
repoName: "repo",
84+
hostedBy: GitHub,
85+
}, {
86+
input: []string{"https://github.com/user/repo"},
87+
url: "https://github.com/user/repo",
88+
owner: "user",
89+
repoName: "repo",
90+
hostedBy: GitHub,
91+
}, {
92+
input: []string{"https://gitlab.com/user/repo"},
93+
url: "https://gitlab.com/user/repo",
94+
owner: "user",
95+
repoName: "repo",
96+
hostedBy: GitLab,
97+
}}
98+
99+
for _, test := range tests {
100+
url := URL(test.input...)
101+
testutil.AssertEqual(t, url.URL(), test.url)
102+
testutil.AssertEqual(t, url.Owner(), test.owner)
103+
testutil.AssertEqual(t, url.RepoName(), test.repoName)
104+
testutil.AssertEqual(t, url.HostedBy(), test.hostedBy)
105+
}
106+
}

0 commit comments

Comments
 (0)