зеркало из https://github.com/mislav/hub.git
Merge branch 'clone'
This commit is contained in:
Коммит
c787d9b845
|
@ -54,6 +54,14 @@ func (a *Args) Remove(i int) string {
|
|||
return item
|
||||
}
|
||||
|
||||
func (a *Args) Replace(i int, item string) {
|
||||
if i > a.Size()-1 {
|
||||
panic(fmt.Sprintf("Index %d is out of bound", i))
|
||||
}
|
||||
|
||||
a.args[i] = item
|
||||
}
|
||||
|
||||
func (a *Args) IndexOf(arg string) int {
|
||||
for i, aa := range a.args {
|
||||
if aa == arg {
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"github.com/jingweno/gh/github"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var cmdClone = &Command{
|
||||
Run: clone,
|
||||
GitExtension: true,
|
||||
Usage: "clone [-p] OPTIONS [USER] REPOSITORY DIRECTORY",
|
||||
Short: "clone a remote repository into a new directory",
|
||||
}
|
||||
|
||||
/**
|
||||
$ gh clone jingweno/gh
|
||||
> git clone git://github.com/jingweno/gh
|
||||
|
||||
$ gh clone -p jingweno/gh
|
||||
> git clone git@github.com:jingweno/gh.git
|
||||
|
||||
$ gh clone jekyll_and_hype
|
||||
> git clone git://github.com/YOUR_LOGIN/jekyll_and_hype.
|
||||
|
||||
$ hub clone -p jekyll_and_hype
|
||||
> git clone git@github.com:YOUR_LOGIN/jekyll_and_hype.git
|
||||
*/
|
||||
func clone(command *Command, args *Args) {
|
||||
if !args.IsEmpty() {
|
||||
transformCloneArgs(args)
|
||||
}
|
||||
}
|
||||
|
||||
func transformCloneArgs(args *Args) {
|
||||
isSSH := parseClonePrivateFlag(args)
|
||||
hasValueRegxp := regexp.MustCompile("^(--(upload-pack|template|depth|origin|branch|reference|name)|-[ubo])$")
|
||||
nameWithOwnerRegexp := regexp.MustCompile(NameWithOwnerRe)
|
||||
for i, a := range args.Array() {
|
||||
if hasValueRegxp.MatchString(a) {
|
||||
continue
|
||||
}
|
||||
|
||||
if nameWithOwnerRegexp.MatchString(a) && !isDir(a) {
|
||||
name, owner := parseCloneNameAndOwner(a)
|
||||
project := github.Project{Name: name, Owner: owner}
|
||||
url := project.GitURL(name, owner, isSSH)
|
||||
args.Replace(i, url)
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parseClonePrivateFlag(args *Args) bool {
|
||||
if i := args.IndexOf("-p"); i != -1 {
|
||||
args.Remove(i)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func parseCloneNameAndOwner(arg string) (name, owner string) {
|
||||
name, owner = arg, ""
|
||||
if strings.Contains(arg, "/") {
|
||||
split := strings.SplitN(arg, "/", 2)
|
||||
name = split[1]
|
||||
owner = split[0]
|
||||
}
|
||||
|
||||
if owner == "" {
|
||||
config := github.CurrentConfig()
|
||||
owner = config.User
|
||||
}
|
||||
|
||||
return
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"github.com/bmizerany/assert"
|
||||
"github.com/jingweno/gh/github"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTransformCloneArgs(t *testing.T) {
|
||||
args := NewArgs([]string{"jingweno/gh"})
|
||||
transformCloneArgs(args)
|
||||
|
||||
assert.Equal(t, 1, args.Size())
|
||||
assert.Equal(t, "git://github.com/jingweno/gh.git", args.First())
|
||||
|
||||
args = NewArgs([]string{"-p", "jingweno/gh"})
|
||||
transformCloneArgs(args)
|
||||
|
||||
assert.Equal(t, 1, args.Size())
|
||||
assert.Equal(t, "git@github.com:jingweno/gh.git", args.First())
|
||||
|
||||
args = NewArgs([]string{"-p", "acl-services/devise-acl"})
|
||||
transformCloneArgs(args)
|
||||
|
||||
assert.Equal(t, 1, args.Size())
|
||||
assert.Equal(t, "git@github.com:acl-services/devise-acl.git", args.First())
|
||||
|
||||
github.DefaultConfigFile = "./test_support/gh"
|
||||
config := github.Config{User: "jingweno", Token: "123"}
|
||||
github.SaveConfig(&config)
|
||||
defer os.RemoveAll(filepath.Dir(github.DefaultConfigFile))
|
||||
|
||||
args = NewArgs([]string{"-p", "jekyll_and_hyde"})
|
||||
transformCloneArgs(args)
|
||||
|
||||
assert.Equal(t, 1, args.Size())
|
||||
assert.Equal(t, "git@github.com:jingweno/jekyll_and_hyde.git", args.First())
|
||||
}
|
|
@ -11,7 +11,7 @@ import (
|
|||
var (
|
||||
NameRe = "[\\w.][\\w.-]*"
|
||||
OwnerRe = "[a-zA-Z0-9][a-zA-Z0-9-]*"
|
||||
NameWithOwnerRe = fmt.Sprintf("/^(?:%s|%s\\/%s)$", NameRe, OwnerRe, NameRe)
|
||||
NameWithOwnerRe = fmt.Sprintf("^(?:%s|%s\\/%s)$", NameRe, OwnerRe, NameRe)
|
||||
)
|
||||
|
||||
type Command struct {
|
||||
|
@ -59,6 +59,7 @@ var Branching = []*Command{
|
|||
}
|
||||
|
||||
var Remote = []*Command{
|
||||
cmdClone,
|
||||
cmdRemote,
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,9 @@ package commands
|
|||
import (
|
||||
"fmt"
|
||||
"github.com/jingweno/gh/github"
|
||||
"github.com/jingweno/gh/utils"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
|
@ -31,15 +34,23 @@ func remote(command *Command, args *Args) {
|
|||
|
||||
func transformRemoteArgs(args *Args) {
|
||||
ownerWithName := args.Last()
|
||||
owner, repo, match := parseRepoNameOwner(ownerWithName)
|
||||
owner, name, match := parseRepoNameOwner(ownerWithName)
|
||||
if !match {
|
||||
return
|
||||
}
|
||||
|
||||
isPriavte := parseRemotePrivateFlag(args)
|
||||
if name == "" {
|
||||
dir, err := os.Getwd()
|
||||
utils.Check(err)
|
||||
name = filepath.Base(dir)
|
||||
}
|
||||
|
||||
gh := github.New()
|
||||
url := gh.ExpandRemoteUrl(owner, repo, isPriavte)
|
||||
if owner == "origin" {
|
||||
owner = github.CurrentConfig().FetchUser()
|
||||
}
|
||||
|
||||
project := github.Project{Owner: owner, Name: name}
|
||||
url := project.GitURL(name, owner, isPriavte)
|
||||
|
||||
args.Append(url)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,10 @@ package commands
|
|||
|
||||
import (
|
||||
"github.com/bmizerany/assert"
|
||||
"github.com/jingweno/gh/github"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
@ -16,7 +20,7 @@ func TestParseRepoNameOwner(t *testing.T) {
|
|||
|
||||
assert.T(t, match)
|
||||
assert.Equal(t, "jingweno", owner)
|
||||
assert.Equal(t, "gh", repo)
|
||||
assert.Equal(t, "gh", repo)
|
||||
}
|
||||
|
||||
func TestTransformRemoteArgs(t *testing.T) {
|
||||
|
@ -26,7 +30,8 @@ func TestTransformRemoteArgs(t *testing.T) {
|
|||
assert.Equal(t, 3, args.Size())
|
||||
assert.Equal(t, "add", args.First())
|
||||
assert.Equal(t, "jingweno", args.Get(1))
|
||||
assert.Equal(t, "git://github.com/jingweno/gh.git", args.Get(2))
|
||||
reg := regexp.MustCompile("^git://github.com/jingweno/.+\\.git$")
|
||||
assert.T(t, reg.MatchString(args.Get(2)))
|
||||
|
||||
args = NewArgs([]string{"add", "-p", "jingweno"})
|
||||
transformRemoteArgs(args)
|
||||
|
@ -34,7 +39,22 @@ func TestTransformRemoteArgs(t *testing.T) {
|
|||
assert.Equal(t, 3, args.Size())
|
||||
assert.Equal(t, "add", args.First())
|
||||
assert.Equal(t, "jingweno", args.Get(1))
|
||||
assert.Equal(t, "git@github.com:jingweno/gh.git", args.Get(2))
|
||||
reg = regexp.MustCompile("^git@github.com:jingweno/.+\\.git$")
|
||||
assert.T(t, reg.MatchString(args.Get(2)))
|
||||
|
||||
github.DefaultConfigFile = "./test_support/gh"
|
||||
config := github.Config{User: "jingweno", Token: "123"}
|
||||
github.SaveConfig(&config)
|
||||
defer os.RemoveAll(filepath.Dir(github.DefaultConfigFile))
|
||||
|
||||
args = NewArgs([]string{"add", "origin"})
|
||||
transformRemoteArgs(args)
|
||||
|
||||
assert.Equal(t, 3, args.Size())
|
||||
assert.Equal(t, "add", args.First())
|
||||
assert.Equal(t, "origin", args.Get(1))
|
||||
reg = regexp.MustCompile("^git://github.com/.+/.+\\.git$")
|
||||
assert.T(t, reg.MatchString(args.Get(2)))
|
||||
|
||||
args = NewArgs([]string{"add", "jingweno", "git@github.com:jingweno/gh.git"})
|
||||
transformRemoteArgs(args)
|
||||
|
|
|
@ -3,6 +3,7 @@ package commands
|
|||
import (
|
||||
"github.com/jingweno/gh/cmd"
|
||||
"github.com/jingweno/gh/utils"
|
||||
"os"
|
||||
)
|
||||
|
||||
func browserCommand(url string) error {
|
||||
|
@ -15,3 +16,18 @@ func browserCommand(url string) error {
|
|||
c := cmd.NewWithArray(launcher)
|
||||
return c.Exec()
|
||||
}
|
||||
|
||||
func isDir(file string) bool {
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return fi.IsDir()
|
||||
}
|
||||
|
|
|
@ -40,14 +40,44 @@ func (c *Config) FetchPassword() string {
|
|||
return string(pass)
|
||||
}
|
||||
|
||||
var DefaultFile string
|
||||
func (c *Config) FetchCredentials() {
|
||||
var changed bool
|
||||
if c.User == "" {
|
||||
c.FetchUser()
|
||||
changed = true
|
||||
}
|
||||
|
||||
func init() {
|
||||
DefaultFile = filepath.Join(os.Getenv("HOME"), ".config", "gh")
|
||||
if c.Token == "" {
|
||||
password := c.FetchPassword()
|
||||
token, err := findOrCreateToken(c.User, password)
|
||||
utils.Check(err)
|
||||
|
||||
c.Token = token
|
||||
changed = true
|
||||
}
|
||||
|
||||
if changed {
|
||||
err := SaveConfig(c)
|
||||
utils.Check(err)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
DefaultConfigFile = filepath.Join(os.Getenv("HOME"), ".config", "gh")
|
||||
)
|
||||
|
||||
func CurrentConfig() *Config {
|
||||
config, err := loadConfig()
|
||||
if err != nil {
|
||||
config = Config{}
|
||||
}
|
||||
config.FetchCredentials()
|
||||
|
||||
return &config
|
||||
}
|
||||
|
||||
func loadConfig() (Config, error) {
|
||||
return loadFrom(DefaultFile)
|
||||
return loadFrom(DefaultConfigFile)
|
||||
}
|
||||
|
||||
func loadFrom(filename string) (Config, error) {
|
||||
|
@ -74,8 +104,8 @@ func doLoadFrom(f *os.File) (Config, error) {
|
|||
return c, nil
|
||||
}
|
||||
|
||||
func saveConfig(config *Config) error {
|
||||
return saveTo(DefaultFile, config)
|
||||
func SaveConfig(config *Config) error {
|
||||
return saveTo(DefaultConfigFile, config)
|
||||
}
|
||||
|
||||
func saveTo(filename string, config *Config) error {
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"github.com/jingweno/gh/git"
|
||||
"github.com/jingweno/gh/utils"
|
||||
"github.com/jingweno/octokat"
|
||||
)
|
||||
|
||||
|
@ -87,9 +86,7 @@ func (gh *GitHub) ExpandRemoteUrl(owner, name string, isSSH bool) (url string) {
|
|||
project := gh.Project
|
||||
if owner == "origin" {
|
||||
config := gh.config
|
||||
project.Owner = config.FetchUser()
|
||||
} else {
|
||||
project.Owner = owner
|
||||
owner = config.FetchUser()
|
||||
}
|
||||
|
||||
return project.GitURL(name, owner, isSSH)
|
||||
|
@ -134,27 +131,15 @@ func findOrCreateToken(user, password string) (string, error) {
|
|||
|
||||
func (gh *GitHub) client() *octokat.Client {
|
||||
config := gh.config
|
||||
if config.User == "" {
|
||||
config.FetchUser()
|
||||
}
|
||||
|
||||
if config.Token == "" {
|
||||
password := config.FetchPassword()
|
||||
token, err := findOrCreateToken(config.User, password)
|
||||
utils.Check(err)
|
||||
|
||||
config.Token = token
|
||||
err = saveConfig(config)
|
||||
utils.Check(err)
|
||||
}
|
||||
config.FetchCredentials()
|
||||
|
||||
return octokat.NewClient().WithToken(config.Token)
|
||||
}
|
||||
|
||||
func New() *GitHub {
|
||||
project := CurrentProject()
|
||||
c, _ := loadConfig()
|
||||
c := CurrentConfig()
|
||||
c.FetchUser()
|
||||
|
||||
return &GitHub{project, &c}
|
||||
return &GitHub{project, c}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче