pr.go 6.0 KB
Newer Older
J
Johan Walles 已提交
1 2 3 4
package commands

import (
	"fmt"
5
	"os"
J
Johan Walles 已提交
6
	"strconv"
7
	"strings"
J
Johan Walles 已提交
8 9

	"github.com/github/hub/github"
10
	"github.com/github/hub/ui"
J
Johan Walles 已提交
11 12 13
	"github.com/github/hub/utils"
)

14
var (
J
Johan Walles 已提交
15
	cmdPr = &Command{
16 17
		Run: printHelp,
		Usage: `
18
pr list [-s <STATE>] [-h <HEAD>] [-b <BASE>] [-o <SORT_KEY> [-^]] [-f <FORMAT>] [-L <LIMIT>]
19 20 21
pr checkout <PR-NUMBER> [<BRANCH>]
`,
		Long: `Manage GitHub pull requests for the current project.
J
Johan Walles 已提交
22

23 24 25 26 27 28 29 30 31 32
## Commands:

	* _list_:
		List pull requests in the current project.

	* _checkout_:
		Check out the head of a pull request in a new branch.

## Options:

33
	-s, --state=<STATE>
34 35
		Filter pull requests by <STATE>. Supported values are: "open" (default),
		"closed", "merged", or "all".
36

37 38 39
	-h, --head=<BRANCH>
		Show pull requests started from the specified head <BRANCH>. The
		"OWNER:BRANCH" format must be used for pull requests from forks.
40

41
	-b, --base=<BRANCH>
42
		Show pull requests based off the specified <BRANCH>.
43

44
	-f, --format=<FORMAT>
45
		Pretty print the list of pull requests using format <FORMAT> (default:
M
Mislav Marohnić 已提交
46 47
		"%sC%>(8)%i%Creset  %t%  l%n"). See the "PRETTY FORMATS" section of
		git-log(1) for some additional details on how placeholders are used in
48 49 50 51 52 53 54 55
		format. The available placeholders are:

		%I: pull request number

		%i: pull request number prefixed with "#"

		%U: the URL of this pull request

56
		%S: state ("open" or "closed")
57 58 59 60 61 62 63 64 65 66 67

		%sC: set color to red or green, depending on pull request state.

		%t: title

		%l: colored labels

		%L: raw, comma-separated labels

		%b: body

68 69
		%B: base branch

70 71
		%sB: base commit SHA

72 73
		%H: head branch

74 75 76 77
		%sH: head commit SHA

		%sm: merge commit SHA

78 79 80 81
		%au: login name of author

		%as: comma-separated list of assignees

82 83
		%rs: comma-separated list of requested reviewers

84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
		%Mn: milestone number

		%Mt: milestone title

		%NC: number of comments

		%Nc: number of comments wrapped in parentheses, or blank string if zero.

		%cD: created date-only (no time of day)

		%cr: created date, relative

		%ct: created date, UNIX timestamp

		%cI: created date, ISO 8601 format

		%uD: updated date-only (no time of day)

		%ur: updated date, relative

		%ut: updated date, UNIX timestamp

		%uI: updated date, ISO 8601 format

108 109 110 111 112 113 114 115
		%mD: merged date-only (no time of day)

		%mr: merged date, relative

		%mt: merged date, UNIX timestamp

		%mI: merged date, ISO 8601 format

116
	-o, --sort=<SORT_KEY>
117 118 119 120 121
		Sort displayed issues by "created" (default), "updated", "popularity", or "long-running".

	-^ --sort-ascending
		Sort by ascending dates instead of descending.

122
	-L, --limit=<LIMIT>
123
		Display only the first <LIMIT> issues.
J
Johan Walles 已提交
124 125 126

## See also:

127 128
hub-issue(1), hub-pull-request(1), hub(1)
`,
129 130 131 132
	}

	cmdCheckoutPr = &Command{
		Key: "checkout",
J
Johan Walles 已提交
133
		Run: checkoutPr,
134
	}
135 136 137 138 139 140 141 142 143 144 145 146 147

	cmdListPulls = &Command{
		Key: "list",
		Run: listPulls,
	}

	flagPullRequestState,
	flagPullRequestFormat,
	flagPullRequestSort string

	flagPullRequestSortAscending bool

	flagPullRequestLimit int
148
)
J
Johan Walles 已提交
149 150

func init() {
151 152 153 154 155 156 157 158 159
	cmdListPulls.Flag.StringVarP(&flagPullRequestState, "state", "s", "", "STATE")
	cmdListPulls.Flag.StringVarP(&flagPullRequestBase, "base", "b", "", "BASE")
	cmdListPulls.Flag.StringVarP(&flagPullRequestHead, "head", "h", "", "HEAD")
	cmdListPulls.Flag.StringVarP(&flagPullRequestFormat, "format", "f", "%sC%>(8)%i%Creset  %t%  l%n", "FORMAT")
	cmdListPulls.Flag.StringVarP(&flagPullRequestSort, "sort", "o", "created", "SORT_KEY")
	cmdListPulls.Flag.BoolVarP(&flagPullRequestSortAscending, "sort-ascending", "^", false, "SORT_KEY")
	cmdListPulls.Flag.IntVarP(&flagPullRequestLimit, "limit", "L", -1, "LIMIT")

	cmdPr.Use(cmdListPulls)
J
Johan Walles 已提交
160
	cmdPr.Use(cmdCheckoutPr)
J
Johan Walles 已提交
161
	CmdRunner.Use(cmdPr)
J
Johan Walles 已提交
162 163
}

164 165 166 167 168
func printHelp(command *Command, args *Args) {
	fmt.Print(command.HelpText())
	os.Exit(0)
}

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
func listPulls(cmd *Command, args *Args) {
	localRepo, err := github.LocalRepo()
	utils.Check(err)

	project, err := localRepo.MainProject()
	utils.Check(err)

	gh := github.NewClient(project.Host)

	args.NoForward()
	if args.Noop {
		ui.Printf("Would request list of pull requests for %s\n", project)
		return
	}

184 185 186 187
	if flagPullRequestHead != "" && !strings.Contains(flagPullRequestHead, ":") {
		flagPullRequestHead = fmt.Sprintf("%s:%s", project.Owner, flagPullRequestHead)
	}

188 189 190 191 192 193 194 195 196 197 198 199 200 201
	flagFilters := map[string]string{
		"state": flagPullRequestState,
		"head":  flagPullRequestHead,
		"base":  flagPullRequestBase,
		"sort":  flagPullRequestSort,
	}
	filters := map[string]interface{}{}
	for flag, filter := range flagFilters {
		if cmd.FlagPassed(flag) {
			filters[flag] = filter
		}
	}
	if flagPullRequestSortAscending {
		filters["direction"] = "asc"
202 203
	} else {
		filters["direction"] = "desc"
204 205
	}

206 207 208 209 210 211 212 213 214
	onlyMerged := false
	if filters["state"] == "merged" {
		filters["state"] = "closed"
		onlyMerged = true
	}

	pulls, err := gh.FetchPullRequests(project, filters, flagPullRequestLimit, func(pr *github.PullRequest) bool {
		return !(onlyMerged && pr.MergedAt.IsZero())
	})
215 216 217 218 219 220 221 222
	utils.Check(err)

	colorize := ui.IsTerminal(os.Stdout)
	for _, pr := range pulls {
		ui.Printf(formatPullRequest(pr, flagPullRequestFormat, colorize))
	}
}

J
Johan Walles 已提交
223
func checkoutPr(command *Command, args *Args) {
224 225 226 227 228 229 230
	words := args.Words()
	var newBranchName string

	if len(words) == 0 {
		utils.Check(fmt.Errorf("Error: No pull request number given"))
	} else if len(words) > 1 {
		newBranchName = words[1]
J
Johan Walles 已提交
231 232
	}

233
	prNumberString := words[0]
J
Johan Walles 已提交
234 235 236 237 238 239 240 241 242 243 244 245 246 247
	_, err := strconv.Atoi(prNumberString)
	utils.Check(err)

	// Figure out the PR URL
	localRepo, err := github.LocalRepo()
	utils.Check(err)
	baseProject, err := localRepo.MainProject()
	utils.Check(err)
	host, err := github.CurrentConfig().PromptForHost(baseProject.Host)
	utils.Check(err)
	client := github.NewClientWithHost(host)
	pr, err := client.PullRequest(baseProject, prNumberString)
	utils.Check(err)

248
	newArgs, err := transformCheckoutArgs(args, pr, newBranchName)
J
Johan Walles 已提交
249
	utils.Check(err)
J
Johan Walles 已提交
250

251
	args.Replace(args.Executable, "checkout", newArgs...)
J
Johan Walles 已提交
252
}
253 254 255

func formatPullRequest(pr github.PullRequest, format string, colorize bool) string {
	placeholders := formatIssuePlaceholders(github.Issue(pr), colorize)
256 257 258
	for key, value := range formatPullRequestPlaceholders(pr) {
		placeholders[key] = value
	}
259 260
	return ui.Expand(format, placeholders, colorize)
}