diff --git a/shared/remote.go b/shared/remote.go index 3579da8..dd2b76f 100644 --- a/shared/remote.go +++ b/shared/remote.go @@ -1,6 +1,8 @@ package shared import ( + "fmt" + "net/url" "regexp" "strings" ) @@ -13,16 +15,54 @@ type ( } ) -// https://datatracker.ietf.org/doc/html/rfc3986#section-2.3 -var regex = regexp.MustCompile(`^(?:(?:[a-zA-Z0-9-._~]+)(?:://|@))?([a-zA-Z0-9-._~]+)[:/](.+?/.+?)(?:\.git|)$`) +var ( + hasSchemePattern = regexp.MustCompile("^[^:]+://") + scpLikeURLPattern = regexp.MustCompile("^([^@]+@)?([^:]+):(/?.+)$") +) +// NewRemote parses the result of `git remote -v` and returns a Remote struct. +// +// acceptable url formats: +// +// ssh://[user@]host.xz[:port]/path/to/repo.git/ +// git://host.xz[:port]/path/to/repo.git/ +// http[s]://host.xz[:port]/path/to/repo.git/ +// ftp[s]://host.xz[:port]/path/to/repo.git/ +// +// An alternative scp-like syntax may also be used with the ssh protocol: +// +// [user@]host.xz:path/to/repo.git/ +// +// ref. http://git-scm.com/docs/git-fetch#_git_urls +// the code is heavily inspired by https://github.com/x-motemen/ghq/blob/7163e61e2309a039241ad40b4a25bea35671ea6f/url.go func NewRemote(remoteConfig string) Remote { splitConfig := strings.Fields(remoteConfig) - if len(splitConfig) == 3 { - found := regex.FindStringSubmatch(splitConfig[1]) - if len(found) == 3 { - return Remote{splitConfig[0], found[1], found[2]} + if len(splitConfig) != 3 { + return Remote{} + } + + ref := splitConfig[1] + if !hasSchemePattern.MatchString(ref) { + if scpLikeURLPattern.MatchString(ref) { + matched := scpLikeURLPattern.FindStringSubmatch(ref) + user := matched[1] + host := matched[2] + path := matched[3] + ref = fmt.Sprintf("ssh://%s%s/%s", user, host, strings.TrimPrefix(path, "/")) } } - return Remote{} + u, err := url.Parse(ref) + if err != nil { + return Remote{} + } + + repo := u.Path + repo = strings.TrimPrefix(repo, "/") + repo = strings.TrimSuffix(repo, ".git") + + return Remote{ + Name: splitConfig[0], + Hostname: u.Host, + RepoName: repo, + } } diff --git a/shared/remote_test.go b/shared/remote_test.go index 4de0c40..a8fcdf6 100644 --- a/shared/remote_test.go +++ b/shared/remote_test.go @@ -28,6 +28,17 @@ func Test_CreateRemoteWithScpLikeUrlAndCustomUserinfo(t *testing.T) { ) } +func Test_CreateRemoteWithSshUrl(t *testing.T) { + assert.Equal(t, + Remote{ + Name: "origin", + Hostname: "github.com", + RepoName: "org/repo", + }, + NewRemote("origin ssh://git@github.com/org/repo.git (fetch)"), + ) +} + func Test_CreateRemoteWithScpLikeUrlWithoutUserinfo(t *testing.T) { assert.Equal(t, Remote{