2013-05-29 22:58:46 +04:00
|
|
|
package commands
|
2013-04-11 19:33:43 +04:00
|
|
|
|
|
|
|
import (
|
2013-04-29 00:41:45 +04:00
|
|
|
"fmt"
|
2016-10-22 13:28:32 +03:00
|
|
|
"os"
|
2013-04-24 14:00:45 +04:00
|
|
|
"regexp"
|
2016-08-08 01:43:48 +03:00
|
|
|
"strconv"
|
2013-04-28 21:45:00 +04:00
|
|
|
"strings"
|
2016-10-22 13:10:00 +03:00
|
|
|
"time"
|
2014-02-24 01:34:10 +04:00
|
|
|
|
|
|
|
"github.com/github/hub/git"
|
|
|
|
"github.com/github/hub/github"
|
|
|
|
"github.com/github/hub/utils"
|
2013-04-11 19:33:43 +04:00
|
|
|
)
|
|
|
|
|
2013-06-30 00:54:40 +04:00
|
|
|
var cmdPullRequest = &Command{
|
2016-01-24 11:56:18 +03:00
|
|
|
Run: pullRequest,
|
|
|
|
Usage: `
|
2019-03-29 03:45:27 +03:00
|
|
|
pull-request [-focpd] [-b <BASE>] [-h <HEAD>] [-r <REVIEWERS> ] [-a <ASSIGNEES>] [-M <MILESTONE>] [-l <LABELS>]
|
2018-05-29 18:17:44 +03:00
|
|
|
pull-request -m <MESSAGE> [--edit]
|
2016-08-21 13:26:42 +03:00
|
|
|
pull-request -F <FILE> [--edit]
|
2016-01-24 11:56:18 +03:00
|
|
|
pull-request -i <ISSUE>
|
|
|
|
`,
|
2019-01-21 23:54:34 +03:00
|
|
|
Long: `Create a GitHub Pull Request.
|
2016-01-24 11:56:18 +03:00
|
|
|
|
|
|
|
## Options:
|
|
|
|
-f, --force
|
|
|
|
Skip the check for unpushed commits.
|
|
|
|
|
2019-01-21 23:35:15 +03:00
|
|
|
-m, --message <MESSAGE>
|
2018-11-28 04:25:52 +03:00
|
|
|
The text up to the first blank line in <MESSAGE> is treated as the pull
|
|
|
|
request title, and the rest is used as pull request description in Markdown
|
|
|
|
format.
|
2016-01-24 11:56:18 +03:00
|
|
|
|
2018-11-28 05:05:15 +03:00
|
|
|
If multiple <MESSAGE> options are given, their values are concatenated as
|
|
|
|
separate paragraphs.
|
|
|
|
|
2018-05-03 02:02:52 +03:00
|
|
|
--no-edit
|
|
|
|
Use the message from the first commit on the branch as pull request title
|
|
|
|
and description without opening a text editor.
|
|
|
|
|
2019-01-21 23:35:15 +03:00
|
|
|
-F, --file <FILE>
|
2016-01-24 11:56:18 +03:00
|
|
|
Read the pull request title and description from <FILE>.
|
|
|
|
|
2016-08-21 13:26:42 +03:00
|
|
|
-e, --edit
|
|
|
|
Further edit the contents of <FILE> in a text editor before submitting.
|
|
|
|
|
2019-01-21 23:35:15 +03:00
|
|
|
-i, --issue <ISSUE>
|
2019-01-17 21:43:04 +03:00
|
|
|
Convert <ISSUE> (referenced by its number) to a pull request.
|
Un-deprecate converting issues to pull requests
5 years ago, in anticipation of an API change, I have made the call to
deprecate issue-to-PR conversion in hub. 4f70dd126f46dec14fc341c97c18efae417743c7
Issue-to-PR conversion was wonky at those times, poorly understood, and
was generating a lot of support requests to hub's issue tracker that I
didn't want to deal with. In most cases, people tried to convert issues
that they have no rights over and they would get a cryptic validation
error in the API response.
Since then, there was a consistent plea from the hub community to keep
this feature as some teams seem to rely on this for their workflows. I
have consulted with other GitHub employees about the stableness of this
feature, and anecdotal evidence suggests that lately there haven't been
as many problems around this as there have been in the past. Also, the
GitHub API v3 will not be getting breaking changes, so it sounds like
this feature is here to stay.
Fixes #1927
Ref. #532, #410, #1806, #1770, #1628
2018-12-28 09:52:45 +03:00
|
|
|
|
2018-12-28 19:54:15 +03:00
|
|
|
You can only convert issues authored by you or that which you have admin
|
|
|
|
rights over. In most workflows it is not necessary to convert issues to
|
|
|
|
pull requests; you can simply reference the original issue in the body of
|
|
|
|
the new pull request.
|
2016-01-24 11:56:18 +03:00
|
|
|
|
|
|
|
-o, --browse
|
|
|
|
Open the new pull request in a web browser.
|
|
|
|
|
2016-09-11 19:21:23 +03:00
|
|
|
-c, --copy
|
|
|
|
Put the URL of the new pull request to clipboard instead of printing it.
|
|
|
|
|
2016-10-03 23:40:20 +03:00
|
|
|
-p, --push
|
2016-10-20 23:53:27 +03:00
|
|
|
Push the current branch to <HEAD> before creating the pull request.
|
2016-10-03 23:40:20 +03:00
|
|
|
|
2019-01-21 23:35:15 +03:00
|
|
|
-b, --base <BASE>
|
2018-12-28 09:21:03 +03:00
|
|
|
The base branch in the "[<OWNER>:]<BRANCH>" format. Defaults to the default
|
|
|
|
branch of the upstream repository (usually "master").
|
|
|
|
|
|
|
|
See the "CONVENTIONS" section of hub(1) for more information on how hub
|
|
|
|
selects the defaults in case of multiple git remotes.
|
2016-01-24 11:56:18 +03:00
|
|
|
|
2019-01-21 23:35:15 +03:00
|
|
|
-h, --head <HEAD>
|
2018-12-28 09:21:03 +03:00
|
|
|
The head branch in "[<OWNER>:]<BRANCH>" format. Defaults to the currently
|
|
|
|
checked out branch.
|
2016-01-24 11:56:18 +03:00
|
|
|
|
2019-01-21 23:35:15 +03:00
|
|
|
-r, --reviewer <USERS>
|
2019-06-25 19:07:42 +03:00
|
|
|
A comma-separated list (no spaces around the comma) of GitHub handles to
|
|
|
|
request a review from.
|
2017-04-17 18:13:23 +03:00
|
|
|
|
2019-01-21 23:35:15 +03:00
|
|
|
-a, --assign <USERS>
|
2019-06-25 19:07:42 +03:00
|
|
|
A comma-separated list (no spaces around the comma) of GitHub handles to
|
|
|
|
assign to this pull request.
|
2016-01-24 11:56:18 +03:00
|
|
|
|
2019-01-21 23:35:15 +03:00
|
|
|
-M, --milestone <NAME>
|
2018-02-12 02:26:50 +03:00
|
|
|
The milestone name to add to this pull request. Passing the milestone number
|
|
|
|
is deprecated.
|
2016-01-24 11:56:18 +03:00
|
|
|
|
2019-01-21 23:35:15 +03:00
|
|
|
-l, --labels <LABELS>
|
2019-06-25 19:07:42 +03:00
|
|
|
A comma-separated list (no spaces around the comma) of labels to add to
|
|
|
|
this pull request. Labels will be created if they do not already exist.
|
2019-06-03 02:08:36 +03:00
|
|
|
|
2019-03-28 15:47:37 +03:00
|
|
|
-d, --draft
|
|
|
|
Create the pull request as a draft.
|
2016-01-24 18:50:01 +03:00
|
|
|
|
2019-06-14 16:00:41 +03:00
|
|
|
--no-maintainer-edits
|
|
|
|
When creating a pull request from a fork, this disallows projects
|
|
|
|
maintainers from being able to push to the head branch of this fork.
|
|
|
|
Maintainer edits are allowed by default.
|
|
|
|
|
2018-09-06 13:55:39 +03:00
|
|
|
## Examples:
|
|
|
|
$ hub pull-request
|
|
|
|
[ opens a text editor for writing title and message ]
|
|
|
|
[ creates a pull request for the current branch ]
|
|
|
|
|
|
|
|
$ hub pull-request --base OWNER:master --head MYUSER:my-branch
|
|
|
|
[ creates a pull request with explicit base and head branches ]
|
|
|
|
|
|
|
|
$ hub pull-request --browse -m "My title"
|
|
|
|
[ creates a pull request with the given title and opens it in a browser ]
|
|
|
|
|
2018-09-06 13:57:45 +03:00
|
|
|
$ hub pull-request -F - --edit < path/to/message-template.md
|
2018-09-06 13:55:39 +03:00
|
|
|
[ further edit the title and message received on standard input ]
|
|
|
|
|
2016-10-22 13:28:32 +03:00
|
|
|
## Configuration:
|
|
|
|
|
2018-12-28 19:54:15 +03:00
|
|
|
* 'HUB_RETRY_TIMEOUT':
|
2016-10-22 13:28:32 +03:00
|
|
|
The maximum time to keep retrying after HTTP 422 on '--push' (default: 9).
|
|
|
|
|
2016-01-24 18:50:01 +03:00
|
|
|
## See also:
|
|
|
|
|
|
|
|
hub(1), hub-merge(1), hub-checkout(1)
|
2013-04-11 19:33:43 +04:00
|
|
|
`,
|
|
|
|
}
|
|
|
|
|
|
|
|
func init() {
|
2013-12-30 02:18:14 +04:00
|
|
|
CmdRunner.Use(cmdPullRequest)
|
2013-04-11 19:33:43 +04:00
|
|
|
}
|
|
|
|
|
2013-06-30 00:54:40 +04:00
|
|
|
func pullRequest(cmd *Command, args *Args) {
|
2014-04-02 00:40:02 +04:00
|
|
|
localRepo, err := github.LocalRepo()
|
|
|
|
utils.Check(err)
|
2013-12-06 22:50:19 +04:00
|
|
|
|
2019-05-29 23:34:41 +03:00
|
|
|
currentBranch, currentBranchErr := localRepo.CurrentBranch()
|
2013-12-06 22:50:19 +04:00
|
|
|
|
|
|
|
baseProject, err := localRepo.MainProject()
|
2013-12-06 22:54:11 +04:00
|
|
|
utils.Check(err)
|
2013-12-06 22:50:19 +04:00
|
|
|
|
2014-09-15 04:46:13 +04:00
|
|
|
host, err := github.CurrentConfig().PromptForHost(baseProject.Host)
|
2014-03-28 03:42:14 +04:00
|
|
|
if err != nil {
|
|
|
|
utils.Check(github.FormatError("creating pull request", err))
|
|
|
|
}
|
2016-08-09 22:40:16 +03:00
|
|
|
client := github.NewClientWithHost(host)
|
2013-12-28 13:14:07 +04:00
|
|
|
|
2019-05-29 23:34:41 +03:00
|
|
|
trackedBranch, headProject, _ := localRepo.RemoteBranchAndProject(host.User, false)
|
|
|
|
if headProject == nil {
|
2019-06-25 18:35:31 +03:00
|
|
|
utils.Check(fmt.Errorf("could not determine project for head branch"))
|
2019-05-29 23:34:41 +03:00
|
|
|
}
|
2013-12-06 22:50:19 +04:00
|
|
|
|
2013-12-07 04:51:11 +04:00
|
|
|
var (
|
2013-12-28 13:14:07 +04:00
|
|
|
base, head string
|
2013-12-07 04:51:11 +04:00
|
|
|
)
|
2013-12-08 12:33:48 +04:00
|
|
|
|
2019-01-16 06:22:20 +03:00
|
|
|
if flagPullRequestBase := args.Flag.Value("--base"); flagPullRequestBase != "" {
|
2013-12-08 12:33:48 +04:00
|
|
|
baseProject, base = parsePullRequestProject(baseProject, flagPullRequestBase)
|
2013-12-06 22:50:19 +04:00
|
|
|
}
|
2013-12-07 04:51:11 +04:00
|
|
|
|
2019-01-16 06:22:20 +03:00
|
|
|
if flagPullRequestHead := args.Flag.Value("--head"); flagPullRequestHead != "" {
|
2013-12-08 12:33:48 +04:00
|
|
|
headProject, head = parsePullRequestProject(headProject, flagPullRequestHead)
|
2013-12-06 22:50:19 +04:00
|
|
|
}
|
2013-12-07 04:51:11 +04:00
|
|
|
|
2018-12-28 05:01:30 +03:00
|
|
|
baseRemote, _ := localRepo.RemoteForProject(baseProject)
|
|
|
|
if base == "" && baseRemote != nil {
|
|
|
|
base = localRepo.DefaultBranch(baseRemote).ShortName()
|
2013-12-06 22:50:19 +04:00
|
|
|
}
|
|
|
|
|
2014-03-05 20:37:17 +04:00
|
|
|
if head == "" && trackedBranch != nil {
|
2013-12-28 13:14:07 +04:00
|
|
|
if !trackedBranch.IsRemote() {
|
|
|
|
// the current branch tracking another branch
|
|
|
|
// pretend there's no upstream at all
|
|
|
|
trackedBranch = nil
|
|
|
|
} else {
|
2014-07-30 09:25:23 +04:00
|
|
|
if baseProject.SameAs(headProject) && base == trackedBranch.ShortName() {
|
2013-12-08 12:33:48 +04:00
|
|
|
e := fmt.Errorf(`Aborted: head branch is the same as base ("%s")`, base)
|
|
|
|
e = fmt.Errorf("%s\n(use `-h <branch>` to specify an explicit pull request head)", e)
|
|
|
|
utils.Check(e)
|
2013-12-06 22:50:19 +04:00
|
|
|
}
|
|
|
|
}
|
2014-03-05 20:37:17 +04:00
|
|
|
}
|
|
|
|
|
2019-03-03 01:43:56 +03:00
|
|
|
force := args.Flag.Bool("--force")
|
2019-06-15 03:53:13 +03:00
|
|
|
flagPullRequestPush := args.Flag.Bool("--push")
|
2019-03-03 01:43:56 +03:00
|
|
|
|
2014-03-05 20:39:23 +04:00
|
|
|
if head == "" {
|
|
|
|
if trackedBranch == nil {
|
2019-05-29 23:34:41 +03:00
|
|
|
utils.Check(currentBranchErr)
|
2019-06-15 03:53:13 +03:00
|
|
|
if !force && !flagPullRequestPush {
|
|
|
|
branchRemote, branchMerge, err := branchTrackingInformation(currentBranch)
|
|
|
|
if err != nil || (baseRemote != nil && branchRemote == baseRemote.Name && branchMerge.ShortName() == base) {
|
|
|
|
if localRepo.RemoteForBranch(currentBranch, host.User) == nil {
|
|
|
|
err = fmt.Errorf("Aborted: the current branch seems not yet pushed to a remote")
|
|
|
|
err = fmt.Errorf("%s\n(use `-p` to push the branch or `-f` to skip this check)", err)
|
|
|
|
utils.Check(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-03-05 20:39:23 +04:00
|
|
|
head = currentBranch.ShortName()
|
|
|
|
} else {
|
|
|
|
head = trackedBranch.ShortName()
|
|
|
|
}
|
2013-12-06 22:50:19 +04:00
|
|
|
}
|
|
|
|
|
2016-08-09 22:40:16 +03:00
|
|
|
if headRepo, err := client.Repository(headProject); err == nil {
|
|
|
|
headProject.Owner = headRepo.Owner.Login
|
|
|
|
headProject.Name = headRepo.Name
|
|
|
|
}
|
|
|
|
|
2013-12-06 22:50:19 +04:00
|
|
|
fullBase := fmt.Sprintf("%s:%s", baseProject.Owner, base)
|
|
|
|
fullHead := fmt.Sprintf("%s:%s", headProject.Owner, head)
|
|
|
|
|
2013-12-19 22:19:38 +04:00
|
|
|
if !force && trackedBranch != nil {
|
2019-05-08 15:59:56 +03:00
|
|
|
remoteCommits, err := git.RefList(trackedBranch.LongName(), "")
|
|
|
|
if err == nil && len(remoteCommits) > 0 {
|
2013-12-19 22:19:38 +04:00
|
|
|
err = fmt.Errorf("Aborted: %d commits are not yet pushed to %s", len(remoteCommits), trackedBranch.LongName())
|
|
|
|
err = fmt.Errorf("%s\n(use `-f` to force submit a pull request anyway)", err)
|
|
|
|
utils.Check(err)
|
|
|
|
}
|
2013-12-07 19:43:55 +04:00
|
|
|
}
|
|
|
|
|
2018-01-23 17:25:43 +03:00
|
|
|
messageBuilder := &github.MessageBuilder{
|
|
|
|
Filename: "PULLREQ_EDITMSG",
|
|
|
|
Title: "pull request",
|
|
|
|
}
|
2016-08-21 13:26:42 +03:00
|
|
|
|
2016-10-03 23:40:20 +03:00
|
|
|
baseTracking := base
|
|
|
|
headTracking := head
|
|
|
|
|
2018-12-28 05:01:30 +03:00
|
|
|
remote := baseRemote
|
2016-10-03 23:40:20 +03:00
|
|
|
if remote != nil {
|
|
|
|
baseTracking = fmt.Sprintf("%s/%s", remote.Name, base)
|
|
|
|
}
|
|
|
|
if remote == nil || !baseProject.SameAs(headProject) {
|
2018-12-28 05:01:30 +03:00
|
|
|
remote, _ = localRepo.RemoteForProject(headProject)
|
2016-10-03 23:40:20 +03:00
|
|
|
}
|
|
|
|
if remote != nil {
|
|
|
|
headTracking = fmt.Sprintf("%s/%s", remote.Name, head)
|
|
|
|
}
|
|
|
|
|
2016-10-20 23:53:27 +03:00
|
|
|
if flagPullRequestPush && remote == nil {
|
|
|
|
utils.Check(fmt.Errorf("Can't find remote for %s", head))
|
|
|
|
}
|
|
|
|
|
2018-01-23 20:08:23 +03:00
|
|
|
messageBuilder.AddCommentedSection(fmt.Sprintf(`Requesting a pull to %s from %s
|
|
|
|
|
|
|
|
Write a message for this pull request. The first block
|
|
|
|
of text is the title and the rest is the description.`, fullBase, fullHead))
|
|
|
|
|
2019-01-16 06:22:20 +03:00
|
|
|
flagPullRequestMessage := args.Flag.AllValues("--message")
|
|
|
|
flagPullRequestEdit := args.Flag.Bool("--edit")
|
|
|
|
flagPullRequestIssue := args.Flag.Value("--issue")
|
|
|
|
if !args.Flag.HasReceived("--issue") && args.ParamsSize() > 0 {
|
|
|
|
flagPullRequestIssue = parsePullRequestIssueNumber(args.GetParam(0))
|
|
|
|
}
|
|
|
|
|
2018-11-28 05:05:15 +03:00
|
|
|
if len(flagPullRequestMessage) > 0 {
|
2019-01-16 06:22:20 +03:00
|
|
|
messageBuilder.Message = strings.Join(flagPullRequestMessage, "\n\n")
|
2018-01-23 17:25:43 +03:00
|
|
|
messageBuilder.Edit = flagPullRequestEdit
|
2019-01-16 06:22:20 +03:00
|
|
|
} else if args.Flag.HasReceived("--file") {
|
|
|
|
messageBuilder.Message, err = msgFromFile(args.Flag.Value("--file"))
|
2016-08-21 13:26:42 +03:00
|
|
|
utils.Check(err)
|
2018-01-23 17:25:43 +03:00
|
|
|
messageBuilder.Edit = flagPullRequestEdit
|
2019-01-16 06:22:20 +03:00
|
|
|
} else if args.Flag.Bool("--no-edit") {
|
2018-05-03 02:02:52 +03:00
|
|
|
commits, _ := git.RefList(baseTracking, head)
|
|
|
|
if len(commits) == 0 {
|
2018-06-06 16:53:43 +03:00
|
|
|
utils.Check(fmt.Errorf("Aborted: no commits detected between %s and %s", baseTracking, head))
|
2018-05-03 02:02:52 +03:00
|
|
|
}
|
|
|
|
message, err := git.Show(commits[len(commits)-1])
|
|
|
|
utils.Check(err)
|
|
|
|
messageBuilder.Message = message
|
2016-08-21 13:26:42 +03:00
|
|
|
} else if flagPullRequestIssue == "" {
|
2018-01-23 17:25:43 +03:00
|
|
|
messageBuilder.Edit = true
|
|
|
|
|
2016-10-03 23:40:20 +03:00
|
|
|
headForMessage := headTracking
|
|
|
|
if flagPullRequestPush {
|
|
|
|
headForMessage = head
|
2014-10-20 04:49:05 +04:00
|
|
|
}
|
|
|
|
|
2017-07-31 18:06:56 +03:00
|
|
|
message := ""
|
|
|
|
|
|
|
|
commits, _ := git.RefList(baseTracking, headForMessage)
|
|
|
|
if len(commits) == 1 {
|
|
|
|
message, err = git.Show(commits[0])
|
|
|
|
utils.Check(err)
|
2018-05-28 21:00:49 +03:00
|
|
|
|
2018-05-29 13:23:39 +03:00
|
|
|
re := regexp.MustCompile(`\nSigned-off-by:\s.*$`)
|
2018-05-28 21:00:49 +03:00
|
|
|
message = re.ReplaceAllString(message, "")
|
2017-07-31 18:06:56 +03:00
|
|
|
} else if len(commits) > 1 {
|
2019-06-03 02:08:36 +03:00
|
|
|
commitLogs, err := git.Log(baseTracking, headForMessage)
|
2017-07-31 18:06:56 +03:00
|
|
|
utils.Check(err)
|
|
|
|
|
2019-06-03 02:08:36 +03:00
|
|
|
if commitLogs != "" {
|
|
|
|
messageBuilder.AddCommentedSection("\nChanges:\n\n" + strings.TrimSpace(commitLogs))
|
|
|
|
}
|
2018-01-23 20:08:23 +03:00
|
|
|
}
|
|
|
|
|
2017-07-31 18:06:56 +03:00
|
|
|
workdir, _ := git.WorkdirName()
|
|
|
|
if workdir != "" {
|
|
|
|
template, _ := github.ReadTemplate(github.PullRequestTemplate, workdir)
|
|
|
|
if template != "" {
|
2018-05-29 16:06:05 +03:00
|
|
|
message = message + "\n\n\n" + template
|
2017-07-31 18:06:56 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-23 17:25:43 +03:00
|
|
|
messageBuilder.Message = message
|
2015-06-08 18:17:28 +03:00
|
|
|
}
|
|
|
|
|
2018-01-23 17:25:43 +03:00
|
|
|
title, body, err := messageBuilder.Extract()
|
|
|
|
utils.Check(err)
|
|
|
|
|
2013-07-05 22:10:24 +04:00
|
|
|
if title == "" && flagPullRequestIssue == "" {
|
|
|
|
utils.Check(fmt.Errorf("Aborting due to empty pull request title"))
|
|
|
|
}
|
2013-05-28 21:13:55 +04:00
|
|
|
|
2016-10-03 23:40:20 +03:00
|
|
|
if flagPullRequestPush {
|
|
|
|
if args.Noop {
|
|
|
|
args.Before(fmt.Sprintf("Would push to %s/%s", remote.Name, head), "")
|
|
|
|
} else {
|
2016-12-08 00:47:46 +03:00
|
|
|
err = git.Spawn("push", "--set-upstream", remote.Name, fmt.Sprintf("HEAD:%s", head))
|
2016-10-20 23:53:27 +03:00
|
|
|
utils.Check(err)
|
2016-10-03 23:40:20 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-14 03:44:25 +03:00
|
|
|
milestoneNumber, err := milestoneValueToNumber(args.Flag.Value("--milestone"), client, baseProject)
|
|
|
|
utils.Check(err)
|
2018-02-12 02:26:50 +03:00
|
|
|
|
2013-07-06 00:45:22 +04:00
|
|
|
var pullRequestURL string
|
2013-07-05 22:10:24 +04:00
|
|
|
if args.Noop {
|
2013-12-06 22:50:19 +04:00
|
|
|
args.Before(fmt.Sprintf("Would request a pull request to %s from %s", fullBase, fullHead), "")
|
2013-07-06 00:45:22 +04:00
|
|
|
pullRequestURL = "PULL_REQUEST_URL"
|
2013-07-05 22:10:24 +04:00
|
|
|
} else {
|
2016-08-08 01:43:48 +03:00
|
|
|
params := map[string]interface{}{
|
2019-06-14 16:00:41 +03:00
|
|
|
"base": base,
|
|
|
|
"head": fullHead,
|
|
|
|
"maintainer_can_modify": !args.Flag.Bool("--no-maintainer-edits"),
|
2019-03-29 14:32:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if args.Flag.Bool("--draft") {
|
|
|
|
params["draft"] = true
|
2016-08-08 01:43:48 +03:00
|
|
|
}
|
2014-02-24 02:24:37 +04:00
|
|
|
|
2013-07-05 22:10:24 +04:00
|
|
|
if title != "" {
|
2016-08-08 01:43:48 +03:00
|
|
|
params["title"] = title
|
|
|
|
if body != "" {
|
|
|
|
params["body"] = body
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
issueNum, _ := strconv.Atoi(flagPullRequestIssue)
|
|
|
|
params["issue"] = issueNum
|
2013-07-05 22:10:24 +04:00
|
|
|
}
|
2016-10-22 13:10:00 +03:00
|
|
|
|
|
|
|
startedAt := time.Now()
|
|
|
|
numRetries := 0
|
|
|
|
retryDelay := 2
|
|
|
|
retryAllowance := 0
|
|
|
|
if flagPullRequestPush {
|
2016-10-22 13:28:32 +03:00
|
|
|
if allowanceFromEnv := os.Getenv("HUB_RETRY_TIMEOUT"); allowanceFromEnv != "" {
|
|
|
|
retryAllowance, err = strconv.Atoi(allowanceFromEnv)
|
|
|
|
utils.Check(err)
|
|
|
|
} else {
|
|
|
|
retryAllowance = 9
|
|
|
|
}
|
2016-10-22 13:10:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
var pr *github.PullRequest
|
|
|
|
for {
|
|
|
|
pr, err = client.CreatePullRequest(baseProject, params)
|
|
|
|
if err != nil && strings.Contains(err.Error(), `Invalid value for "head"`) {
|
|
|
|
if retryAllowance > 0 {
|
|
|
|
retryAllowance -= retryDelay
|
|
|
|
time.Sleep(time.Duration(retryDelay) * time.Second)
|
|
|
|
retryDelay += 1
|
|
|
|
numRetries += 1
|
|
|
|
} else {
|
|
|
|
if numRetries > 0 {
|
2019-07-23 10:28:32 +03:00
|
|
|
duration := time.Since(startedAt)
|
2016-10-22 13:10:00 +03:00
|
|
|
err = fmt.Errorf("%s\nGiven up after retrying for %.1f seconds.", err, duration.Seconds())
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2013-04-28 21:45:00 +04:00
|
|
|
|
2018-01-23 17:25:43 +03:00
|
|
|
if err == nil {
|
|
|
|
defer messageBuilder.Cleanup()
|
2014-03-02 23:43:43 +04:00
|
|
|
}
|
|
|
|
|
2014-02-24 02:24:37 +04:00
|
|
|
utils.Check(err)
|
2015-04-02 16:53:10 +03:00
|
|
|
|
2016-08-14 20:54:31 +03:00
|
|
|
pullRequestURL = pr.HtmlUrl
|
2015-04-02 16:53:10 +03:00
|
|
|
|
2016-09-09 00:27:32 +03:00
|
|
|
params = map[string]interface{}{}
|
2019-01-16 06:22:20 +03:00
|
|
|
flagPullRequestLabels := commaSeparated(args.Flag.AllValues("--labels"))
|
2016-09-09 00:27:32 +03:00
|
|
|
if len(flagPullRequestLabels) > 0 {
|
|
|
|
params["labels"] = flagPullRequestLabels
|
|
|
|
}
|
2019-01-16 06:22:20 +03:00
|
|
|
flagPullRequestAssignees := commaSeparated(args.Flag.AllValues("--assign"))
|
2016-09-09 00:27:32 +03:00
|
|
|
if len(flagPullRequestAssignees) > 0 {
|
|
|
|
params["assignees"] = flagPullRequestAssignees
|
|
|
|
}
|
2018-02-12 02:26:50 +03:00
|
|
|
if milestoneNumber > 0 {
|
|
|
|
params["milestone"] = milestoneNumber
|
2016-09-09 00:27:32 +03:00
|
|
|
}
|
2015-08-06 00:29:51 +03:00
|
|
|
|
2016-09-09 00:27:32 +03:00
|
|
|
if len(params) > 0 {
|
2015-08-06 00:29:51 +03:00
|
|
|
err = client.UpdateIssue(baseProject, pr.Number, params)
|
2015-04-02 16:53:10 +03:00
|
|
|
utils.Check(err)
|
|
|
|
}
|
2017-04-17 18:13:23 +03:00
|
|
|
|
2019-01-16 06:22:20 +03:00
|
|
|
flagPullRequestReviewers := commaSeparated(args.Flag.AllValues("--reviewer"))
|
2017-04-17 18:13:23 +03:00
|
|
|
if len(flagPullRequestReviewers) > 0 {
|
2018-01-19 16:47:06 +03:00
|
|
|
userReviewers := []string{}
|
|
|
|
teamReviewers := []string{}
|
|
|
|
for _, reviewer := range flagPullRequestReviewers {
|
|
|
|
if strings.Contains(reviewer, "/") {
|
2018-08-23 14:26:24 +03:00
|
|
|
teamName := strings.SplitN(reviewer, "/", 2)[1]
|
|
|
|
if !pr.HasRequestedTeam(teamName) {
|
|
|
|
teamReviewers = append(teamReviewers, teamName)
|
|
|
|
}
|
|
|
|
} else if !pr.HasRequestedReviewer(reviewer) {
|
2018-01-19 16:47:06 +03:00
|
|
|
userReviewers = append(userReviewers, reviewer)
|
|
|
|
}
|
|
|
|
}
|
2018-08-23 14:26:24 +03:00
|
|
|
if len(userReviewers) > 0 || len(teamReviewers) > 0 {
|
|
|
|
err = client.RequestReview(baseProject, pr.Number, map[string]interface{}{
|
|
|
|
"reviewers": userReviewers,
|
|
|
|
"team_reviewers": teamReviewers,
|
|
|
|
})
|
|
|
|
utils.Check(err)
|
|
|
|
}
|
2017-04-17 18:13:23 +03:00
|
|
|
}
|
2013-07-05 22:10:24 +04:00
|
|
|
}
|
2013-07-06 00:45:22 +04:00
|
|
|
|
2016-09-11 05:38:13 +03:00
|
|
|
args.NoForward()
|
2019-01-16 06:22:20 +03:00
|
|
|
printBrowseOrCopy(args, pullRequestURL, args.Flag.Bool("--browse"), args.Flag.Bool("--copy"))
|
2013-07-05 22:10:24 +04:00
|
|
|
}
|
2013-06-30 20:11:25 +04:00
|
|
|
|
2013-12-08 12:33:48 +04:00
|
|
|
func parsePullRequestProject(context *github.Project, s string) (p *github.Project, ref string) {
|
|
|
|
p = context
|
|
|
|
ref = s
|
|
|
|
|
|
|
|
if strings.Contains(s, ":") {
|
|
|
|
split := strings.SplitN(s, ":", 2)
|
|
|
|
ref = split[1]
|
|
|
|
var name string
|
|
|
|
if !strings.Contains(split[0], "/") {
|
|
|
|
name = context.Name
|
|
|
|
}
|
|
|
|
p = github.NewProject(split[0], name, context.Host)
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func parsePullRequestIssueNumber(url string) string {
|
|
|
|
u, e := github.ParseURL(url)
|
|
|
|
if e != nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
r := regexp.MustCompile(`^issues\/(\d+)`)
|
|
|
|
p := u.ProjectPath()
|
|
|
|
if r.MatchString(p) {
|
|
|
|
return r.FindStringSubmatch(p)[1]
|
|
|
|
}
|
|
|
|
|
|
|
|
return ""
|
|
|
|
}
|
2018-02-12 02:26:50 +03:00
|
|
|
|
2019-01-16 06:22:20 +03:00
|
|
|
func commaSeparated(l []string) []string {
|
|
|
|
res := []string{}
|
|
|
|
for _, i := range l {
|
|
|
|
res = append(res, strings.Split(i, ",")...)
|
|
|
|
}
|
|
|
|
return res
|
|
|
|
}
|