func (b *Branch) PushTarget(owner string, preferUpstream bool) (branch *Branch) { var err error pushDefault, _ := git.Config("push.default") if pushDefault == "upstream" || pushDefault == "tracking" { branch, err = b.Upstream() if err != nil { return } } else { shortName := b.ShortName() remotes := b.Repo.remotesForPublish(owner) var remotesInOrder []Remote if preferUpstream { // reverse the remote lookup order // see OriginNamesInLookupOrder for i := len(remotes) - 1; i >= 0; i-- { remotesInOrder = append(remotesInOrder, remotes[i]) } } else { remotesInOrder = remotes } for _, remote := range remotesInOrder { if git.HasFile("refs", "remotes", remote.Name, shortName) { name := fmt.Sprintf("refs/remotes/%s/%s", remote.Name, shortName) branch = &Branch{b.Repo, name} break } } } return }
func sync(cmd *Command, args *Args) { localRepo, err := github.LocalRepo() utils.Check(err) remote, err := localRepo.MainRemote() utils.Check(err) defaultBranch := localRepo.MasterBranch().ShortName() fullDefaultBranch := fmt.Sprintf("refs/remotes/%s/%s", remote.Name, defaultBranch) currentBranch := "" if curBranch, err := localRepo.CurrentBranch(); err == nil { currentBranch = curBranch.ShortName() } err = git.Spawn("fetch", "--prune", "--quiet", "--progress", remote.Name) utils.Check(err) branchToRemote := map[string]string{} if lines, err := git.ConfigAll("branch.*.remote"); err == nil { configRe := regexp.MustCompile(`^branch\.(.+?)\.remote (.+)`) for _, line := range lines { if matches := configRe.FindStringSubmatch(line); len(matches) > 0 { branchToRemote[matches[1]] = matches[2] } } } branches, err := git.LocalBranches() utils.Check(err) var green, lightGreen, red, lightRed, resetColor string if ui.IsTerminal(os.Stdout) { green = "\033[32m" lightGreen = "\033[32;1m" red = "\033[31m" lightRed = "\033[31;1m" resetColor = "\033[0m" } for _, branch := range branches { fullBranch := fmt.Sprintf("refs/heads/%s", branch) remoteBranch := fmt.Sprintf("refs/remotes/%s/%s", remote.Name, branch) gone := false if branchToRemote[branch] == remote.Name { if upstream, err := git.SymbolicFullName(fmt.Sprintf("%s@{upstream}", branch)); err == nil { remoteBranch = upstream } else { remoteBranch = "" gone = true } } else if !git.HasFile(strings.Split(remoteBranch, "/")...) { remoteBranch = "" } if remoteBranch != "" { diff, err := git.NewRange(fullBranch, remoteBranch) utils.Check(err) if diff.IsIdentical() { continue } else if diff.IsAncestor() { if branch == currentBranch { git.Quiet("merge", "--ff-only", "--quiet", remoteBranch) } else { git.Quiet("update-ref", fullBranch, remoteBranch) } ui.Printf("%sUpdated branch %s%s%s (was %s).\n", green, lightGreen, branch, resetColor, diff.A[0:7]) } else { ui.Errorf("warning: `%s' seems to contain unpushed commits\n", branch) } } else if gone { diff, err := git.NewRange(fullBranch, fullDefaultBranch) utils.Check(err) if diff.IsAncestor() { if branch == currentBranch { git.Quiet("checkout", "--quiet", defaultBranch) currentBranch = defaultBranch } git.Quiet("branch", "-D", branch) ui.Printf("%sDeleted branch %s%s%s (was %s).\n", red, lightRed, branch, resetColor, diff.A[0:7]) } else { ui.Errorf("warning: `%s' was deleted on %s, but appears not merged into %s\n", branch, remote.Name, defaultBranch) } } } args.NoForward() }
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 }