зеркало из https://github.com/mislav/hub.git
Merge branch 'pr-merge' into master
This commit is contained in:
Коммит
222d11022f
|
@ -222,7 +222,7 @@ These GitHub commands are provided by hub:
|
|||
fork Make a fork of a remote repository on GitHub and add as remote
|
||||
gist Make a gist
|
||||
issue List or create GitHub issues
|
||||
pr List or checkout GitHub pull requests
|
||||
pr Manage GitHub pull requests
|
||||
pull-request Open a pull request on GitHub
|
||||
release List or create GitHub releases
|
||||
sync Fetch git objects from upstream and update branches
|
||||
|
|
|
@ -19,6 +19,8 @@ change the state of the pull request. However, the pull request will get
|
|||
auto-closed and marked as "merged" as soon as the newly created merge commit is
|
||||
pushed to the default branch of the remote repository.
|
||||
|
||||
To merge a pull request remotely, use ''hub pr merge''.
|
||||
|
||||
## Examples:
|
||||
$ hub merge https://github.com/jingweno/gh/pull/73
|
||||
> git fetch origin refs/pull/73/head
|
||||
|
@ -26,7 +28,7 @@ pushed to the default branch of the remote repository.
|
|||
|
||||
## See also:
|
||||
|
||||
hub-checkout(1), hub(1), git-merge(1)
|
||||
hub-pr(1), hub-checkout(1), hub(1), git-merge(1)
|
||||
`,
|
||||
}
|
||||
|
||||
|
|
109
commands/pr.go
109
commands/pr.go
|
@ -19,6 +19,7 @@ pr list [-s <STATE>] [-h <HEAD>] [-b <BASE>] [-o <SORT_KEY> [-^]] [-f <FORMAT>]
|
|||
pr checkout <PR-NUMBER> [<BRANCH>]
|
||||
pr show [-uc] [-f <FORMAT>] [-h <HEAD>]
|
||||
pr show [-uc] [-f <FORMAT>] <PR-NUMBER>
|
||||
pr merge [-d] [--squash | --rebase] <PR-NUMBER> [-m <MESSAGE> | -F <FILE>] [--head-sha <COMMIT-SHA>]
|
||||
`,
|
||||
Long: `Manage GitHub Pull Requests for the current repository.
|
||||
|
||||
|
@ -38,6 +39,11 @@ pr show [-uc] [-f <FORMAT>] <PR-NUMBER>
|
|||
the current branch name. With ''--format'', print information about the
|
||||
pull request instead of opening it.
|
||||
|
||||
* _merge_:
|
||||
Merge a pull request in the current repository remotely. Select an
|
||||
alternate merge method with ''--squash'' or ''--rebase''. Change the
|
||||
commit subject and body with ''--message'' or ''--file''.
|
||||
|
||||
## Options:
|
||||
|
||||
-s, --state <STATE>
|
||||
|
@ -146,6 +152,29 @@ pr show [-uc] [-f <FORMAT>] <PR-NUMBER>
|
|||
-c, --copy
|
||||
Put the pull request URL to clipboard instead of opening it.
|
||||
|
||||
-m, --message <MESSAGE>
|
||||
The text up to the first blank line in <MESSAGE> is treated as the commit
|
||||
subject for the merge commit, and the rest is used as commit body.
|
||||
|
||||
When multiple ''--message'' are passed, their values are concatenated with a
|
||||
blank line in-between.
|
||||
|
||||
-F, --file <FILE>
|
||||
Read the subject and body for the merge commit from <FILE>. Pass "-" to read
|
||||
from standard input instead. See ''--message'' for the formatting rules.
|
||||
|
||||
--head-sha <COMMIT-SHA>
|
||||
Ensure that the head of the pull request matches the commit SHA when merging.
|
||||
|
||||
--squash
|
||||
Squash commits instead of creating a merge commit when merging a pull request.
|
||||
|
||||
--rebase
|
||||
Rebase commits on top of the base branch when merging a pull request.
|
||||
|
||||
-d, --delete-branch
|
||||
Delete the head branch after successfully merging a pull request.
|
||||
|
||||
## See also:
|
||||
|
||||
hub-issue(1), hub-pull-request(1), hub(1)
|
||||
|
@ -173,7 +202,20 @@ hub-issue(1), hub-pull-request(1), hub(1)
|
|||
-c, --copy
|
||||
-f, --format FORMAT
|
||||
--color
|
||||
`,
|
||||
`,
|
||||
}
|
||||
|
||||
cmdMergePr = &Command{
|
||||
Key: "merge",
|
||||
Run: mergePr,
|
||||
KnownFlags: `
|
||||
-m, --message MESSAGE
|
||||
-F, --file FILE
|
||||
--head-sha COMMIT
|
||||
--squash
|
||||
--rebase
|
||||
-d, --delete-branch
|
||||
`,
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -181,6 +223,7 @@ func init() {
|
|||
cmdPr.Use(cmdListPulls)
|
||||
cmdPr.Use(cmdCheckoutPr)
|
||||
cmdPr.Use(cmdShowPr)
|
||||
cmdPr.Use(cmdMergePr)
|
||||
CmdRunner.Use(cmdPr)
|
||||
}
|
||||
|
||||
|
@ -413,6 +456,70 @@ func deducePushTarget(branch *github.Branch, owner string) (*github.Project, err
|
|||
return remote.Project()
|
||||
}
|
||||
|
||||
func mergePr(command *Command, args *Args) {
|
||||
words := args.Words()
|
||||
if len(words) == 0 {
|
||||
utils.Check(fmt.Errorf("Error: No pull request number given"))
|
||||
}
|
||||
|
||||
prNumber, err := strconv.Atoi(words[0])
|
||||
utils.Check(err)
|
||||
|
||||
params := map[string]interface{}{
|
||||
"merge_method": "merge",
|
||||
}
|
||||
if args.Flag.Bool("--squash") {
|
||||
params["merge_method"] = "squash"
|
||||
}
|
||||
if args.Flag.Bool("--rebase") {
|
||||
params["merge_method"] = "rebase"
|
||||
}
|
||||
|
||||
msgs := args.Flag.AllValues("--message")
|
||||
if len(msgs) > 0 {
|
||||
params["commit_title"] = msgs[0]
|
||||
params["commit_message"] = strings.Join(msgs[1:], "\n\n")
|
||||
} else if args.Flag.HasReceived("--file") {
|
||||
content, err := msgFromFile(args.Flag.Value("--file"))
|
||||
utils.Check(err)
|
||||
params["commit_title"], params["commit_message"] = github.SplitTitleBody(content)
|
||||
}
|
||||
|
||||
if headSHA := args.Flag.Value("--head-sha"); headSHA != "" {
|
||||
params["sha"] = args.Flag.Value("--head-sha")
|
||||
}
|
||||
|
||||
localRepo, err := github.LocalRepo()
|
||||
utils.Check(err)
|
||||
|
||||
project, err := localRepo.MainProject()
|
||||
utils.Check(err)
|
||||
|
||||
args.NoForward()
|
||||
if args.Noop {
|
||||
ui.Printf("Would merge pull request #%d for %s\n", prNumber, project)
|
||||
return
|
||||
}
|
||||
|
||||
gh := github.NewClient(project.Host)
|
||||
_, err = gh.MergePullRequest(project, prNumber, params)
|
||||
utils.Check(err)
|
||||
|
||||
if !args.Flag.Bool("--delete-branch") {
|
||||
return
|
||||
}
|
||||
|
||||
pr, err := gh.PullRequest(project, strconv.Itoa(prNumber))
|
||||
utils.Check(err)
|
||||
if !pr.IsSameRepo() {
|
||||
return
|
||||
}
|
||||
|
||||
branchName := pr.Head.Ref
|
||||
err = gh.DeleteBranch(project, branchName)
|
||||
utils.Check(err)
|
||||
}
|
||||
|
||||
func formatPullRequest(pr github.PullRequest, format string, colorize bool) string {
|
||||
placeholders := formatIssuePlaceholders(github.Issue(pr), colorize)
|
||||
delete(placeholders, "NC")
|
||||
|
|
|
@ -0,0 +1,215 @@
|
|||
Feature: hub pr merge
|
||||
Background:
|
||||
Given I am in "git://github.com/friederbluemle/hub.git" git repo
|
||||
And I am "friederbluemle" on github.com with OAuth token "OTOKEN"
|
||||
|
||||
Scenario: Default merge
|
||||
Given the GitHub API server:
|
||||
"""
|
||||
put('/repos/friederbluemle/hub/pulls/12/merge'){
|
||||
assert :merge_method => "merge",
|
||||
:commit_title => :no,
|
||||
:commit_message => :no,
|
||||
:sha => :no
|
||||
|
||||
json :merged => true,
|
||||
:sha => "MERGESHA",
|
||||
:message => "All done!"
|
||||
}
|
||||
"""
|
||||
When I successfully run `hub pr merge 12`
|
||||
Then the output should contain exactly ""
|
||||
|
||||
Scenario: Squash merge
|
||||
Given the GitHub API server:
|
||||
"""
|
||||
put('/repos/friederbluemle/hub/pulls/12/merge'){
|
||||
assert :merge_method => "squash",
|
||||
:commit_title => :no,
|
||||
:commit_message => :no,
|
||||
:sha => :no
|
||||
|
||||
json :merged => true,
|
||||
:sha => "MERGESHA",
|
||||
:message => "All done!"
|
||||
}
|
||||
"""
|
||||
When I successfully run `hub pr merge --squash 12`
|
||||
Then the output should contain exactly ""
|
||||
|
||||
Scenario: Merge with rebase
|
||||
Given the GitHub API server:
|
||||
"""
|
||||
put('/repos/friederbluemle/hub/pulls/12/merge'){
|
||||
assert :merge_method => "rebase",
|
||||
:commit_title => :no,
|
||||
:commit_message => :no,
|
||||
:sha => :no
|
||||
|
||||
json :merged => true,
|
||||
:sha => "MERGESHA",
|
||||
:message => "All done!"
|
||||
}
|
||||
"""
|
||||
When I successfully run `hub pr merge --rebase 12`
|
||||
Then the output should contain exactly ""
|
||||
|
||||
Scenario: Merge with title
|
||||
Given the GitHub API server:
|
||||
"""
|
||||
put('/repos/friederbluemle/hub/pulls/12/merge'){
|
||||
assert :commit_title => "mytitle",
|
||||
:commit_message => ""
|
||||
|
||||
json :merged => true,
|
||||
:sha => "MERGESHA",
|
||||
:message => "All done!"
|
||||
}
|
||||
"""
|
||||
When I successfully run `hub pr merge 12 -m mytitle`
|
||||
Then the output should contain exactly ""
|
||||
|
||||
Scenario: Merge with title and body
|
||||
Given the GitHub API server:
|
||||
"""
|
||||
put('/repos/friederbluemle/hub/pulls/12/merge'){
|
||||
assert :commit_title => "mytitle",
|
||||
:commit_message => "msg1\n\nmsg2"
|
||||
|
||||
json :merged => true,
|
||||
:sha => "MERGESHA",
|
||||
:message => "All done!"
|
||||
}
|
||||
"""
|
||||
When I successfully run `hub pr merge 12 -m mytitle -m msg1 -m msg2`
|
||||
Then the output should contain exactly ""
|
||||
|
||||
Scenario: Merge with title and body from file
|
||||
Given a file named "msg.txt" with:
|
||||
"""
|
||||
mytitle
|
||||
|
||||
msg1
|
||||
|
||||
msg2
|
||||
"""
|
||||
Given the GitHub API server:
|
||||
"""
|
||||
put('/repos/friederbluemle/hub/pulls/12/merge'){
|
||||
assert :commit_title => "mytitle",
|
||||
:commit_message => "msg1\n\nmsg2"
|
||||
|
||||
json :merged => true,
|
||||
:sha => "MERGESHA",
|
||||
:message => "All done!"
|
||||
}
|
||||
"""
|
||||
When I successfully run `hub pr merge 12 -F msg.txt`
|
||||
Then the output should contain exactly ""
|
||||
|
||||
Scenario: Merge with head SHA
|
||||
Given the GitHub API server:
|
||||
"""
|
||||
put('/repos/friederbluemle/hub/pulls/12/merge'){
|
||||
assert :sha => "MYSHA"
|
||||
|
||||
json :merged => true,
|
||||
:sha => "MERGESHA",
|
||||
:message => "All done!"
|
||||
}
|
||||
"""
|
||||
When I successfully run `hub pr merge 12 --head-sha MYSHA`
|
||||
Then the output should contain exactly ""
|
||||
|
||||
Scenario: Delete branch
|
||||
Given the GitHub API server:
|
||||
"""
|
||||
put('/repos/friederbluemle/hub/pulls/12/merge'){
|
||||
json :merged => true,
|
||||
:sha => "MERGESHA",
|
||||
:message => "All done!"
|
||||
}
|
||||
|
||||
get('/repos/friederbluemle/hub/pulls/12'){
|
||||
json \
|
||||
:number => 12,
|
||||
:state => "merged",
|
||||
:base => {
|
||||
:ref => "main",
|
||||
:label => "friederbluemle:main",
|
||||
:repo => { :owner => { :login => "friederbluemle" } }
|
||||
},
|
||||
:head => {
|
||||
:ref => "patch-1",
|
||||
:label => "friederbluemle:patch-1",
|
||||
:repo => { :owner => { :login => "friederbluemle" } }
|
||||
}
|
||||
}
|
||||
|
||||
delete('/repos/friederbluemle/hub/git/refs/heads/patch-1'){
|
||||
status 204
|
||||
}
|
||||
"""
|
||||
When I successfully run `hub pr merge -d 12`
|
||||
Then the output should contain exactly ""
|
||||
|
||||
Scenario: Delete already deleted branch
|
||||
Given the GitHub API server:
|
||||
"""
|
||||
put('/repos/friederbluemle/hub/pulls/12/merge'){
|
||||
json :merged => true,
|
||||
:sha => "MERGESHA",
|
||||
:message => "All done!"
|
||||
}
|
||||
|
||||
get('/repos/friederbluemle/hub/pulls/12'){
|
||||
json \
|
||||
:number => 12,
|
||||
:state => "merged",
|
||||
:base => {
|
||||
:ref => "main",
|
||||
:label => "friederbluemle:main",
|
||||
:repo => { :owner => { :login => "friederbluemle" } }
|
||||
},
|
||||
:head => {
|
||||
:ref => "patch-1",
|
||||
:label => "friederbluemle:patch-1",
|
||||
:repo => { :owner => { :login => "friederbluemle" } }
|
||||
}
|
||||
}
|
||||
|
||||
delete('/repos/friederbluemle/hub/git/refs/heads/patch-1'){
|
||||
status 422
|
||||
json :message => "Invalid branch name"
|
||||
}
|
||||
"""
|
||||
When I successfully run `hub pr merge -d 12`
|
||||
Then the output should contain exactly ""
|
||||
|
||||
Scenario: Delete branch on cross-repo PR
|
||||
Given the GitHub API server:
|
||||
"""
|
||||
put('/repos/friederbluemle/hub/pulls/12/merge'){
|
||||
json :merged => true,
|
||||
:sha => "MERGESHA",
|
||||
:message => "All done!"
|
||||
}
|
||||
|
||||
get('/repos/friederbluemle/hub/pulls/12'){
|
||||
json \
|
||||
:number => 12,
|
||||
:state => "merged",
|
||||
:base => {
|
||||
:ref => "main",
|
||||
:label => "friederbluemle:main",
|
||||
:repo => { :owner => { :login => "friederbluemle" } }
|
||||
},
|
||||
:head => {
|
||||
:ref => "patch-1",
|
||||
:label => "monalisa:patch-1",
|
||||
:repo => { :owner => { :login => "monalisa" } }
|
||||
}
|
||||
}
|
||||
"""
|
||||
When I successfully run `hub pr merge -d 12`
|
||||
Then the output should contain exactly ""
|
|
@ -146,6 +146,48 @@ func (client *Client) CreatePullRequest(project *Project, params map[string]inte
|
|||
return
|
||||
}
|
||||
|
||||
type PullRequestMergeResponse struct {
|
||||
SHA string
|
||||
Merged bool
|
||||
Message string
|
||||
}
|
||||
|
||||
func (client *Client) MergePullRequest(project *Project, prNumber int, params map[string]interface{}) (mr PullRequestMergeResponse, err error) {
|
||||
api, err := client.simpleAPI()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
res, err := api.PutJSON(fmt.Sprintf("repos/%s/%s/pulls/%d/merge", project.Owner, project.Name, prNumber), params)
|
||||
if err = checkStatus(200, "merging pull request", res, err); err != nil {
|
||||
return
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
err = res.Unmarshal(&mr)
|
||||
return
|
||||
}
|
||||
|
||||
func (client *Client) DeleteBranch(project *Project, branchName string) (err error) {
|
||||
api, err := client.simpleAPI()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
res, err := api.Delete(fmt.Sprintf("repos/%s/%s/git/refs/heads/%s", project.Owner, project.Name, branchName))
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode == 422 {
|
||||
return
|
||||
}
|
||||
}
|
||||
if err = checkStatus(204, "deleting branch", res, err); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (client *Client) RequestReview(project *Project, prNumber int, params map[string]interface{}) (err error) {
|
||||
api, err := client.simpleAPI()
|
||||
if err != nil {
|
||||
|
|
|
@ -476,6 +476,10 @@ func (c *simpleClient) PostJSONPreview(path string, payload interface{}, mimeTyp
|
|||
})
|
||||
}
|
||||
|
||||
func (c *simpleClient) PutJSON(path string, payload interface{}) (*simpleResponse, error) {
|
||||
return c.jsonRequest("PUT", path, payload, nil)
|
||||
}
|
||||
|
||||
func (c *simpleClient) PatchJSON(path string, payload interface{}) (*simpleResponse, error) {
|
||||
return c.jsonRequest("PATCH", path, payload, nil)
|
||||
}
|
||||
|
|
|
@ -38,14 +38,7 @@ func (b *MessageBuilder) Extract() (title, body string, err error) {
|
|||
content = nl.ReplaceAllString(content, "\n")
|
||||
}
|
||||
|
||||
parts := strings.SplitN(content, "\n\n", 2)
|
||||
if len(parts) >= 1 {
|
||||
title = strings.TrimSpace(strings.Replace(parts[0], "\n", " ", -1))
|
||||
}
|
||||
if len(parts) >= 2 {
|
||||
body = strings.TrimSpace(parts[1])
|
||||
}
|
||||
|
||||
title, body = SplitTitleBody(content)
|
||||
if title == "" {
|
||||
defer b.Cleanup()
|
||||
}
|
||||
|
@ -58,3 +51,14 @@ func (b *MessageBuilder) Cleanup() {
|
|||
b.editor.DeleteFile()
|
||||
}
|
||||
}
|
||||
|
||||
func SplitTitleBody(content string) (title string, body string) {
|
||||
parts := strings.SplitN(content, "\n\n", 2)
|
||||
if len(parts) >= 1 {
|
||||
title = strings.TrimSpace(strings.Replace(parts[0], "\n", " ", -1))
|
||||
}
|
||||
if len(parts) >= 2 {
|
||||
body = strings.TrimSpace(parts[1])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче