clone.go 3.2 KB
Newer Older
J
Jingwen Owen Ou 已提交
1 2 3
package commands

import (
4
	"fmt"
J
Jingwen Owen Ou 已提交
5 6
	"regexp"
	"strings"
7 8

	"github.com/github/hub/github"
9
	"github.com/github/hub/utils"
J
Jingwen Owen Ou 已提交
10 11 12 13 14
)

var cmdClone = &Command{
	Run:          clone,
	GitExtension: true,
15 16 17 18 19 20 21 22 23 24 25 26 27
	Usage:        "clone [-p] [<OPTIONS>] [<USER>/]<REPOSITORY> [<DESTINATION>]",
	Long: `Clone a repository from GitHub.

## Options:
	-p
		(Deprecated) Clone private repositories over SSH.

	[<USER>/]<REPOSITORY>
		<USER> defaults to your own GitHub username.

	<DESTINATION>
		Directory name to clone into (default: <REPOSITORY>).

28 29 30 31
## Protocol used for cloning

The 'git:' protocol will be used for cloning public repositories, while the SSH
protocol will be used for private repositories and those that you have push
N
nikolas 已提交
32
access to. Alternatively, hub can be configured to use HTTPS protocol for
33 34
everything. See "HTTPS instead of git protocol" and "HUB_PROTOCOL" of hub(1).

35 36 37
## Examples:
		$ hub clone rtomayko/ronn
		> git clone git://github.com/rtomayko/ronn.git
M
Mislav Marohnić 已提交
38 39 40 41

## See also:

hub-fork(1), hub(1), git-clone(1)
42
`,
J
Jingwen Owen Ou 已提交
43 44
}

45 46 47 48
func init() {
	CmdRunner.Use(cmdClone)
}

J
Jingwen Owen Ou 已提交
49
func clone(command *Command, args *Args) {
J
Jingwen Owen Ou 已提交
50
	if !args.IsParamsEmpty() {
J
Jingwen Owen Ou 已提交
51 52 53 54 55 56
		transformCloneArgs(args)
	}
}

func transformCloneArgs(args *Args) {
	isSSH := parseClonePrivateFlag(args)
J
Josh Soref 已提交
57
	hasValueRegexp := regexp.MustCompile("^(--(upload-pack|template|depth|origin|branch|reference|name)|-[ubo])$")
J
Jingwen Owen Ou 已提交
58
	nameWithOwnerRegexp := regexp.MustCompile(NameWithOwnerRe)
J
Jingwen Owen Ou 已提交
59 60
	for i := 0; i < args.ParamsSize(); i++ {
		a := args.Params[i]
61 62

		if strings.HasPrefix(a, "-") {
J
Josh Soref 已提交
63
			if hasValueRegexp.MatchString(a) {
J
Jingwen Owen Ou 已提交
64
				i++
65
			}
J
Jingwen Owen Ou 已提交
66
		} else {
67
			if nameWithOwnerRegexp.MatchString(a) && !isCloneable(a) {
J
Jingwen Owen Ou 已提交
68
				name, owner := parseCloneNameAndOwner(a)
J
Jingwen Owen Ou 已提交
69
				var host *github.Host
J
Jingwen Owen Ou 已提交
70
				if owner == "" {
71 72
					config := github.CurrentConfig()
					h, err := config.DefaultHost()
73 74 75 76 77
					if err != nil {
						utils.Check(github.FormatError("cloning repository", err))
					}

					host = h
J
Jingwen Owen Ou 已提交
78
					owner = host.User
J
Jingwen Owen Ou 已提交
79 80
				}

J
Jingwen Owen Ou 已提交
81 82 83
				var hostStr string
				if host != nil {
					hostStr = host.Host
J
Jingwen Owen Ou 已提交
84 85
				}

86 87 88 89 90
				expectWiki := strings.HasSuffix(name, ".wiki")
				if expectWiki {
					name = strings.TrimSuffix(name, ".wiki")
				}

J
Jingwen Owen Ou 已提交
91
				project := github.NewProject(owner, name, hostStr)
92 93 94 95 96 97 98 99 100
				gh := github.NewClient(project.Host)
				repo, err := gh.Repository(project)
				if err != nil {
					if strings.Contains(err.Error(), "HTTP 404") {
						err = fmt.Errorf("Error: repository %s/%s doesn't exist", project.Owner, project.Name)
					}
					utils.Check(err)
				}

101 102
				owner = repo.Owner.Login
				name = repo.Name
103 104
				if expectWiki {
					if !repo.HasWiki {
105
						utils.Check(fmt.Errorf("Error: %s/%s doesn't have a wiki", owner, name))
106 107 108 109 110
					} else {
						name = name + ".wiki"
					}
				}

111
				if !isSSH &&
J
Jingwen Owen Ou 已提交
112
					args.Command != "submodule" &&
113
					!github.IsHttpsProtocol() {
114
					isSSH = repo.Private || repo.Permissions.Push
115
				}
J
Jingwen Owen Ou 已提交
116 117 118

				url := project.GitURL(name, owner, isSSH)
				args.ReplaceParam(i, url)
119 120
			}

J
Jingwen Owen Ou 已提交
121 122 123 124 125 126
			break
		}
	}
}

func parseClonePrivateFlag(args *Args) bool {
J
Jingwen Owen Ou 已提交
127 128
	if i := args.IndexOfParam("-p"); i != -1 {
		args.RemoveParam(i)
J
Jingwen Owen Ou 已提交
129 130 131 132 133
		return true
	}

	return false
}
134 135 136 137 138 139 140 141 142 143 144

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]
	}

	return
}