/* $ 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 */ func compare(command *Command, args *Args) { localRepo, err := github.LocalRepo() utils.Check(err) var ( branch *github.Branch project *github.Project r string ) branch, project, err = localRepo.RemoteBranchAndProject("", false) utils.Check(err) usageHelp := func() { utils.Check(fmt.Errorf("Usage: hub compare [-u] [-b <BASE>] [<USER>] [[<START>...]<END>]")) } if args.IsParamsEmpty() { if branch == nil || (branch.IsMaster() && flagCompareBase == "") || (flagCompareBase == branch.ShortName()) { usageHelp() } else { r = branch.ShortName() if flagCompareBase != "" { r = parseCompareRange(flagCompareBase + "..." + r) } } } else { if flagCompareBase != "" { usageHelp() } else { 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), "", "") } } } if project == nil { project, err = localRepo.CurrentProject() utils.Check(err) } subpage := utils.ConcatPaths("compare", rangeQueryEscape(r)) url := project.WebURL("", "", subpage) launcher, err := utils.BrowserLauncher() utils.Check(err) if flagCompareURLOnly { args.Replace("echo", url) } else { args.Replace(launcher[0], "", launcher[1:]...) args.AppendParams(url) } }
/* $ 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 */ func compare(command *Command, args *Args) { localRepo, err := github.LocalRepo() utils.Check(err) var ( branch *github.Branch project *github.Project r string ) branch, project, err = localRepo.RemoteBranchAndProject("", false) utils.Check(err) if args.IsParamsEmpty() { if branch != nil && !branch.IsMaster() { r = branch.ShortName() } else { err = fmt.Errorf("Usage: hub compare [USER] [<START>...]<END>") utils.Check(err) } } else { 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), "", "") } } subpage := utils.ConcatPaths("compare", rangeQueryEscape(r)) url := project.WebURL("", "", subpage) launcher, err := utils.BrowserLauncher() utils.Check(err) if flagCompareURLOnly { args.Replace("echo", url) } else { args.Replace(launcher[0], "", launcher[1:]...) args.AppendParams(url) } }
func printBrowseOrCopy(args *Args, msg string, openBrowser bool, performCopy bool) { if performCopy { if err := clipboard.WriteAll(msg); err != nil { ui.Errorf("Error copying %s to clipboard:\n%s\n", msg, err.Error()) } } if openBrowser { launcher, err := utils.BrowserLauncher() utils.Check(err) args.Replace(launcher[0], "", launcher[1:]...) args.AppendParams(msg) } if !openBrowser && !performCopy { args.AfterFn(func() error { ui.Println(msg) return nil }) } }
/* # while on a topic branch called "feature": $ gh pull-request [ opens text editor to edit title & body for the request ] [ opened pull request on GitHub for "YOUR_USER:feature" ] # explicit pull base & head: $ gh pull-request -b jingweno:master -h jingweno:feature $ gh pull-request -m "title\n\nbody" [ create pull request with title & body ] $ gh pull-request -i 123 [ attached pull request to issue #123 ] $ gh pull-request https://github.com/jingweno/gh/pull/123 [ attached pull request to issue #123 ] $ gh pull-request -F FILE [ create pull request with title & body from FILE ] */ func pullRequest(cmd *Command, args *Args) { localRepo, err := github.LocalRepo() utils.Check(err) currentBranch, err := localRepo.CurrentBranch() utils.Check(err) baseProject, err := localRepo.MainProject() utils.Check(err) host, err := github.CurrentConfig().PromptForHost(baseProject.Host) if err != nil { utils.Check(github.FormatError("creating pull request", err)) } trackedBranch, headProject, err := localRepo.RemoteBranchAndProject(host.User, false) utils.Check(err) var ( base, head string force bool ) force = flagPullRequestForce if flagPullRequestBase != "" { baseProject, base = parsePullRequestProject(baseProject, flagPullRequestBase) } if flagPullRequestHead != "" { headProject, head = parsePullRequestProject(headProject, flagPullRequestHead) } if args.ParamsSize() == 1 { arg := args.RemoveParam(0) flagPullRequestIssue = parsePullRequestIssueNumber(arg) } if base == "" { masterBranch := localRepo.MasterBranch() base = masterBranch.ShortName() } if head == "" && trackedBranch != nil { if !trackedBranch.IsRemote() { // the current branch tracking another branch // pretend there's no upstream at all trackedBranch = nil } else { if baseProject.SameAs(headProject) && base == trackedBranch.ShortName() { 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) } } } if head == "" { if trackedBranch == nil { head = currentBranch.ShortName() } else { head = trackedBranch.ShortName() } } title, body, err := getTitleAndBodyFromFlags(flagPullRequestMessage, flagPullRequestFile) utils.Check(err) fullBase := fmt.Sprintf("%s:%s", baseProject.Owner, base) fullHead := fmt.Sprintf("%s:%s", headProject.Owner, head) if !force && trackedBranch != nil { remoteCommits, _ := git.RefList(trackedBranch.LongName(), "") if len(remoteCommits) > 0 { 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) } } var editor *github.Editor if title == "" && flagPullRequestIssue == "" { baseTracking := base headTracking := head remote := gitRemoteForProject(baseProject) if remote != nil { baseTracking = fmt.Sprintf("%s/%s", remote.Name, base) } if remote == nil || !baseProject.SameAs(headProject) { remote = gitRemoteForProject(headProject) } if remote != nil { headTracking = fmt.Sprintf("%s/%s", remote.Name, head) } message, err := pullRequestChangesMessage(baseTracking, headTracking, fullBase, fullHead) utils.Check(err) editor, err = github.NewEditor("PULLREQ", "pull request", message) utils.Check(err) title, body, err = editor.EditTitleAndBody() utils.Check(err) } if title == "" && flagPullRequestIssue == "" { utils.Check(fmt.Errorf("Aborting due to empty pull request title")) } var pullRequestURL string if args.Noop { args.Before(fmt.Sprintf("Would request a pull request to %s from %s", fullBase, fullHead), "") pullRequestURL = "PULL_REQUEST_URL" } else { var ( pr *octokit.PullRequest err error ) client := github.NewClientWithHost(host) if title != "" { pr, err = client.CreatePullRequest(baseProject, base, fullHead, title, body) } else if flagPullRequestIssue != "" { pr, err = client.CreatePullRequestForIssue(baseProject, base, fullHead, flagPullRequestIssue) } if err == nil && editor != nil { defer editor.DeleteFile() } utils.Check(err) pullRequestURL = pr.HTMLURL if flagPullRequestAssignee != "" || flagPullRequestMilestone > 0 || flagPullRequestLabels != "" { params := octokit.IssueParams{ Assignee: flagPullRequestAssignee, Milestone: flagPullRequestMilestone, Labels: strings.Split(flagPullRequestLabels, ","), } err = client.UpdateIssue(baseProject, pr.Number, params) utils.Check(err) } } if flagPullRequestBrowse { launcher, err := utils.BrowserLauncher() utils.Check(err) args.Replace(launcher[0], "", launcher[1:]...) args.AppendParams(pullRequestURL) } else { args.Replace("echo", "", pullRequestURL) } if flagPullRequestIssue != "" { args.After("echo", "Warning: Issue to pull request conversion is deprecated and might not work in the future.") } }
func browse(command *Command, args *Args) { var ( dest string subpage string path string project *github.Project branch *github.Branch err error ) if !args.IsParamsEmpty() { dest = args.RemoveParam(0) } if !args.IsParamsEmpty() { subpage = args.RemoveParam(0) } if args.Terminator { subpage = dest dest = "" } localRepo, _ := github.LocalRepo() if dest != "" { project = github.NewProject("", dest, "") branch = localRepo.MasterBranch() } else if subpage != "" && subpage != "commits" && subpage != "tree" && subpage != "blob" && subpage != "settings" { project, err = localRepo.MainProject() branch = localRepo.MasterBranch() utils.Check(err) } else { currentBranch, err := localRepo.CurrentBranch() if err != nil { currentBranch = localRepo.MasterBranch() } var owner string mainProject, err := localRepo.MainProject() if err == nil { host, err := github.CurrentConfig().PromptForHost(mainProject.Host) if err != nil { utils.Check(github.FormatError("in browse", err)) } else { owner = host.User } } branch, project, _ = localRepo.RemoteBranchAndProject(owner, currentBranch.IsMaster()) if branch == nil { branch = localRepo.MasterBranch() } } if project == nil { err := fmt.Errorf(command.Synopsis()) utils.Check(err) } if subpage == "commits" { path = fmt.Sprintf("commits/%s", branchInURL(branch)) } else if subpage == "tree" || subpage == "" { if !branch.IsMaster() { path = fmt.Sprintf("tree/%s", branchInURL(branch)) } } else { path = subpage } pageUrl := project.WebURL("", "", path) launcher, err := utils.BrowserLauncher() utils.Check(err) if flagBrowseURLOnly { args.Replace("echo", pageUrl) } else { args.Replace(launcher[0], "", launcher[1:]...) args.AppendParams(pageUrl) } }
func createIssue(cmd *Command, args *Args) { localRepo, err := github.LocalRepo() utils.Check(err) project, err := localRepo.MainProject() utils.Check(err) gh := github.NewClient(project.Host) var title string var body string var editor *github.Editor if cmd.FlagPassed("message") { title, body = readMsg(flagIssueMessage) } else if cmd.FlagPassed("file") { title, body, err = readMsgFromFile(flagIssueFile) utils.Check(err) } else { cs := git.CommentChar() message := strings.Replace(fmt.Sprintf(` # Creating an issue for %s # # Write a message for this issue. The first block of # text is the title and the rest is the description. `, project), "#", cs, -1) editor, err := github.NewEditor("ISSUE", "issue", message) utils.Check(err) title, body, err = editor.EditTitleAndBody() utils.Check(err) } if title == "" { utils.Check(fmt.Errorf("Aborting creation due to empty issue title")) } params := map[string]interface{}{ "title": title, "body": body, "labels": flagIssueLabels, "assignees": flagIssueAssignees, } if flagIssueMilestone > 0 { params["milestone"] = flagIssueMilestone } if args.Noop { ui.Printf("Would create issue `%s' for %s\n", params["title"], project) os.Exit(0) } else { issue, err := gh.CreateIssue(project, params) utils.Check(err) if editor != nil { editor.DeleteFile() } if flagIssueBrowse { launcher, err := utils.BrowserLauncher() utils.Check(err) args.Replace(launcher[0], "", launcher[1:]...) args.AppendParams(issue.HtmlUrl) } else { ui.Println(issue.HtmlUrl) os.Exit(0) } } }
/* $ gh browse > open https://github.com/CURRENT_REPO $ gh browse -- issues > open https://github.com/CURRENT_REPO/issues $ gh browse jingweno/gh > open https://github.com/jingweno/gh $ gh browse gh > open https://github.com/YOUR_LOGIN/gh $ gh browse gh wiki > open https://github.com/YOUR_LOGIN/gh/wiki */ func browse(command *Command, args *Args) { var ( dest string subpage string path string project *github.Project branch *github.Branch err error ) flagBrowseURLOnly := parseFlagBrowseURLOnly(args) if !args.IsParamsEmpty() { dest = args.RemoveParam(0) } if dest == "--" { dest = "" } if !args.IsParamsEmpty() { subpage = args.RemoveParam(0) } localRepo, _ := github.LocalRepo() if dest != "" { project = github.NewProject("", dest, "") branch = localRepo.MasterBranch() } else if subpage != "" && subpage != "commits" && subpage != "tree" && subpage != "blob" && subpage != "settings" { project, err = localRepo.MainProject() branch = localRepo.MasterBranch() utils.Check(err) } else { currentBranch, err := localRepo.CurrentBranch() if err != nil { currentBranch = localRepo.MasterBranch() } branch, project, _ = localRepo.RemoteBranchAndProject("", currentBranch.IsMaster()) if branch == nil { branch = localRepo.MasterBranch() } } if project == nil { err := fmt.Errorf(command.FormattedUsage()) utils.Check(err) } if subpage == "commits" { path = fmt.Sprintf("commits/%s", branchInURL(branch)) } else if subpage == "tree" || subpage == "" { if !branch.IsMaster() { path = fmt.Sprintf("tree/%s", branchInURL(branch)) } } else { path = subpage } pageUrl := project.WebURL("", "", path) launcher, err := utils.BrowserLauncher() utils.Check(err) if flagBrowseURLOnly { args.Replace("echo", pageUrl) } else { args.Replace(launcher[0], "", launcher[1:]...) args.AppendParams(pageUrl) } }