package repo import ( "context" "errors" "fmt" "polar/internal/git" ) var ErrForkNotFound = errors.New("fork repository not found") // MergePullRequest merges an open pull request into its base branch and // records the merge. Same-repository pull requests merge local branches; // cross-repository (fork) pull requests are merged through a temporary ref // fetched from the fork. func (s *Service) MergePullRequest(ctx context.Context, base *Repository, pr *PullRequest, authorName, authorEmail string, mergedByID int64) error { if pr == nil || base == nil { return errors.New("pull request and repository are required") } if pr.Status != "open" { return errors.New("only open pull requests can be merged") } baseDisk, err := s.DiskPath(base.OwnerUsername, base.Name) if err != nil { return err } headLabel := pr.HeadBranch if pr.HeadRepoID != nil && *pr.HeadRepoID != base.ID { headLabel = fmt.Sprintf("%s/%s:%s", pr.HeadRepoOwner, pr.HeadRepoName, pr.HeadBranch) } msg := fmt.Sprintf("Merge pull request #%d from %s\n\n%s", pr.Number, headLabel, pr.Title) if pr.HeadRepoID != nil && *pr.HeadRepoID != base.ID { head, err := s.GetByID(ctx, *pr.HeadRepoID) if err != nil || head == nil { return ErrForkNotFound } forkDisk, err := s.DiskPath(head.OwnerUsername, head.Name) if err != nil { return err } tmpRef := fmt.Sprintf("refs/polar/tmp-merge-%d", pr.ID) if _, err := git.MergeFromPath(ctx, baseDisk, forkDisk, pr.BaseBranch, pr.HeadBranch, tmpRef, msg, authorName, authorEmail); err != nil { return err } } else { if _, err := git.MergeBranches(ctx, baseDisk, pr.BaseBranch, pr.HeadBranch, msg, authorName, authorEmail); err != nil { return err } } if err := s.SetPullRequestStatus(ctx, pr.ID, "merged", &mergedByID); err != nil { return err } s.TouchUpdatedAt(ctx, base.ID) return nil }