github.com/halhenke/ghq@v0.8.1-0.20180207100234-7e68f823b46d/remote_repository.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "net/url" 6 "regexp" 7 "strings" 8 9 "github.com/motemen/ghq/utils" 10 ) 11 12 // A RemoteRepository represents a remote repository. 13 type RemoteRepository interface { 14 // The repository URL. 15 URL() *url.URL 16 // Checks if the URL is valid. 17 IsValid() bool 18 // The VCS backend that hosts the repository. 19 VCS() *VCSBackend 20 } 21 22 // A GitHubRepository represents a GitHub repository. Impliments RemoteRepository. 23 type GitHubRepository struct { 24 url *url.URL 25 } 26 27 func (repo *GitHubRepository) URL() *url.URL { 28 return repo.url 29 } 30 31 func (repo *GitHubRepository) IsValid() bool { 32 if strings.HasPrefix(repo.url.Path, "/blog/") { 33 return false 34 } 35 36 // must be /{user}/{project}/? 37 pathComponents := strings.Split(strings.TrimRight(repo.url.Path, "/"), "/") 38 if len(pathComponents) != 3 { 39 return false 40 } 41 42 return true 43 } 44 45 func (repo *GitHubRepository) VCS() *VCSBackend { 46 return GitBackend 47 } 48 49 // A GitHubGistRepository represents a GitHub Gist repository. 50 type GitHubGistRepository struct { 51 url *url.URL 52 } 53 54 func (repo *GitHubGistRepository) URL() *url.URL { 55 return repo.url 56 } 57 58 func (repo *GitHubGistRepository) IsValid() bool { 59 return true 60 } 61 62 func (repo *GitHubGistRepository) VCS() *VCSBackend { 63 return GitBackend 64 } 65 66 type GoogleCodeRepository struct { 67 url *url.URL 68 } 69 70 func (repo *GoogleCodeRepository) URL() *url.URL { 71 return repo.url 72 } 73 74 var validGoogleCodePathPattern = regexp.MustCompile(`^/p/[^/]+/?$`) 75 76 func (repo *GoogleCodeRepository) IsValid() bool { 77 return validGoogleCodePathPattern.MatchString(repo.url.Path) 78 } 79 80 func (repo *GoogleCodeRepository) VCS() *VCSBackend { 81 if utils.RunSilently("hg", "identify", repo.url.String()) == nil { 82 return MercurialBackend 83 } else if utils.RunSilently("git", "ls-remote", repo.url.String()) == nil { 84 return GitBackend 85 } else { 86 return nil 87 } 88 } 89 90 type DarksHubRepository struct { 91 url *url.URL 92 } 93 94 func (repo *DarksHubRepository) URL() *url.URL { 95 return repo.url 96 } 97 98 func (repo *DarksHubRepository) IsValid() bool { 99 return strings.Count(repo.url.Path, "/") == 2 100 } 101 102 func (repo *DarksHubRepository) VCS() *VCSBackend { 103 return DarcsBackend 104 } 105 106 type BluemixRepository struct { 107 url *url.URL 108 } 109 110 func (repo *BluemixRepository) URL() *url.URL { 111 return repo.url 112 } 113 114 var validBluemixPathPattern = regexp.MustCompile(`^/git/[^/]+/[^/]+$`) 115 116 func (repo *BluemixRepository) IsValid() bool { 117 return validBluemixPathPattern.MatchString(repo.url.Path) 118 } 119 120 func (repo *BluemixRepository) VCS() *VCSBackend { 121 return GitBackend 122 } 123 124 type OtherRepository struct { 125 url *url.URL 126 } 127 128 func (repo *OtherRepository) URL() *url.URL { 129 return repo.url 130 } 131 132 func (repo *OtherRepository) IsValid() bool { 133 return true 134 } 135 136 func (repo *OtherRepository) VCS() *VCSBackend { 137 if GitHasFeatureConfigURLMatch() { 138 // Respect 'ghq.url.https://ghe.example.com/.vcs' config variable 139 // (in gitconfig:) 140 // [ghq "https://ghe.example.com/"] 141 // vcs = github 142 vcs, err := GitConfig("--get-urlmatch", "ghq.vcs", repo.URL().String()) 143 if err != nil { 144 utils.Log("error", err.Error()) 145 } 146 147 if vcs == "git" || vcs == "github" { 148 return GitBackend 149 } 150 151 if vcs == "svn" || vcs == "subversion" { 152 return SubversionBackend 153 } 154 155 if vcs == "git-svn" { 156 return GitsvnBackend 157 } 158 159 if vcs == "hg" || vcs == "mercurial" { 160 return MercurialBackend 161 } 162 163 if vcs == "darcs" { 164 return DarcsBackend 165 } 166 } else { 167 utils.Log("warning", "This version of Git does not support `config --get-urlmatch`; per-URL settings are not available") 168 } 169 170 // Detect VCS backend automatically 171 if utils.RunSilently("git", "ls-remote", repo.url.String()) == nil { 172 return GitBackend 173 } else if utils.RunSilently("hg", "identify", repo.url.String()) == nil { 174 return MercurialBackend 175 } else if utils.RunSilently("svn", "info", repo.url.String()) == nil { 176 return SubversionBackend 177 } else { 178 return nil 179 } 180 } 181 182 func NewRemoteRepository(url *url.URL) (RemoteRepository, error) { 183 if url.Host == "github.com" { 184 return &GitHubRepository{url}, nil 185 } 186 187 if url.Host == "gist.github.com" { 188 return &GitHubGistRepository{url}, nil 189 } 190 191 if url.Host == "code.google.com" { 192 return &GoogleCodeRepository{url}, nil 193 } 194 195 if url.Host == "hub.darcs.net" { 196 return &DarksHubRepository{url}, nil 197 } 198 199 if url.Host == "hub.jazz.net" { 200 return &BluemixRepository{url}, nil 201 } 202 203 gheHosts, err := GitConfigAll("ghq.ghe.host") 204 205 if err != nil { 206 return nil, fmt.Errorf("failed to retrieve GH:E hostname from .gitconfig: %s", err) 207 } 208 209 for _, host := range gheHosts { 210 if url.Host == host { 211 return &GitHubRepository{url}, nil 212 } 213 } 214 215 return &OtherRepository{url}, nil 216 }