func parseCherryPickProjectAndSha(ref string) (project *github.Project, sha string) { url, err := github.ParseURL(ref) if err == nil { commitRegex := regexp.MustCompile("^commit\\/([a-f0-9]{7,40})") projectPath := url.ProjectPath() if commitRegex.MatchString(projectPath) { sha = commitRegex.FindStringSubmatch(projectPath)[1] project = url.Project return } } ownerWithShaRegexp := regexp.MustCompile("^([a-zA-Z0-9][a-zA-Z0-9-]*)@([a-f0-9]{7,40})$") if ownerWithShaRegexp.MatchString(ref) { matches := ownerWithShaRegexp.FindStringSubmatch(ref) sha = matches[2] localRepo, err := github.LocalRepo() utils.Check(err) project, err = localRepo.CurrentProject() utils.Check(err) project.Owner = matches[1] } return }
func fork(cmd *Command, args *Args) { localRepo, err := github.LocalRepo() utils.Check(err) project, err := localRepo.MainProject() if err != nil { utils.Check(fmt.Errorf("Error: repository under 'origin' remote is not a GitHub project")) } config := github.CurrentConfig() host, err := config.PromptForHost(project.Host) if err != nil { utils.Check(github.FormatError("forking repository", err)) } originRemote, err := localRepo.OriginRemote() if err != nil { utils.Check(fmt.Errorf("Error creating fork: %s", err)) } forkProject := github.NewProject(host.User, project.Name, project.Host) newRemoteName := forkProject.Owner client := github.NewClient(project.Host) existingRepo, err := client.Repository(forkProject) if err == nil { var parentURL *github.URL if parent := existingRepo.Parent; parent != nil { parentURL, _ = github.ParseURL(parent.HTMLURL) } if parentURL == nil || !project.SameAs(parentURL.Project) { err = fmt.Errorf("Error creating fork: %s already exists on %s", forkProject, forkProject.Host) utils.Check(err) } } else { if !args.Noop { newRepo, err := client.ForkRepository(project) utils.Check(err) forkProject.Owner = newRepo.Owner.Login forkProject.Name = newRepo.Name } } args.NoForward() if !flagForkNoRemote { originURL := originRemote.URL.String() url := forkProject.GitURL("", "", true) args.Before("git", "remote", "add", "-f", newRemoteName, originURL) args.Before("git", "remote", "set-url", newRemoteName, url) args.AfterFn(func() error { ui.Printf("new remote: %s\n", newRemoteName) return nil }) } }
func transformMergeArgs(args *Args) error { words := args.Words() if len(words) == 0 { return nil } mergeURL := words[0] url, err := github.ParseURL(mergeURL) if err != nil { return nil } pullURLRegex := regexp.MustCompile("^pull/(\\d+)") projectPath := url.ProjectPath() if !pullURLRegex.MatchString(projectPath) { return nil } id := pullURLRegex.FindStringSubmatch(projectPath)[1] gh := github.NewClient(url.Project.Host) pullRequest, err := gh.PullRequest(url.Project, id) if err != nil { return err } repo, err := github.LocalRepo() if err != nil { return err } remote, err := repo.RemoteForRepo(pullRequest.Base.Repo) if err != nil { return err } branch := pullRequest.Head.Ref headRepo := pullRequest.Head.Repo if headRepo == nil { return fmt.Errorf("Error: that fork is not available anymore") } args.Before("git", "fetch", remote.Name, fmt.Sprintf("refs/pull/%s/head", id)) // Remove pull request URL idx := args.IndexOfParam(mergeURL) args.RemoveParam(idx) mergeMsg := fmt.Sprintf("Merge pull request #%s from %s/%s\n\n%s", id, headRepo.Owner.Login, branch, pullRequest.Title) args.AppendParams("FETCH_HEAD", "-m", mergeMsg) if args.IndexOfParam("--ff-only") == -1 && args.IndexOfParam("--squash") == -1 && args.IndexOfParam("--ff") == -1 { i := args.IndexOfParam("-m") args.InsertParam(i, "--no-ff") } return nil }
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 "" }
func transformApplyArgs(args *Args) { gistRegexp := regexp.MustCompile("^https?://gist\\.github\\.com/([\\w.-]+/)?([a-f0-9]+)") pullRegexp := regexp.MustCompile("^(pull|commit)/([0-9a-f]+)") for _, arg := range args.Params { var ( patch io.ReadCloser apiError error ) projectURL, err := github.ParseURL(arg) if err == nil { gh := github.NewClient(projectURL.Project.Host) match := pullRegexp.FindStringSubmatch(projectURL.ProjectPath()) if match != nil { if match[1] == "pull" { patch, apiError = gh.PullRequestPatch(projectURL.Project, match[2]) } else { patch, apiError = gh.CommitPatch(projectURL.Project, match[2]) } } } else { match := gistRegexp.FindStringSubmatch(arg) if match != nil { // TODO: support Enterprise gist gh := github.NewClient(github.GitHubHost) patch, apiError = gh.GistPatch(match[2]) } } utils.Check(apiError) if patch == nil { continue } idx := args.IndexOfParam(arg) patchFile, err := ioutil.TempFile("", "hub") utils.Check(err) _, err = io.Copy(patchFile, patch) utils.Check(err) patchFile.Close() patch.Close() args.Params[idx] = patchFile.Name() } }
func parseCherryPickProjectAndSha(ref string) (project *github.Project, sha string, isPrivate bool) { shaRe := "[a-f0-9]{7,40}" var mainProject *github.Project localRepo, mainProjectErr := github.LocalRepo() if mainProjectErr == nil { mainProject, mainProjectErr = localRepo.MainProject() } url, err := github.ParseURL(ref) if err == nil { projectPath := url.ProjectPath() commitRegex := regexp.MustCompile(fmt.Sprintf("^commit/(%s)", shaRe)) if matches := commitRegex.FindStringSubmatch(projectPath); len(matches) > 0 { sha = matches[1] project = url.Project return } pullRegex := regexp.MustCompile(fmt.Sprintf(`^pull/(\d+)/commits/(%s)`, shaRe)) if matches := pullRegex.FindStringSubmatch(projectPath); len(matches) > 0 { pullId := matches[1] sha = matches[2] utils.Check(mainProjectErr) api := github.NewClient(mainProject.Host) pullRequest, err := api.PullRequest(url.Project, pullId) utils.Check(err) headRepo := pullRequest.Head.Repo project = github.NewProject(headRepo.Owner.Login, headRepo.Name, mainProject.Host) isPrivate = headRepo.Private return } } ownerWithShaRegexp := regexp.MustCompile(fmt.Sprintf("^(%s)@(%s)$", OwnerRe, shaRe)) if matches := ownerWithShaRegexp.FindStringSubmatch(ref); len(matches) > 0 { utils.Check(mainProjectErr) project = mainProject project.Owner = matches[1] sha = matches[2] } return }
func transformCheckoutArgs(args *Args) error { words := args.Words() if len(words) == 0 { return nil } checkoutURL := words[0] var newBranchName string if len(words) > 1 { newBranchName = words[1] } url, err := github.ParseURL(checkoutURL) if err != nil { // not a valid GitHub URL return nil } pullURLRegex := regexp.MustCompile("^pull/(\\d+)") projectPath := url.ProjectPath() if !pullURLRegex.MatchString(projectPath) { // not a valid PR URL return nil } err = sanitizeCheckoutFlags(args) if err != nil { return err } id := pullURLRegex.FindStringSubmatch(projectPath)[1] gh := github.NewClient(url.Project.Host) pullRequest, err := gh.PullRequest(url.Project, id) if err != nil { return err } if idx := args.IndexOfParam(newBranchName); idx >= 0 { args.RemoveParam(idx) } branch := pullRequest.Head.Ref headRepo := pullRequest.Head.Repo if headRepo == nil { return fmt.Errorf("Error: that fork is not available anymore") } user := headRepo.Owner.Login if newBranchName == "" { newBranchName = fmt.Sprintf("%s-%s", user, branch) } repo, err := github.LocalRepo() utils.Check(err) _, err = repo.RemoteByName(user) if err == nil { args.Before("git", "remote", "set-branches", "--add", user, branch) remoteURL := fmt.Sprintf("+refs/heads/%s:refs/remotes/%s/%s", branch, user, branch) args.Before("git", "fetch", user, remoteURL) } else { u := url.Project.GitURL(pullRequest.Head.Repo.Name, user, pullRequest.Head.Repo.Private) args.Before("git", "remote", "add", "-f", "--no-tags", "-t", branch, user, u) } remoteName := fmt.Sprintf("%s/%s", user, branch) replaceCheckoutParam(args, checkoutURL, newBranchName, remoteName) return nil }
func transformCheckoutArgs(args *Args) error { words := args.Words() if len(words) == 0 { return nil } checkoutURL := words[0] var newBranchName string if len(words) > 1 { newBranchName = words[1] } url, err := github.ParseURL(checkoutURL) if err != nil { // not a valid GitHub URL return nil } pullURLRegex := regexp.MustCompile("^pull/(\\d+)") projectPath := url.ProjectPath() if !pullURLRegex.MatchString(projectPath) { // not a valid PR URL return nil } err = sanitizeCheckoutFlags(args) if err != nil { return err } id := pullURLRegex.FindStringSubmatch(projectPath)[1] gh := github.NewClient(url.Project.Host) pullRequest, err := gh.PullRequest(url.Project, id) if err != nil { return err } if idx := args.IndexOfParam(newBranchName); idx >= 0 { args.RemoveParam(idx) } repo, err := github.LocalRepo() if err != nil { return err } baseRemote, err := repo.RemoteForRepo(pullRequest.Base.Repo) if err != nil { return err } var headRemote *github.Remote if pullRequest.IsSameRepo() { headRemote = baseRemote } else if pullRequest.Head.Repo != nil { headRemote, _ = repo.RemoteForRepo(pullRequest.Head.Repo) } var newArgs []string if headRemote != nil { if newBranchName == "" { newBranchName = pullRequest.Head.Ref } remoteBranch := fmt.Sprintf("%s/%s", headRemote.Name, pullRequest.Head.Ref) refSpec := fmt.Sprintf("+refs/heads/%s:refs/remotes/%s", pullRequest.Head.Ref, remoteBranch) if git.HasFile("refs", "heads", newBranchName) { newArgs = append(newArgs, newBranchName) args.After("git", "merge", "--ff-only", fmt.Sprintf("refs/remotes/%s", remoteBranch)) } else { newArgs = append(newArgs, "-b", newBranchName, "--track", remoteBranch) } args.Before("git", "fetch", headRemote.Name, refSpec) } else { if newBranchName == "" { if pullRequest.Head.Repo == nil { newBranchName = fmt.Sprintf("pr-%s", id) } else { newBranchName = fmt.Sprintf("%s-%s", pullRequest.Head.Repo.Owner.Login, pullRequest.Head.Ref) } } refSpec := fmt.Sprintf("refs/pull/%s/head:%s", id, newBranchName) newArgs = append(newArgs, newBranchName) args.Before("git", "fetch", baseRemote.Name, refSpec) } replaceCheckoutParam(args, checkoutURL, newArgs...) return nil }