Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 21 additions & 13 deletions cmd/clone.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ package cmd

import (
"fmt"
"strconv"

"github.com/barrettj12/jit/common"
"github.com/barrettj12/jit/common/git"
"github.com/barrettj12/jit/common/path"
"github.com/barrettj12/jit/common/types"
"github.com/barrettj12/jit/common/url"
"github.com/spf13/cobra"
"strconv"
)

var cloneDocs = `
Expand Down Expand Up @@ -45,12 +46,12 @@ func newCloneCmd() *cobra.Command {
// Clone clones the provided repo, using the workflow described in
// https://morgan.cugerone.com/blog/how-to-use-git-worktree-and-in-a-clean-way/
func Clone(cmd *cobra.Command, args []string) error {
githubRepo := url.GitHubURL(args...)
user := githubRepo.Owner()
repoURL := url.URL(args...)
user := repoURL.Owner()
if user == "" {
return fmt.Errorf("must specify a user to clone repo from")
}
repo := githubRepo.RepoName()
repo := repoURL.RepoName()
if repo == "" {
return fmt.Errorf("must specify a repo to clone")
}
Expand All @@ -64,7 +65,7 @@ func Clone(cmd *cobra.Command, args []string) error {
// Clone the repo
remote := types.RemoteName(user)
err = git.Clone(git.CloneArgs{
Repo: githubRepo,
Repo: repoURL,
CloneDir: path.GitFolderPath(cloneDir),
Bare: true,
OriginName: remote,
Expand Down Expand Up @@ -97,11 +98,14 @@ Create new branches using

var shouldFork bool
if forkFlagVal == "" {
// The user did not specify when typing the command whether we should
// fork the repo or not. Ask them.
shouldFork, err = confirm("Create a fork")
if err != nil {
return err
// Only ask to fork if this is a GitHub repo
if repoURL.HostedBy() == url.GitHub {
// The user did not specify when typing the command whether we should
// fork the repo or not. Ask them.
shouldFork, err = confirm("Create a fork")
if err != nil {
return err
}
}
} else {
shouldFork, err = strconv.ParseBool(forkFlagVal)
Expand All @@ -111,9 +115,13 @@ Create new branches using
}

if shouldFork {
err = fork(cloneDir, user, repo)
if err != nil {
return err
if repoURL.HostedBy() == url.GitHub {
err = fork(cloneDir, user, repo)
if err != nil {
return err
}
} else {
fmt.Printf("WARNING: don't know how to fork for repo type %q, skipping\n", repoURL.HostedBy())
}
}

Expand Down
18 changes: 11 additions & 7 deletions common/git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ package git
import (
"bytes"
"fmt"
"github.com/barrettj12/jit/common/env"
"github.com/barrettj12/jit/common/path"
"github.com/barrettj12/jit/common/types"
"github.com/barrettj12/jit/common/url"
"io"
"os"
"os/exec"
"strings"

"github.com/barrettj12/jit/common/env"
"github.com/barrettj12/jit/common/path"
"github.com/barrettj12/jit/common/types"
"github.com/barrettj12/jit/common/url"
)

type CloneArgs struct {
Expand All @@ -33,7 +34,11 @@ func Clone(opts CloneArgs) error {
args = append(args, out)
}

_, err := internalExec(internalExecArgs{args: args})
_, err := internalExec(internalExecArgs{
args: args,
attachStdout: true,
attachStderr: true,
})
return err
}

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

var runErr error
runErr = cmd.Run() // this error contains the exit code
runErr := cmd.Run() // this error contains the exit code

// handle errors
if runErr != nil {
Expand Down
74 changes: 74 additions & 0 deletions common/url/url.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import (
// RemoteRepo represents a remote Git repository on GitHub, GitLab, etc
type RemoteRepo interface {
URL() string
Owner() string
RepoName() string
HostedBy() RepoSource
}

// Raw is a raw URL.
Expand All @@ -18,6 +21,41 @@ func (u Raw) URL() string {
return string(u)
}

func (u Raw) Owner() string {
// Assume the first url component is the owner
parsed, _ := url.Parse(string(u))
split := strings.Split(parsed.Path, "/")
if len(split) > 1 {
return split[1]
}
return ""
}

func (u Raw) RepoName() string {
// Assume the second url component is the repo name
parsed, _ := url.Parse(string(u))
split := strings.Split(parsed.Path, "/")
if len(split) > 2 {
return split[2]
}
return ""
}

func (u Raw) HostedBy() RepoSource {
parsed, err := url.Parse(string(u))
if err == nil {
switch parsed.Host {
case "github.com":
return GitHub
case "gitlab.com":
return GitLab
default:
return UnknownSite
}
}
return UnknownSite
}

// Nil represents an unspecified URL.
var Nil RemoteRepo = Raw("")

Expand Down Expand Up @@ -69,3 +107,39 @@ func (r GitHubRepo) RepoName() string {
}
return ""
}

func (r GitHubRepo) HostedBy() RepoSource {
return GitHub
}

// URL converts the given path components into a repo URL. If the website is
// not specified, GitHub will be assumed.
//
// "user" -> "https://github.com/user"
// "user/repo" -> "https://github.com/user/repo"
// "user", "repo" -> "https://github.com/user/repo"
// "https://server.com/user/repo" -> "https://server.com/user/repo"
func URL(c ...string) RemoteRepo {
if len(c) == 0 {
return Nil
}
parsed, err := url.Parse(c[0])
if err != nil {
return GitHubURL(c...)
}
if parsed.Host == "" {
// Assume GitHub
return GitHubURL(c...)
}
return Raw(c[0])
}

// RepoSource represents a possible hosting site for a Git repo, e.g. GitHub,
// GitLab, private website, ...
type RepoSource string

const (
GitHub RepoSource = "github"
GitLab RepoSource = "gitlab"
UnknownSite RepoSource = "unknown"
)
51 changes: 50 additions & 1 deletion common/url/url_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package url

import (
"github.com/barrettj12/jit/common/testutil"
"testing"

"github.com/barrettj12/jit/common/testutil"
)

func TestGitHubRepo(t *testing.T) {
Expand Down Expand Up @@ -55,3 +56,51 @@ func TestIsNil(t *testing.T) {
testutil.AssertEqual(t, IsNil(url), true)
}
}

func TestParseURL(t *testing.T) {
tests := []struct {
input []string
url string
owner string
repoName string
hostedBy RepoSource
}{{
input: []string{"user"},
url: "https://github.com/user",
owner: "user",
repoName: "",
hostedBy: GitHub,
}, {
input: []string{"user", "repo"},
url: "https://github.com/user/repo",
owner: "user",
repoName: "repo",
hostedBy: GitHub,
}, {
input: []string{"user/repo"},
url: "https://github.com/user/repo",
owner: "user",
repoName: "repo",
hostedBy: GitHub,
}, {
input: []string{"https://github.com/user/repo"},
url: "https://github.com/user/repo",
owner: "user",
repoName: "repo",
hostedBy: GitHub,
}, {
input: []string{"https://gitlab.com/user/repo"},
url: "https://gitlab.com/user/repo",
owner: "user",
repoName: "repo",
hostedBy: GitLab,
}}

for _, test := range tests {
url := URL(test.input...)
testutil.AssertEqual(t, url.URL(), test.url)
testutil.AssertEqual(t, url.Owner(), test.owner)
testutil.AssertEqual(t, url.RepoName(), test.repoName)
testutil.AssertEqual(t, url.HostedBy(), test.hostedBy)
}
}