github.com/scorpionis/hub@v2.2.1+incompatible/commands/compare.go (about)

     1  package commands
     2  
     3  import (
     4  	"fmt"
     5  	"regexp"
     6  	"strings"
     7  	"net/url"
     8  
     9  	"github.com/github/hub/github"
    10  	"github.com/github/hub/utils"
    11  )
    12  
    13  var cmdCompare = &Command{
    14  	Run:   compare,
    15  	Usage: "compare [-u] [USER] [<START>...]<END>",
    16  	Short: "Open a compare page on GitHub",
    17  	Long: `Open a GitHub compare view page in the system's default web browser.
    18  <START> to <END> are branch names, tag names, or commit SHA1s specifying
    19  the range of history to compare. If a range with two dots ("a..b") is given,
    20  it will be transformed into one with three dots. If <START> is omitted,
    21  GitHub will compare against the base branch (the default is "master").
    22  If <END> is omitted, GitHub compare view is opened for the current branch.
    23  With "-u", outputs the URL rather than opening the browser.
    24  `,
    25  }
    26  
    27  var (
    28  	flagCompareURLOnly bool
    29  )
    30  
    31  func init() {
    32  	cmdCompare.Flag.BoolVarP(&flagCompareURLOnly, "url-only", "u", false, "URL only")
    33  
    34  	CmdRunner.Use(cmdCompare)
    35  }
    36  
    37  /*
    38    $ gh compare refactor
    39    > open https://github.com/CURRENT_REPO/compare/refactor
    40  
    41    $ gh compare 1.0..1.1
    42    > open https://github.com/CURRENT_REPO/compare/1.0...1.1
    43  
    44    $ gh compare -u other-user patch
    45    > open https://github.com/other-user/REPO/compare/patch
    46  */
    47  func compare(command *Command, args *Args) {
    48  	localRepo, err := github.LocalRepo()
    49  	utils.Check(err)
    50  
    51  	var (
    52  		branch  *github.Branch
    53  		project *github.Project
    54  		r       string
    55  	)
    56  
    57  	branch, project, err = localRepo.RemoteBranchAndProject("", false)
    58  	utils.Check(err)
    59  
    60  	if args.IsParamsEmpty() {
    61  		if branch != nil && !branch.IsMaster() {
    62  			r = branch.ShortName()
    63  		} else {
    64  			err = fmt.Errorf("Usage: hub compare [USER] [<START>...]<END>")
    65  			utils.Check(err)
    66  		}
    67  	} else {
    68  		r = parseCompareRange(args.RemoveParam(args.ParamsSize() - 1))
    69  		if args.IsParamsEmpty() {
    70  			project, err = localRepo.CurrentProject()
    71  			utils.Check(err)
    72  		} else {
    73  			project = github.NewProject(args.RemoveParam(args.ParamsSize()-1), "", "")
    74  		}
    75  	}
    76  
    77  	subpage := utils.ConcatPaths("compare", rangeQueryEscape(r))
    78  	url := project.WebURL("", "", subpage)
    79  	launcher, err := utils.BrowserLauncher()
    80  	utils.Check(err)
    81  
    82  	if flagCompareURLOnly {
    83  		args.Replace("echo", url)
    84  	} else {
    85  		args.Replace(launcher[0], "", launcher[1:]...)
    86  		args.AppendParams(url)
    87  	}
    88  }
    89  
    90  func parseCompareRange(r string) string {
    91  	shaOrTag := fmt.Sprintf("((?:%s:)?\\w[\\w.-]+\\w)", OwnerRe)
    92  	shaOrTagRange := fmt.Sprintf("^%s\\.\\.%s$", shaOrTag, shaOrTag)
    93  	shaOrTagRangeRegexp := regexp.MustCompile(shaOrTagRange)
    94  	return shaOrTagRangeRegexp.ReplaceAllString(r, "$1...$2")
    95  }
    96  
    97  // characters we want to allow unencoded in compare views
    98  var compareUnescaper = strings.NewReplacer(
    99  	"%2F", "/",
   100  	"%3A", ":",
   101  	"%5E", "^",
   102  	"%7E", "~",
   103  	"%2A", "*",
   104  	"%21", "!",
   105  )
   106  
   107  func rangeQueryEscape(r string) string {
   108  	if strings.Contains(r, "..") {
   109  		return r
   110  	} else {
   111  		return compareUnescaper.Replace(url.QueryEscape(r))
   112  	}
   113  }