зеркало из https://github.com/mislav/hub.git
Merge branch 'cherry-pick'
This commit is contained in:
Коммит
4f7494d744
|
@ -2,7 +2,6 @@ package commands
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jingweno/gh/git"
|
||||
"github.com/jingweno/gh/utils"
|
||||
)
|
||||
|
||||
|
@ -43,13 +42,8 @@ func transformCheckoutArgs(args *Args) error {
|
|||
user := pullRequest.User.Login
|
||||
branch := pullRequest.Head.Ref
|
||||
|
||||
remoteExists, err := checkIfRemoteExists(user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args.RemoveParam(0) // Remove the pull request URL
|
||||
if remoteExists {
|
||||
if hasGitRemote(user) {
|
||||
updateExistingRemote(args, user, branch)
|
||||
} else {
|
||||
sshURL, err := convertToGitURL(pullRequest)
|
||||
|
@ -74,21 +68,6 @@ func transformCheckoutArgs(args *Args) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func checkIfRemoteExists(remote string) (bool, error) {
|
||||
remotes, err := git.Remotes()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, r := range remotes {
|
||||
if r.Name == remote {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func updateExistingRemote(args *Args, user, branch string) {
|
||||
args.Before("git", "remote", "set-branches", "--add", user, branch)
|
||||
remoteURL := fmt.Sprintf("+refs/heads/%s:refs/remotes/%s/%s", branch, user, branch)
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jingweno/gh/github"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
var cmdCherryPick = &Command{
|
||||
Run: cherryPick,
|
||||
GitExtension: true,
|
||||
Usage: "cherry-pick GITHUB-REF",
|
||||
Short: "Apply the changes introduced by some existing commits",
|
||||
Long: `Cherry-pick a commit from a fork using either full URL to the commit
|
||||
or GitHub-flavored Markdown notation, which is user@sha. If the remote
|
||||
doesn't yet exist, it will be added. A git-fetch(1) user is issued
|
||||
prior to the cherry-pick attempt.
|
||||
`,
|
||||
}
|
||||
|
||||
/*
|
||||
$ gh cherry-pick https://github.com/jingweno/gh/commit/a319d88#comments
|
||||
> git remote add -f jingweno git://github.com/jingweno/gh.git
|
||||
> git cherry-pick a319d88
|
||||
|
||||
$ gh cherry-pick jingweno@a319d88
|
||||
> git remote add -f jingweno git://github.com/jingweno/gh.git
|
||||
> git cherry-pick a319d88
|
||||
|
||||
$ gh cherry-pick jingweno@SHA
|
||||
> git fetch jingweno
|
||||
> git cherry-pick SHA
|
||||
*/
|
||||
func cherryPick(command *Command, args *Args) {
|
||||
if args.IndexOfParam("-m") == -1 && args.IndexOfParam("--mainline") == -1 {
|
||||
transformCherryPickArgs(args)
|
||||
}
|
||||
}
|
||||
|
||||
func transformCherryPickArgs(args *Args) {
|
||||
ref := args.LastParam()
|
||||
project, sha := parseCherryPickProjectAndSha(ref)
|
||||
if project != nil {
|
||||
args.ReplaceParam(args.IndexOfParam(ref), sha)
|
||||
|
||||
if hasGitRemote(project.Owner) {
|
||||
args.Before("git", "fetch", project.Owner)
|
||||
} else {
|
||||
args.Before("git", "remote", "add", "-f", project.Owner, project.GitURL("", "", false))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parseCherryPickProjectAndSha(ref string) (project *github.Project, sha string) {
|
||||
url, err := github.ParseURL(ref)
|
||||
if err == nil {
|
||||
commitRegex := regexp.MustCompile("^commit\\/([a-f0-9]{7,40})")
|
||||
projectPath := url.ProjectPath()
|
||||
if commitRegex.MatchString(projectPath) {
|
||||
sha = commitRegex.FindStringSubmatch(projectPath)[1]
|
||||
project = &url.Project
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ownerWithShaRegexp := regexp.MustCompile(fmt.Sprintf("^(%s)@([a-f0-9]{7,40})$"))
|
||||
if ownerWithShaRegexp.MatchString(ref) {
|
||||
matches := ownerWithShaRegexp.FindStringSubmatch(ref)
|
||||
sha = matches[2]
|
||||
project = github.CurrentProject()
|
||||
project.Owner = matches[1]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"github.com/bmizerany/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseCherryPickProjectAndSha(t *testing.T) {
|
||||
ref := "https://github.com/jingweno/gh/commit/a319d88#comments"
|
||||
project, sha := parseCherryPickProjectAndSha(ref)
|
||||
|
||||
assert.Equal(t, "jingweno", project.Owner)
|
||||
assert.Equal(t, "gh", project.Name)
|
||||
assert.Equal(t, "a319d88", sha)
|
||||
|
||||
ref = "https://github.com/jingweno/gh/commit/a319d88#comments"
|
||||
project, sha = parseCherryPickProjectAndSha(ref)
|
||||
|
||||
assert.Equal(t, "jingweno", project.Owner)
|
||||
assert.Equal(t, "gh", project.Name)
|
||||
assert.Equal(t, "a319d88", sha)
|
||||
}
|
||||
|
||||
func TestTransformCherryPickArgs(t *testing.T) {
|
||||
args := NewArgs([]string{"cherry-pick", "https://github.com/jingweno/gh/commit/a319d88#comments"})
|
||||
transformCherryPickArgs(args)
|
||||
|
||||
cmds := args.Commands()
|
||||
assert.Equal(t, 2, len(cmds))
|
||||
assert.Equal(t, "git remote add -f jingweno git://github.com/jingweno/gh.git", cmds[0].String())
|
||||
assert.Equal(t, "git cherry-pick a319d88", cmds[1].String())
|
||||
}
|
|
@ -55,6 +55,7 @@ var Branching = []*Command{
|
|||
cmdCheckout,
|
||||
cmdMerge,
|
||||
cmdApply,
|
||||
cmdCherryPick,
|
||||
}
|
||||
|
||||
var Remote = []*Command{
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"github.com/jingweno/gh/git"
|
||||
"github.com/jingweno/gh/github"
|
||||
"github.com/jingweno/gh/utils"
|
||||
"regexp"
|
||||
|
@ -41,17 +40,12 @@ func fetch(command *Command, args *Args) {
|
|||
}
|
||||
|
||||
func tranformFetchArgs(args *Args) error {
|
||||
remotes, err := git.Remotes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
names := parseRemoteNames(args)
|
||||
gh := github.New()
|
||||
projects := []github.Project{}
|
||||
ownerRegexp := regexp.MustCompile(OwnerRe)
|
||||
for _, name := range names {
|
||||
if ownerRegexp.MatchString(name) && !isRemoteExist(remotes, name) {
|
||||
if ownerRegexp.MatchString(name) && !hasGitRemote(name) {
|
||||
project := github.NewProjectFromNameAndOwner("", name)
|
||||
repo, err := gh.Repository(project)
|
||||
if err != nil {
|
||||
|
@ -95,13 +89,3 @@ func parseRemoteNames(args *Args) (names []string) {
|
|||
|
||||
return
|
||||
}
|
||||
|
||||
func isRemoteExist(remotes []*git.GitRemote, name string) bool {
|
||||
for _, r := range remotes {
|
||||
if r.Name == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ package commands
|
|||
import (
|
||||
"fmt"
|
||||
"github.com/jingweno/gh/github"
|
||||
"github.com/jingweno/gh/git"
|
||||
"github.com/jingweno/gh/utils"
|
||||
"github.com/jingweno/octokat"
|
||||
"os"
|
||||
"regexp"
|
||||
|
@ -23,13 +25,19 @@ func isDir(file string) bool {
|
|||
return fi.IsDir()
|
||||
}
|
||||
|
||||
func parsePullRequestId(url string) string {
|
||||
pullURLRegex := regexp.MustCompile("https://github\\.com/.+/.+/pull/(\\d+)")
|
||||
if pullURLRegex.MatchString(url) {
|
||||
return pullURLRegex.FindStringSubmatch(url)[1]
|
||||
func parsePullRequestId(rawurl string) (id string) {
|
||||
url, err := github.ParseURL(rawurl)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return ""
|
||||
pullURLRegex := regexp.MustCompile("^pull/(\\d+)")
|
||||
projectPath := url.ProjectPath()
|
||||
if pullURLRegex.MatchString(projectPath) {
|
||||
id = pullURLRegex.FindStringSubmatch(projectPath)[1]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func fetchPullRequest(id string) (*octokat.PullRequest, error) {
|
||||
|
@ -52,12 +60,12 @@ func convertToGitURL(pullRequest *octokat.PullRequest) (string, error) {
|
|||
user := pullRequest.User.Login
|
||||
isSSH := pullRequest.Head.Repo.Private
|
||||
|
||||
project, err := github.ParseProjectFromURL(pullRequestURL)
|
||||
url, err := github.ParseURL(pullRequestURL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return project.GitURL("", user, isSSH), nil
|
||||
return url.GitURL("", user, isSSH), nil
|
||||
}
|
||||
|
||||
func parseRepoNameOwner(nameWithOwner string) (owner, name string, match bool) {
|
||||
|
@ -80,3 +88,15 @@ func parseRepoNameOwner(nameWithOwner string) (owner, name string, match bool) {
|
|||
|
||||
return
|
||||
}
|
||||
|
||||
func hasGitRemote(name string) bool {
|
||||
remotes, err := git.Remotes()
|
||||
utils.Check(err)
|
||||
for _, remote := range remotes {
|
||||
if remote.Name == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -78,22 +78,17 @@ func CurrentProject() *Project {
|
|||
return &Project{name, owner}
|
||||
}
|
||||
|
||||
func ParseProjectFromURL(uu string) (*Project, error) {
|
||||
u, err := url.Parse(uu)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func NewProjectFromURL(url *url.URL) (*Project, error) {
|
||||
if url.Host != GitHubHost || url.Scheme != "https" {
|
||||
return nil, fmt.Errorf("Invalid GitHub URL: %s", url)
|
||||
}
|
||||
|
||||
if u.Host != GitHubHost || u.Scheme != "https" {
|
||||
return nil, fmt.Errorf("Invalid GitHub URL: %s", u)
|
||||
parts := strings.SplitN(url.Path, "/", 4)
|
||||
if len(parts) < 2 {
|
||||
return nil, fmt.Errorf("Invalid GitHub URL: %s", url)
|
||||
}
|
||||
|
||||
parts := strings.SplitN(u.Path, "/", 4)
|
||||
if len(parts) >= 2 {
|
||||
return &Project{Name: parts[2], Owner: parts[1]}, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("Invalid GitHub URL: %s", u)
|
||||
return &Project{Name: parts[2], Owner: parts[1]}, nil
|
||||
}
|
||||
|
||||
func NewProjectFromNameAndOwner(name, owner string) Project {
|
||||
|
|
|
@ -84,31 +84,3 @@ func TestMustMatchGitHubURL(t *testing.T) {
|
|||
assert.Equal(t, "jingweno", url[1])
|
||||
assert.Equal(t, "gh", url[2])
|
||||
}
|
||||
|
||||
func TestParseProjectFromURL(t *testing.T) {
|
||||
project, err :=
|
||||
ParseProjectFromURL("https://github.com/jingweno/gh/pulls/21")
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, "jingweno", project.Owner)
|
||||
assert.Equal(t, "gh", project.Name)
|
||||
|
||||
project, err =
|
||||
ParseProjectFromURL("https://github.com/jingweno/gh")
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, "jingweno", project.Owner)
|
||||
assert.Equal(t, "gh", project.Name)
|
||||
|
||||
project, err =
|
||||
ParseProjectFromURL("https://github.com/jingweno/gh/")
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, "jingweno", project.Owner)
|
||||
assert.Equal(t, "gh", project.Name)
|
||||
|
||||
project, err =
|
||||
ParseProjectFromURL("http://github.com/jingweno/gh/")
|
||||
assert.NotEqual(t, nil, err)
|
||||
|
||||
project, err =
|
||||
ParseProjectFromURL("http://github.com/jingweno/")
|
||||
assert.NotEqual(t, nil, err)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package github
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type URL struct {
|
||||
url.URL
|
||||
Project
|
||||
}
|
||||
|
||||
func (url URL) ProjectPath() (projectPath string) {
|
||||
split := strings.SplitN(url.Path, "/", 4)
|
||||
if len(split) > 3 {
|
||||
projectPath = split[3]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func ParseURL(rawurl string) (*URL, error) {
|
||||
url, err := url.Parse(rawurl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
project, err := NewProjectFromURL(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &URL{Project: *project, URL: *url}, nil
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package github
|
||||
|
||||
import (
|
||||
"github.com/bmizerany/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseURL(t *testing.T) {
|
||||
url, err :=
|
||||
ParseURL("https://github.com/jingweno/gh/pulls/21")
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, "jingweno", url.Owner)
|
||||
assert.Equal(t, "gh", url.Name)
|
||||
assert.Equal(t, "pulls/21", url.ProjectPath())
|
||||
|
||||
url, err =
|
||||
ParseURL("https://github.com/jingweno/gh")
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, "jingweno", url.Owner)
|
||||
assert.Equal(t, "gh", url.Name)
|
||||
assert.Equal(t, "", url.ProjectPath())
|
||||
|
||||
url, err =
|
||||
ParseURL("https://github.com/jingweno/gh/")
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, "jingweno", url.Owner)
|
||||
assert.Equal(t, "gh", url.Name)
|
||||
assert.Equal(t, "", url.ProjectPath())
|
||||
|
||||
url, err =
|
||||
ParseURL("http://github.com/jingweno/gh/")
|
||||
assert.NotEqual(t, nil, err)
|
||||
|
||||
url, err =
|
||||
ParseURL("http://github.com/jingweno/")
|
||||
assert.NotEqual(t, nil, err)
|
||||
}
|
Загрузка…
Ссылка в новой задаче