提交 13240c70 编写于 作者: J Jingwen Owen Ou

Implement merge

上级 c4425a04
......@@ -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 TestNewArgs(t *testing.T) {
args := NewArgs([]string{})
assert.Equal(t, "", args.Command)
assert.Equal(t, 0, args.Size())
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"})
item := args.Remove(1)
args := NewArgs([]string{"1", "2", "3", "4"})
assert.Equal(t, "2", item)
item := args.Remove(1)
assert.Equal(t, "3", item)
assert.Equal(t, 2, args.Size())
assert.Equal(t, "1", args.First())
assert.Equal(t, "3", args.Get(1))
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())
......
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
}
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
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册