googlecode and hg support

This commit is contained in:
motemen 2014-05-05 00:23:09 +09:00
parent f6300cfdd0
commit f7bf6ed8d7
8 changed files with 168 additions and 55 deletions

View file

@ -44,9 +44,9 @@ func CommandGet(c *cli.Context) {
}
func getRemoteRepository(remote RemoteRepository, doUpdate bool) {
remoteURL := remote.RepositoryURL()
remoteURL := remote.URL()
pathParts := append(
[]string{remoteURL.Host}, strings.Split(remote.RepositoryURL().Path, "/")...,
[]string{remoteURL.Host}, strings.Split(remoteURL.Path, "/")...,
)
local := LocalRepositoryFromPathParts(pathParts)
@ -63,14 +63,13 @@ func getRemoteRepository(remote RemoteRepository, doUpdate bool) {
}
if newPath {
utils.Log("clone", fmt.Sprintf("%s -> %s", remote.RepositoryURL(), path))
utils.Log("clone", fmt.Sprintf("%s -> %s", remoteURL, path))
remote.VCS().Clone(remote.RepositoryURL(), path)
remote.VCS().Clone(remoteURL, path)
} else {
if doUpdate {
utils.Log("update", path)
remote.VCS().Update(path)
local.VCS().Update(path)
} else {
utils.Log("exists", path)
}
@ -197,7 +196,7 @@ func CommandPocket(c *cli.Context) {
utils.Log("authorized", authorized.Username)
accessToken = authorized.AccessToken
Git("config", "ghq.pocket.token", authorized.AccessToken)
utils.Run("git", "config", "ghq.pocket.token", authorized.AccessToken)
}
utils.Log("pocket", "Retrieving github.com entries")

18
git.go
View file

@ -1,30 +1,12 @@
package main
import (
"fmt"
"os"
"os/exec"
"strings"
"syscall"
"github.com/motemen/ghq/utils"
)
func Git(command ...string) error {
utils.Log("git", strings.Join(command, " "))
cmd := exec.Command("git", command...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
return fmt.Errorf("git: %s", err)
}
return nil
}
func GitConfig(key string) (string, error) {
cmd := exec.Command("git", "config", "--path", "--null", "--get", key)
cmd.Stderr = os.Stderr

View file

@ -53,6 +53,7 @@ func (repo *LocalRepository) NonHostPath() string {
return strings.Join(repo.PathParts[1:], "/")
}
// Checks if any subpath of the local repository equals the query.
func (repo *LocalRepository) Matches(pathQuery string) bool {
for _, p := range repo.Subpaths() {
if p == pathQuery {
@ -63,6 +64,26 @@ func (repo *LocalRepository) Matches(pathQuery string) bool {
return false
}
// TODO return err
func (repo *LocalRepository) VCS() *VCSBackend {
var (
fi os.FileInfo
err error
)
fi, err = os.Stat(filepath.Join(repo.FullPath, ".git"))
if err == nil && fi.IsDir() {
return GitBackend
}
fi, err = os.Stat(filepath.Join(repo.FullPath, ".hg"))
if err == nil && fi.IsDir() {
return MercurialBackend
}
return nil
}
func walkLocalRepositories(callback func(*LocalRepository)) {
filepath.Walk(localRepositoriesRoot(), func(path string, fileInfo os.FileInfo, err error) error {
repo, err := LocalRepositoryFromFullPath(path)

View file

@ -55,7 +55,7 @@ func main() {
func mustBeOkay(err error) {
if err != nil {
_, file, line, _ := runtime.Caller(0)
_, file, line, _ := runtime.Caller(1)
utils.Log("error", fmt.Sprintf("at %s line %d: %s", file, line, err))
os.Exit(1)
}

View file

@ -3,32 +3,38 @@ package main
import (
"errors"
"net/url"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/motemen/ghq/utils"
)
// Represents a remote repository.
type RemoteRepository interface {
RepositoryURL() *url.URL
// The repository URL.
URL() *url.URL
// Checks if the URL is valid.
IsValid() bool
// The VCS backend that hosts the repository.
VCS() *VCSBackend
}
// Represents a GitHub repository. Impliments RemoteRepository.
type GitHubRepository struct {
*url.URL
url *url.URL
}
func (repo *GitHubRepository) RepositoryURL() *url.URL {
return repo.URL
func (repo *GitHubRepository) URL() *url.URL {
return repo.url
}
func (repo *GitHubRepository) IsValid() bool {
if strings.HasPrefix(repo.Path, "/blog/") {
if strings.HasPrefix(repo.url.Path, "/blog/") {
return false
}
// must be /{user}/{project}
if len(strings.Split(repo.Path, "/")) != 3 {
if len(strings.Split(repo.url.Path, "/")) != 3 {
return false
}
@ -39,29 +45,28 @@ func (repo *GitHubRepository) VCS() *VCSBackend {
return GitBackend
}
type VCSBackend struct {
Clone func(*url.URL, string) error
Update func(string) error
type GoogleCodeRepository struct {
url *url.URL
}
var GitBackend = &VCSBackend{
Clone: func(remote *url.URL, local string) error {
dir, _ := filepath.Split(local)
err := os.MkdirAll(dir, 0755)
if err != nil {
return err
}
func (repo *GoogleCodeRepository) URL() *url.URL {
return repo.url
}
return Git("clone", remote.String(), local)
},
Update: func(local string) error {
err := os.Chdir(local)
if err != nil {
return err
}
var validGoogleCodePathPattern = regexp.MustCompile(`^/p/[^/]+/?$`)
return Git("remote", "update")
},
func (repo *GoogleCodeRepository) IsValid() bool {
return validGoogleCodePathPattern.MatchString(repo.url.Path)
}
func (repo *GoogleCodeRepository) VCS() *VCSBackend {
if utils.RunSilently("hg", "identify", repo.url.String()) == nil {
return MercurialBackend
} else if utils.RunSilently("git", "ls-remote", repo.url.String()) == nil {
return GitBackend
} else {
return nil
}
}
func NewRemoteRepository(url *url.URL) (RemoteRepository, error) {
@ -69,5 +74,9 @@ func NewRemoteRepository(url *url.URL) (RemoteRepository, error) {
return &GitHubRepository{url}, nil
}
return nil, errors.New("Unsupported domain")
if url.Host == "code.google.com" {
return &GoogleCodeRepository{url}, nil
}
return nil, errors.New("Unsupported host")
}

View file

@ -7,7 +7,7 @@ import (
)
func Log(prefix string, message string) {
if prefix == "git" || prefix == "skip" {
if prefix == "git" || prefix == "hg" || prefix == "skip" {
ct.ChangeColor(ct.White, false, ct.None, false)
} else if prefix == "open" || prefix == "exists" {
ct.ChangeColor(ct.Yellow, false, ct.None, false)

45
utils/run.go Normal file
View file

@ -0,0 +1,45 @@
package utils
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"strings"
)
func Run(command string, args ...string) error {
cmd := exec.Command(command, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return RunCommand(cmd)
}
func RunSilently(command string, args ...string) error {
cmd := exec.Command(command, args...)
cmd.Stdout = ioutil.Discard
cmd.Stderr = ioutil.Discard
return RunCommand(cmd)
}
func RunCommand(cmd *exec.Cmd) error {
Log(cmd.Args[0], strings.Join(cmd.Args[1:], " "))
err := cmd.Run()
if err != nil {
return &RunError{cmd, err}
}
return nil
}
type RunError struct {
Command *exec.Cmd
ExecError error
}
func (e *RunError) Error() string {
return fmt.Sprintf("%s: %s", e.Command.Path, e.ExecError)
}

57
vcs.go Normal file
View file

@ -0,0 +1,57 @@
package main
import (
"net/url"
"os"
"path/filepath"
"github.com/motemen/ghq/utils"
)
// Represents a VCS backend.
type VCSBackend struct {
// Clones a remote repository to local path.
Clone func(*url.URL, string) error
// Updates a cloned local repository.
Update func(string) error
}
var GitBackend = &VCSBackend{
Clone: func(remote *url.URL, local string) error {
dir, _ := filepath.Split(local)
err := os.MkdirAll(dir, 0755)
if err != nil {
return err
}
return utils.Run("git", "clone", remote.String(), local)
},
Update: func(local string) error {
err := os.Chdir(local)
if err != nil {
return err
}
return utils.Run("git", "remote", "update")
},
}
var MercurialBackend = &VCSBackend{
Clone: func(remote *url.URL, local string) error {
dir, _ := filepath.Split(local)
err := os.MkdirAll(dir, 0755)
if err != nil {
return err
}
return utils.Run("hg", "clone", remote.String(), local)
},
Update: func(local string) error {
err := os.Chdir(local)
if err != nil {
return err
}
return utils.Run("hg", "pull")
},
}