compare.go 3.3 KB
Newer Older
J
Jingwen Owen Ou 已提交
1 2 3 4
package commands

import (
	"fmt"
D
Darin Minamoto 已提交
5
	"net/url"
6
	"regexp"
7
	"strings"
8

J
Jingwen Owen Ou 已提交
9 10
	"github.com/github/hub/github"
	"github.com/github/hub/utils"
J
Jingwen Owen Ou 已提交
11 12 13 14
)

var cmdCompare = &Command{
	Run:   compare,
15
	Usage: "compare [-u] [USER] [<START>...]<END>",
J
Jingwen Owen Ou 已提交
16 17
	Short: "Open a compare page on GitHub",
	Long: `Open a GitHub compare view page in the system's default web browser.
18 19 20
<START> to <END> are branch names, tag names, or commit SHA1s specifying
the range of history to compare. If a range with two dots ("a..b") is given,
it will be transformed into one with three dots. If <START> is omitted,
J
Jingwen Owen Ou 已提交
21
GitHub will compare against the base branch (the default is "master").
22 23
If <END> is omitted, GitHub compare view is opened for the current branch.
With "-u", outputs the URL rather than opening the browser.
J
Jingwen Owen Ou 已提交
24 25 26
`,
}

J
Jingwen Owen Ou 已提交
27 28
var (
	flagCompareURLOnly bool
D
Darin Minamoto 已提交
29
	flagCompareBase    string
J
Jingwen Owen Ou 已提交
30
)
J
Jingwen Owen Ou 已提交
31 32

func init() {
33
	cmdCompare.Flag.BoolVarP(&flagCompareURLOnly, "url-only", "u", false, "URL only")
D
Darin Minamoto 已提交
34
	cmdCompare.Flag.StringVarP(&flagCompareBase, "base", "b", "", "BASE")
35 36

	CmdRunner.Use(cmdCompare)
J
Jingwen Owen Ou 已提交
37 38
}

39 40 41 42 43 44 45 46 47 48
/*
  $ gh compare refactor
  > open https://github.com/CURRENT_REPO/compare/refactor

  $ gh compare 1.0..1.1
  > open https://github.com/CURRENT_REPO/compare/1.0...1.1

  $ gh compare -u other-user patch
  > open https://github.com/other-user/REPO/compare/patch
*/
J
Jingwen Owen Ou 已提交
49
func compare(command *Command, args *Args) {
50 51 52
	localRepo, err := github.LocalRepo()
	utils.Check(err)

J
Jingwen Owen Ou 已提交
53
	var (
J
Jingwen Owen Ou 已提交
54
		branch  *github.Branch
J
Jingwen Owen Ou 已提交
55 56 57 58
		project *github.Project
		r       string
	)

59
	branch, project, err = localRepo.RemoteBranchAndProject("", false)
J
Jingwen Owen Ou 已提交
60
	utils.Check(err)
J
Jingwen Owen Ou 已提交
61

62 63 64 65
	usageHelp := func() {
		utils.Check(fmt.Errorf("Usage: hub compare [-u] [-b <BASE>] [<USER>] [[<START>...]<END>]"))
	}

J
Jingwen Owen Ou 已提交
66
	if args.IsParamsEmpty() {
D
Darin Minamoto 已提交
67 68 69 70
		if branch == nil ||
			(branch.IsMaster() && flagCompareBase == "") ||
			(flagCompareBase == branch.ShortName()) {

71
			usageHelp()
D
Darin Minamoto 已提交
72 73 74 75 76
		} else {
			r = branch.ShortName()
			if flagCompareBase != "" {
				r = parseCompareRange(flagCompareBase + "..." + r)
			}
J
Jingwen Owen Ou 已提交
77
		}
J
Jingwen Owen Ou 已提交
78
	} else {
D
Darin Minamoto 已提交
79
		if flagCompareBase != "" {
80
			usageHelp()
J
Jingwen Owen Ou 已提交
81
		} else {
D
Darin Minamoto 已提交
82 83 84 85 86 87 88
			r = parseCompareRange(args.RemoveParam(args.ParamsSize() - 1))
			if args.IsParamsEmpty() {
				project, err = localRepo.CurrentProject()
				utils.Check(err)
			} else {
				project = github.NewProject(args.RemoveParam(args.ParamsSize()-1), "", "")
			}
J
Jingwen Owen Ou 已提交
89
		}
J
Jingwen Owen Ou 已提交
90 91
	}

92 93 94 95 96
	if project == nil {
		project, err = localRepo.CurrentProject()
		utils.Check(err)
	}

97
	subpage := utils.ConcatPaths("compare", rangeQueryEscape(r))
J
Jingwen Owen Ou 已提交
98
	url := project.WebURL("", "", subpage)
J
Jingwen Owen Ou 已提交
99
	launcher, err := utils.BrowserLauncher()
J
Jingwen Owen Ou 已提交
100
	utils.Check(err)
101

J
Jingwen Owen Ou 已提交
102 103 104 105 106 107
	if flagCompareURLOnly {
		args.Replace("echo", url)
	} else {
		args.Replace(launcher[0], "", launcher[1:]...)
		args.AppendParams(url)
	}
J
Jingwen Owen Ou 已提交
108 109
}

J
Jingwen Owen Ou 已提交
110 111
func parseCompareRange(r string) string {
	shaOrTag := fmt.Sprintf("((?:%s:)?\\w[\\w.-]+\\w)", OwnerRe)
J
Jingwen Owen Ou 已提交
112 113 114 115
	shaOrTagRange := fmt.Sprintf("^%s\\.\\.%s$", shaOrTag, shaOrTag)
	shaOrTagRangeRegexp := regexp.MustCompile(shaOrTagRange)
	return shaOrTagRangeRegexp.ReplaceAllString(r, "$1...$2")
}
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133

// characters we want to allow unencoded in compare views
var compareUnescaper = strings.NewReplacer(
	"%2F", "/",
	"%3A", ":",
	"%5E", "^",
	"%7E", "~",
	"%2A", "*",
	"%21", "!",
)

func rangeQueryEscape(r string) string {
	if strings.Contains(r, "..") {
		return r
	} else {
		return compareUnescaper.Replace(url.QueryEscape(r))
	}
}