зеркало из https://github.com/mislav/hub.git
Implement merge
This commit is contained in:
Родитель
c4425a04e4
Коммит
13240c7021
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
|
@ -12,6 +13,10 @@ type Cmd struct {
|
|||
Args []string
|
||||
}
|
||||
|
||||
func (cmd Cmd) String() string {
|
||||
return fmt.Sprintf("%s %s", cmd.Name, strings.Join(cmd.Args, " "))
|
||||
}
|
||||
|
||||
func (cmd *Cmd) WithArg(arg string) *Cmd {
|
||||
cmd.Args = append(cmd.Args, arg)
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
)
|
||||
|
||||
type Args struct {
|
||||
Command string
|
||||
args []string
|
||||
beforeChain []*cmd.Cmd
|
||||
afterChain []*cmd.Cmd
|
||||
|
@ -28,7 +29,7 @@ func (a *Args) Commands() []*cmd.Cmd {
|
|||
}
|
||||
|
||||
func (a *Args) ToCmd() *cmd.Cmd {
|
||||
return cmd.New("git").WithArgs(a.Array()...)
|
||||
return cmd.New("git").WithArg(a.Command).WithArgs(a.Array()...)
|
||||
}
|
||||
|
||||
func (a *Args) Get(i int) string {
|
||||
|
@ -93,7 +94,16 @@ func (a *Args) Prepend(args ...string) {
|
|||
}
|
||||
|
||||
func NewArgs(args []string) *Args {
|
||||
return &Args{args, make([]*cmd.Cmd, 0), make([]*cmd.Cmd, 0)}
|
||||
var command string
|
||||
var a []string
|
||||
if len(args) == 0 {
|
||||
a = []string{}
|
||||
} else {
|
||||
command = args[0]
|
||||
a = args[1:]
|
||||
}
|
||||
|
||||
return &Args{command, a, make([]*cmd.Cmd, 0), make([]*cmd.Cmd, 0)}
|
||||
}
|
||||
|
||||
func removeItem(slice []string, index int) (newSlice []string, item string) {
|
||||
|
|
|
@ -5,12 +5,26 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
func TestRemove(t *testing.T) {
|
||||
args := NewArgs([]string{"1", "2", "3"})
|
||||
item := args.Remove(1)
|
||||
func TestNewArgs(t *testing.T) {
|
||||
args := NewArgs([]string{})
|
||||
assert.Equal(t, "", args.Command)
|
||||
assert.Equal(t, 0, args.Size())
|
||||
|
||||
assert.Equal(t, "2", item)
|
||||
assert.Equal(t, 2, args.Size())
|
||||
assert.Equal(t, "1", args.First())
|
||||
assert.Equal(t, "3", args.Get(1))
|
||||
args = NewArgs([]string{"command"})
|
||||
assert.Equal(t, "command", args.Command)
|
||||
assert.Equal(t, 0, args.Size())
|
||||
|
||||
args = NewArgs([]string{"command", "args"})
|
||||
assert.Equal(t, "command", args.Command)
|
||||
assert.Equal(t, 1, args.Size())
|
||||
}
|
||||
|
||||
func TestRemove(t *testing.T) {
|
||||
args := NewArgs([]string{"1", "2", "3", "4"})
|
||||
|
||||
item := args.Remove(1)
|
||||
assert.Equal(t, "3", item)
|
||||
assert.Equal(t, 2, args.Size())
|
||||
assert.Equal(t, "2", args.First())
|
||||
assert.Equal(t, "4", args.Get(1))
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"github.com/jingweno/gh/git"
|
||||
"github.com/jingweno/gh/github"
|
||||
"github.com/jingweno/gh/utils"
|
||||
"github.com/jingweno/octokat"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
|
@ -28,9 +29,8 @@ set with BRANCH.
|
|||
$ gh checkout https://github.com/jingweno/gh/pull/73 custom-branch-name
|
||||
**/
|
||||
func checkout(command *Command, args *Args) {
|
||||
var err error
|
||||
if !args.IsEmpty() {
|
||||
err = transformCheckoutArgs(args)
|
||||
err := transformCheckoutArgs(args)
|
||||
utils.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
@ -39,17 +39,13 @@ func transformCheckoutArgs(args *Args) error {
|
|||
id := parsePullRequestId(args.First())
|
||||
if id != "" {
|
||||
url := args.Remove(0)
|
||||
gh := github.New()
|
||||
pullRequest, err := gh.PullRequest(id)
|
||||
pullRequest, err := fetchPullRequest(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user := pullRequest.User.Login
|
||||
branch := pullRequest.Head.Ref
|
||||
if pullRequest.Head.Repo.ID == 0 {
|
||||
return fmt.Errorf("%s's fork is not available anymore", user)
|
||||
}
|
||||
|
||||
remoteExists, err := checkIfRemoteExists(user)
|
||||
if err != nil {
|
||||
|
@ -59,10 +55,13 @@ func transformCheckoutArgs(args *Args) error {
|
|||
if remoteExists {
|
||||
updateExistingRemote(args, user, branch)
|
||||
} else {
|
||||
err = addRmote(args, user, branch, url, pullRequest.Head.Repo.Private)
|
||||
isSSH := pullRequest.Head.Repo.Private
|
||||
sshURL, err := convertPullRequestURLToGitURL(url, user, isSSH)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
addRmote(args, user, branch, sshURL)
|
||||
}
|
||||
|
||||
var newBranchName string
|
||||
|
@ -90,6 +89,21 @@ func parsePullRequestId(url string) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
func fetchPullRequest(id string) (*octokat.PullRequest, error) {
|
||||
gh := github.New()
|
||||
pullRequest, err := gh.PullRequest(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pullRequest.Head.Repo.ID == 0 {
|
||||
user := pullRequest.User.Login
|
||||
return nil, fmt.Errorf("%s's fork is not available anymore", user)
|
||||
}
|
||||
|
||||
return pullRequest, nil
|
||||
}
|
||||
|
||||
func checkIfRemoteExists(remote string) (bool, error) {
|
||||
remotes, err := git.Remotes()
|
||||
if err != nil {
|
||||
|
@ -111,14 +125,15 @@ func updateExistingRemote(args *Args, user, branch string) {
|
|||
args.Before("git", "fetch", user, remoteURL)
|
||||
}
|
||||
|
||||
func addRmote(args *Args, user, branch, url string, isPrivate bool) error {
|
||||
project, err := github.ParseProjectFromURL(url)
|
||||
func addRmote(args *Args, user, branch, sshURL string) {
|
||||
args.Before("git", "remote", "add", "-f", "-t", branch, user, sshURL)
|
||||
}
|
||||
|
||||
func convertPullRequestURLToGitURL(pullRequestURL, user string, isSSH bool) (string, error) {
|
||||
project, err := github.ParseProjectFromURL(pullRequestURL)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
sshURL := project.GitURL("", user, isPrivate)
|
||||
args.Before("git", "remote", "add", "-f", "-t", branch, user, sshURL)
|
||||
|
||||
return nil
|
||||
return project.GitURL("", user, isSSH), nil
|
||||
}
|
||||
|
|
|
@ -14,25 +14,25 @@ func TestTransformCloneArgs(t *testing.T) {
|
|||
github.SaveConfig(&config)
|
||||
defer os.RemoveAll(filepath.Dir(github.DefaultConfigFile))
|
||||
|
||||
args := NewArgs([]string{"foo/gh"})
|
||||
args := NewArgs([]string{"clone", "foo/gh"})
|
||||
transformCloneArgs(args)
|
||||
|
||||
assert.Equal(t, 1, args.Size())
|
||||
assert.Equal(t, "git://github.com/foo/gh.git", args.First())
|
||||
|
||||
args = NewArgs([]string{"-p", "foo/gh"})
|
||||
args = NewArgs([]string{"clone", "-p", "foo/gh"})
|
||||
transformCloneArgs(args)
|
||||
|
||||
assert.Equal(t, 1, args.Size())
|
||||
assert.Equal(t, "git@github.com:foo/gh.git", args.First())
|
||||
|
||||
args = NewArgs([]string{"jingweno/gh"})
|
||||
args = NewArgs([]string{"clone", "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"})
|
||||
args = NewArgs([]string{"clone", "-p", "acl-services/devise-acl"})
|
||||
transformCloneArgs(args)
|
||||
|
||||
assert.Equal(t, 1, args.Size())
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
package commands
|
||||
|
||||
import ()
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jingweno/gh/utils"
|
||||
"github.com/jingweno/octokat"
|
||||
)
|
||||
|
||||
var cmdMerge = &Command{
|
||||
Run: merge,
|
||||
|
@ -12,5 +16,51 @@ ID and title, similar to the GitHub Merge Button.
|
|||
`,
|
||||
}
|
||||
|
||||
/*
|
||||
$ gh merge https://github.com/jingweno/gh/pull/73
|
||||
> git fetch git://github.com/jingweno/gh.git +refs/heads/feature:refs/remotes/jingweno/feature
|
||||
> git merge jingweno/feature --no-ff -m 'Merge pull request #73 from jingweno/feature...'
|
||||
*/
|
||||
func merge(command *Command, args *Args) {
|
||||
if !args.IsEmpty() {
|
||||
err := transformMergeArgs(args)
|
||||
utils.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func transformMergeArgs(args *Args) error {
|
||||
id := parsePullRequestId(args.First())
|
||||
if id != "" {
|
||||
pullRequest, err := fetchPullRequest(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = fetchAndMerge(args, pullRequest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func fetchAndMerge(args *Args, pullRequest *octokat.PullRequest) error {
|
||||
user := pullRequest.User.Login
|
||||
branch := pullRequest.Head.Ref
|
||||
isSSH := pullRequest.Head.Repo.Private
|
||||
url, err := convertPullRequestURLToGitURL(pullRequest.URL, user, isSSH)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args.Remove(0) // Remove the pull request URL
|
||||
|
||||
mergeHead := fmt.Sprintf("%s/%s", user, branch)
|
||||
ref := fmt.Sprintf("+refs/heads/%s:refs/remotes/%s", branch, mergeHead)
|
||||
args.Before("git", "fetch", url, ref)
|
||||
mergeMsg := fmt.Sprintf("'Merge pull request #%v from %s\n\n%s'", pullRequest.Id, mergeHead, pullRequest.Title)
|
||||
args.Append(mergeHead, "--no-ff", "-m", mergeMsg)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"github.com/bmizerany/assert"
|
||||
"github.com/jingweno/octokat"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFetchAndMerge(t *testing.T) {
|
||||
url := "https://github.com/jingweno/gh/pull/73"
|
||||
|
||||
args := NewArgs([]string{"merge", url})
|
||||
|
||||
id := 73
|
||||
title := "title"
|
||||
|
||||
userLogin := "jingweno"
|
||||
user := octokat.User{Login: userLogin}
|
||||
|
||||
repoPrivate := true
|
||||
repo := octokat.Repository{Private: repoPrivate}
|
||||
|
||||
headRef := "new-feature"
|
||||
head := octokat.Commit{Ref: headRef, Repo: repo}
|
||||
|
||||
pullRequest := octokat.PullRequest{Id: id, Title: title, URL: url, User: user, Head: head}
|
||||
|
||||
err := fetchAndMerge(args, &pullRequest)
|
||||
assert.Equal(t, nil, err)
|
||||
|
||||
cmds := args.Commands()
|
||||
assert.Equal(t, 2, len(cmds))
|
||||
|
||||
cmd := cmds[0]
|
||||
assert.Equal(t, "git fetch git@github.com:jingweno/gh.git +refs/heads/new-feature:refs/remotes/jingweno/new-feature", cmd.String())
|
||||
|
||||
cmd = cmds[1]
|
||||
assert.Equal(t, "git merge jingweno/new-feature --no-ff -m 'Merge pull request #73 from jingweno/new-feature\n\ntitle'", cmd.String())
|
||||
}
|
|
@ -24,7 +24,7 @@ func TestParseRepoNameOwner(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestTransformRemoteArgs(t *testing.T) {
|
||||
args := NewArgs([]string{"add", "jingweno"})
|
||||
args := NewArgs([]string{"remote", "add", "jingweno"})
|
||||
transformRemoteArgs(args)
|
||||
|
||||
assert.Equal(t, 3, args.Size())
|
||||
|
@ -33,7 +33,7 @@ func TestTransformRemoteArgs(t *testing.T) {
|
|||
reg := regexp.MustCompile("^git://github.com/jingweno/.+\\.git$")
|
||||
assert.T(t, reg.MatchString(args.Get(2)))
|
||||
|
||||
args = NewArgs([]string{"add", "-p", "jingweno"})
|
||||
args = NewArgs([]string{"remote", "add", "-p", "jingweno"})
|
||||
transformRemoteArgs(args)
|
||||
|
||||
assert.Equal(t, 3, args.Size())
|
||||
|
@ -47,7 +47,7 @@ func TestTransformRemoteArgs(t *testing.T) {
|
|||
github.SaveConfig(&config)
|
||||
defer os.RemoveAll(filepath.Dir(github.DefaultConfigFile))
|
||||
|
||||
args = NewArgs([]string{"add", "origin"})
|
||||
args = NewArgs([]string{"remote", "add", "origin"})
|
||||
transformRemoteArgs(args)
|
||||
|
||||
assert.Equal(t, 3, args.Size())
|
||||
|
@ -56,7 +56,7 @@ func TestTransformRemoteArgs(t *testing.T) {
|
|||
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"})
|
||||
args = NewArgs([]string{"remote", "add", "jingweno", "git@github.com:jingweno/gh.git"})
|
||||
transformRemoteArgs(args)
|
||||
|
||||
assert.Equal(t, 3, args.Size())
|
||||
|
|
|
@ -12,29 +12,30 @@ type Runner struct {
|
|||
|
||||
func (r *Runner) Execute() error {
|
||||
args := NewArgs(os.Args[1:])
|
||||
if args.Size() < 1 {
|
||||
if args.Command == "" {
|
||||
usage()
|
||||
}
|
||||
|
||||
expandAlias(args)
|
||||
|
||||
for _, cmd := range All() {
|
||||
if cmd.Name() == args.First() && cmd.Runnable() {
|
||||
cmdArgs := args.Rest()
|
||||
if cmd.Name() == args.Command && cmd.Runnable() {
|
||||
if !cmd.GitExtension {
|
||||
cmd.Flag.Usage = func() {
|
||||
cmd.PrintUsage()
|
||||
}
|
||||
cmdArgs := args.Array()
|
||||
if err := cmd.Flag.Parse(cmdArgs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmdArgs = cmd.Flag.Args()
|
||||
newArgs := []string{cmd.Name()}
|
||||
newArgs = append(newArgs, cmdArgs...)
|
||||
args = NewArgs(newArgs)
|
||||
}
|
||||
|
||||
args = NewArgs(cmdArgs)
|
||||
cmd.Run(cmd, args)
|
||||
args.Prepend(cmd.Name())
|
||||
|
||||
cmds := args.Commands()
|
||||
length := len(cmds)
|
||||
|
@ -55,14 +56,13 @@ func (r *Runner) Execute() error {
|
|||
}
|
||||
}
|
||||
|
||||
return git.SysExec(args.First(), args.Rest()...)
|
||||
return git.SysExec(args.Command, args.Array()...)
|
||||
}
|
||||
|
||||
func expandAlias(args *Args) {
|
||||
cmd := args.First()
|
||||
cmd := args.Command
|
||||
expandedCmd, err := git.Config(fmt.Sprintf("alias.%s", cmd))
|
||||
if err == nil && expandedCmd != "" {
|
||||
args.Remove(0)
|
||||
args.Prepend(expandedCmd)
|
||||
args.Command = expandedCmd
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче