Skip to content

Commit

Permalink
Support ssh URL (#113)
Browse files Browse the repository at this point in the history
  • Loading branch information
shogo82148 authored Oct 28, 2023
1 parent da77b46 commit 16bde6f
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 7 deletions.
54 changes: 47 additions & 7 deletions shared/remote.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package shared

import (
"fmt"
"net/url"
"regexp"
"strings"
)
Expand All @@ -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,
}
}
11 changes: 11 additions & 0 deletions shared/remote_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down

0 comments on commit 16bde6f

Please sign in to comment.