* Display pull request head branch even the branch deleted or repository deleted * Merge getHeadRepo/loadHeadRepo and getBaseRepo/loadBaseRepo on pull and fill repo when pr.Issue.Repo is available * retrieve sha from pull head when pull request branch deleted and fix tests * Fix test * Ensure MustHeadRepoName returns empty string if no head repo Co-authored-by: zeripath <art27@cantab.net>tags/v1.10.5
MergerID int64 `xorm:"INDEX"` | MergerID int64 `xorm:"INDEX"` | ||||
Merger *User `xorm:"-"` | Merger *User `xorm:"-"` | ||||
MergedUnix timeutil.TimeStamp `xorm:"updated INDEX"` | MergedUnix timeutil.TimeStamp `xorm:"updated INDEX"` | ||||
isHeadRepoLoaded bool `xorm:"-"` | |||||
} | } | ||||
// MustHeadUserName returns the HeadRepo's username if failed return blank | // MustHeadUserName returns the HeadRepo's username if failed return blank | ||||
} | } | ||||
return "" | return "" | ||||
} | } | ||||
if pr.HeadRepo == nil { | |||||
return "" | |||||
} | |||||
return pr.HeadRepo.OwnerName | return pr.HeadRepo.OwnerName | ||||
} | } | ||||
return pr.loadAttributes(x) | return pr.loadAttributes(x) | ||||
} | } | ||||
// LoadBaseRepo loads pull request base repository from database | |||||
func (pr *PullRequest) LoadBaseRepo() error { | |||||
if pr.BaseRepo == nil { | |||||
if pr.HeadRepoID == pr.BaseRepoID && pr.HeadRepo != nil { | |||||
pr.BaseRepo = pr.HeadRepo | |||||
return nil | |||||
func (pr *PullRequest) loadHeadRepo(e Engine) (err error) { | |||||
if !pr.isHeadRepoLoaded && pr.HeadRepo == nil && pr.HeadRepoID > 0 { | |||||
if pr.HeadRepoID == pr.BaseRepoID { | |||||
if pr.BaseRepo != nil { | |||||
pr.HeadRepo = pr.BaseRepo | |||||
return nil | |||||
} else if pr.Issue != nil && pr.Issue.Repo != nil { | |||||
pr.HeadRepo = pr.Issue.Repo | |||||
return nil | |||||
} | |||||
} | } | ||||
var repo Repository | |||||
if has, err := x.ID(pr.BaseRepoID).Get(&repo); err != nil { | |||||
return err | |||||
} else if !has { | |||||
return ErrRepoNotExist{ID: pr.BaseRepoID} | |||||
pr.HeadRepo, err = getRepositoryByID(e, pr.HeadRepoID) | |||||
if err != nil && !IsErrRepoNotExist(err) { // Head repo maybe deleted, but it should still work | |||||
return fmt.Errorf("getRepositoryByID(head): %v", err) | |||||
} | } | ||||
pr.BaseRepo = &repo | |||||
pr.isHeadRepoLoaded = true | |||||
} | } | ||||
return nil | return nil | ||||
} | } | ||||
// LoadHeadRepo loads pull request head repository from database | |||||
// LoadHeadRepo loads the head repository | |||||
func (pr *PullRequest) LoadHeadRepo() error { | func (pr *PullRequest) LoadHeadRepo() error { | ||||
if pr.HeadRepo == nil { | |||||
if pr.HeadRepoID == pr.BaseRepoID && pr.BaseRepo != nil { | |||||
pr.HeadRepo = pr.BaseRepo | |||||
return nil | |||||
} | |||||
var repo Repository | |||||
if has, err := x.ID(pr.HeadRepoID).Get(&repo); err != nil { | |||||
return err | |||||
} else if !has { | |||||
return ErrRepoNotExist{ID: pr.HeadRepoID} | |||||
} | |||||
pr.HeadRepo = &repo | |||||
return pr.loadHeadRepo(x) | |||||
} | |||||
// LoadBaseRepo loads the target repository | |||||
func (pr *PullRequest) LoadBaseRepo() error { | |||||
return pr.loadBaseRepo(x) | |||||
} | |||||
func (pr *PullRequest) loadBaseRepo(e Engine) (err error) { | |||||
if pr.BaseRepo != nil { | |||||
return nil | |||||
} | |||||
if pr.HeadRepoID == pr.BaseRepoID && pr.HeadRepo != nil { | |||||
pr.BaseRepo = pr.HeadRepo | |||||
return nil | |||||
} | |||||
if pr.Issue != nil && pr.Issue.Repo != nil { | |||||
pr.BaseRepo = pr.Issue.Repo | |||||
return nil | |||||
} | |||||
pr.BaseRepo, err = getRepositoryByID(e, pr.BaseRepoID) | |||||
if err != nil { | |||||
return fmt.Errorf("GetRepositoryByID(base): %v", err) | |||||
} | } | ||||
return nil | return nil | ||||
} | } | ||||
return fmt.Sprintf("refs/pull/%d/head", pr.Index) | return fmt.Sprintf("refs/pull/%d/head", pr.Index) | ||||
} | } | ||||
func (pr *PullRequest) getHeadRepo(e Engine) (err error) { | |||||
pr.HeadRepo, err = getRepositoryByID(e, pr.HeadRepoID) | |||||
if err != nil && !IsErrRepoNotExist(err) { | |||||
return fmt.Errorf("getRepositoryByID(head): %v", err) | |||||
} | |||||
return nil | |||||
} | |||||
// GetHeadRepo loads the head repository | |||||
func (pr *PullRequest) GetHeadRepo() error { | |||||
return pr.getHeadRepo(x) | |||||
} | |||||
// GetBaseRepo loads the target repository | |||||
func (pr *PullRequest) GetBaseRepo() (err error) { | |||||
if pr.BaseRepo != nil { | |||||
return nil | |||||
} | |||||
pr.BaseRepo, err = GetRepositoryByID(pr.BaseRepoID) | |||||
if err != nil { | |||||
return fmt.Errorf("GetRepositoryByID(base): %v", err) | |||||
} | |||||
return nil | |||||
} | |||||
// IsChecking returns true if this pull request is still checking conflict. | // IsChecking returns true if this pull request is still checking conflict. | ||||
func (pr *PullRequest) IsChecking() bool { | func (pr *PullRequest) IsChecking() bool { | ||||
return pr.Status == PullRequestStatusChecking | return pr.Status == PullRequestStatusChecking | ||||
// GetLastCommitStatus returns the last commit status for this pull request. | // GetLastCommitStatus returns the last commit status for this pull request. | ||||
func (pr *PullRequest) GetLastCommitStatus() (status *CommitStatus, err error) { | func (pr *PullRequest) GetLastCommitStatus() (status *CommitStatus, err error) { | ||||
if err = pr.GetHeadRepo(); err != nil { | |||||
if err = pr.LoadHeadRepo(); err != nil { | |||||
return nil, err | return nil, err | ||||
} | } | ||||
// IsHeadEqualWithBranch returns if the commits of branchName are available in pull request head | // IsHeadEqualWithBranch returns if the commits of branchName are available in pull request head | ||||
func (pr *PullRequest) IsHeadEqualWithBranch(branchName string) (bool, error) { | func (pr *PullRequest) IsHeadEqualWithBranch(branchName string) (bool, error) { | ||||
var err error | var err error | ||||
if err = pr.GetBaseRepo(); err != nil { | |||||
if err = pr.LoadBaseRepo(); err != nil { | |||||
return false, err | return false, err | ||||
} | } | ||||
baseGitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath()) | baseGitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath()) | ||||
return false, err | return false, err | ||||
} | } | ||||
if err = pr.GetHeadRepo(); err != nil { | |||||
if err = pr.LoadHeadRepo(); err != nil { | |||||
return false, err | return false, err | ||||
} | } | ||||
headGitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath()) | headGitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath()) |
// SignMerge determines if we should sign a PR merge commit to the base repository | // SignMerge determines if we should sign a PR merge commit to the base repository | ||||
func (pr *PullRequest) SignMerge(u *User, tmpBasePath, baseCommit, headCommit string) (bool, string, error) { | func (pr *PullRequest) SignMerge(u *User, tmpBasePath, baseCommit, headCommit string) (bool, string, error) { | ||||
if err := pr.GetBaseRepo(); err != nil { | |||||
if err := pr.LoadBaseRepo(); err != nil { | |||||
log.Error("Unable to get Base Repo for pull request") | log.Error("Unable to get Base Repo for pull request") | ||||
return false, "", err | return false, "", err | ||||
} | } |
assert.Equal(t, int64(2), pr.Issue.ID) | assert.Equal(t, int64(2), pr.Issue.ID) | ||||
} | } | ||||
func TestPullRequest_GetBaseRepo(t *testing.T) { | |||||
func TestPullRequest_LoadBaseRepo(t *testing.T) { | |||||
assert.NoError(t, PrepareTestDatabase()) | assert.NoError(t, PrepareTestDatabase()) | ||||
pr := AssertExistsAndLoadBean(t, &PullRequest{ID: 1}).(*PullRequest) | pr := AssertExistsAndLoadBean(t, &PullRequest{ID: 1}).(*PullRequest) | ||||
assert.NoError(t, pr.GetBaseRepo()) | |||||
assert.NoError(t, pr.LoadBaseRepo()) | |||||
assert.NotNil(t, pr.BaseRepo) | assert.NotNil(t, pr.BaseRepo) | ||||
assert.Equal(t, pr.BaseRepoID, pr.BaseRepo.ID) | assert.Equal(t, pr.BaseRepoID, pr.BaseRepo.ID) | ||||
assert.NoError(t, pr.GetBaseRepo()) | |||||
assert.NoError(t, pr.LoadBaseRepo()) | |||||
assert.NotNil(t, pr.BaseRepo) | assert.NotNil(t, pr.BaseRepo) | ||||
assert.Equal(t, pr.BaseRepoID, pr.BaseRepo.ID) | assert.Equal(t, pr.BaseRepoID, pr.BaseRepo.ID) | ||||
} | } | ||||
func TestPullRequest_GetHeadRepo(t *testing.T) { | |||||
func TestPullRequest_LoadHeadRepo(t *testing.T) { | |||||
assert.NoError(t, PrepareTestDatabase()) | assert.NoError(t, PrepareTestDatabase()) | ||||
pr := AssertExistsAndLoadBean(t, &PullRequest{ID: 1}).(*PullRequest) | pr := AssertExistsAndLoadBean(t, &PullRequest{ID: 1}).(*PullRequest) | ||||
assert.NoError(t, pr.GetHeadRepo()) | |||||
assert.NoError(t, pr.LoadHeadRepo()) | |||||
assert.NotNil(t, pr.HeadRepo) | assert.NotNil(t, pr.HeadRepo) | ||||
assert.Equal(t, pr.HeadRepoID, pr.HeadRepo.ID) | assert.Equal(t, pr.HeadRepoID, pr.HeadRepo.ID) | ||||
} | } |
baseBranch *git.Branch | baseBranch *git.Branch | ||||
headBranch *git.Branch | headBranch *git.Branch | ||||
baseCommit *git.Commit | baseCommit *git.Commit | ||||
headCommit *git.Commit | |||||
err error | err error | ||||
) | ) | ||||
} | } | ||||
apiIssue := ToAPIIssue(pr.Issue) | apiIssue := ToAPIIssue(pr.Issue) | ||||
if pr.BaseRepo == nil { | |||||
pr.BaseRepo, err = models.GetRepositoryByID(pr.BaseRepoID) | |||||
if err != nil { | |||||
log.Error("GetRepositoryById[%d]: %v", pr.ID, err) | |||||
return nil | |||||
} | |||||
if err := pr.LoadBaseRepo(); err != nil { | |||||
log.Error("GetRepositoryById[%d]: %v", pr.ID, err) | |||||
return nil | |||||
} | } | ||||
if pr.HeadRepoID != 0 && pr.HeadRepo == nil { | |||||
pr.HeadRepo, err = models.GetRepositoryByID(pr.HeadRepoID) | |||||
if err != nil && !models.IsErrRepoNotExist(err) { | |||||
log.Error("GetRepositoryById[%d]: %v", pr.ID, err) | |||||
return nil | |||||
} | |||||
if err := pr.LoadHeadRepo(); err != nil { | |||||
log.Error("GetRepositoryById[%d]: %v", pr.ID, err) | |||||
return nil | |||||
} | } | ||||
apiPullRequest := &api.PullRequest{ | apiPullRequest := &api.PullRequest{ | ||||
Deadline: apiIssue.Deadline, | Deadline: apiIssue.Deadline, | ||||
Created: pr.Issue.CreatedUnix.AsTimePtr(), | Created: pr.Issue.CreatedUnix.AsTimePtr(), | ||||
Updated: pr.Issue.UpdatedUnix.AsTimePtr(), | Updated: pr.Issue.UpdatedUnix.AsTimePtr(), | ||||
} | |||||
baseBranch, err = repo_module.GetBranch(pr.BaseRepo, pr.BaseBranch) | |||||
if err != nil { | |||||
if git.IsErrBranchNotExist(err) { | |||||
apiPullRequest.Base = nil | |||||
} else { | |||||
log.Error("GetBranch[%s]: %v", pr.BaseBranch, err) | |||||
return nil | |||||
} | |||||
} else { | |||||
apiBaseBranchInfo := &api.PRBranchInfo{ | |||||
Base: &api.PRBranchInfo{ | |||||
Name: pr.BaseBranch, | Name: pr.BaseBranch, | ||||
Ref: pr.BaseBranch, | Ref: pr.BaseBranch, | ||||
RepoID: pr.BaseRepoID, | RepoID: pr.BaseRepoID, | ||||
Repository: pr.BaseRepo.APIFormat(models.AccessModeNone), | Repository: pr.BaseRepo.APIFormat(models.AccessModeNone), | ||||
} | |||||
}, | |||||
Head: &api.PRBranchInfo{ | |||||
Name: pr.HeadBranch, | |||||
Ref: fmt.Sprintf("refs/pull/%d/head", pr.Index), | |||||
RepoID: -1, | |||||
}, | |||||
} | |||||
baseBranch, err = repo_module.GetBranch(pr.BaseRepo, pr.BaseBranch) | |||||
if err != nil && !git.IsErrBranchNotExist(err) { | |||||
log.Error("GetBranch[%s]: %v", pr.BaseBranch, err) | |||||
return nil | |||||
} | |||||
if err == nil { | |||||
baseCommit, err = baseBranch.GetCommit() | baseCommit, err = baseBranch.GetCommit() | ||||
if err != nil { | |||||
if git.IsErrNotExist(err) { | |||||
apiBaseBranchInfo.Sha = "" | |||||
} else { | |||||
log.Error("GetCommit[%s]: %v", baseBranch.Name, err) | |||||
return nil | |||||
} | |||||
} else { | |||||
apiBaseBranchInfo.Sha = baseCommit.ID.String() | |||||
if err != nil && !git.IsErrNotExist(err) { | |||||
log.Error("GetCommit[%s]: %v", baseBranch.Name, err) | |||||
return nil | |||||
} | |||||
if err == nil { | |||||
apiPullRequest.Base.Sha = baseCommit.ID.String() | |||||
} | } | ||||
apiPullRequest.Base = apiBaseBranchInfo | |||||
} | } | ||||
if pr.HeadRepo != nil { | if pr.HeadRepo != nil { | ||||
headBranch, err = repo_module.GetBranch(pr.HeadRepo, pr.HeadBranch) | |||||
apiPullRequest.Head.RepoID = pr.HeadRepo.ID | |||||
apiPullRequest.Head.Repository = pr.HeadRepo.APIFormat(models.AccessModeNone) | |||||
headGitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath()) | |||||
if err != nil { | if err != nil { | ||||
if git.IsErrBranchNotExist(err) { | |||||
apiPullRequest.Head = nil | |||||
} else { | |||||
log.Error("GetBranch[%s]: %v", pr.HeadBranch, err) | |||||
log.Error("OpenRepository[%s]: %v", pr.HeadRepo.RepoPath(), err) | |||||
return nil | |||||
} | |||||
defer headGitRepo.Close() | |||||
headBranch, err = headGitRepo.GetBranch(pr.HeadBranch) | |||||
if err != nil && !git.IsErrBranchNotExist(err) { | |||||
log.Error("GetBranch[%s]: %v", pr.HeadBranch, err) | |||||
return nil | |||||
} | |||||
if git.IsErrBranchNotExist(err) { | |||||
headCommitID, err := headGitRepo.GetRefCommitID(apiPullRequest.Head.Ref) | |||||
if err != nil && !git.IsErrNotExist(err) { | |||||
log.Error("GetCommit[%s]: %v", headBranch.Name, err) | |||||
return nil | return nil | ||||
} | } | ||||
if err == nil { | |||||
apiPullRequest.Head.Sha = headCommitID | |||||
} | |||||
} else { | } else { | ||||
apiHeadBranchInfo := &api.PRBranchInfo{ | |||||
Name: pr.HeadBranch, | |||||
Ref: pr.HeadBranch, | |||||
RepoID: pr.HeadRepoID, | |||||
Repository: pr.HeadRepo.APIFormat(models.AccessModeNone), | |||||
commit, err := headBranch.GetCommit() | |||||
if err != nil && !git.IsErrNotExist(err) { | |||||
log.Error("GetCommit[%s]: %v", headBranch.Name, err) | |||||
return nil | |||||
} | } | ||||
headCommit, err = headBranch.GetCommit() | |||||
if err != nil { | |||||
if git.IsErrNotExist(err) { | |||||
apiHeadBranchInfo.Sha = "" | |||||
} else { | |||||
log.Error("GetCommit[%s]: %v", headBranch.Name, err) | |||||
return nil | |||||
} | |||||
} else { | |||||
apiHeadBranchInfo.Sha = headCommit.ID.String() | |||||
if err == nil { | |||||
apiPullRequest.Head.Ref = pr.HeadBranch | |||||
apiPullRequest.Head.Sha = commit.ID.String() | |||||
} | } | ||||
apiPullRequest.Head = apiHeadBranchInfo | |||||
} | |||||
} else { | |||||
apiPullRequest.Head = &api.PRBranchInfo{ | |||||
Name: pr.HeadBranch, | |||||
Ref: fmt.Sprintf("refs/pull/%d/head", pr.Index), | |||||
RepoID: -1, | |||||
} | } | ||||
} | } | ||||
"testing" | "testing" | ||||
"code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
"code.gitea.io/gitea/modules/structs" | |||||
"github.com/stretchr/testify/assert" | "github.com/stretchr/testify/assert" | ||||
) | ) | ||||
func TestPullRequest_APIFormat(t *testing.T) { | func TestPullRequest_APIFormat(t *testing.T) { | ||||
//with HeadRepo | //with HeadRepo | ||||
assert.NoError(t, models.PrepareTestDatabase()) | assert.NoError(t, models.PrepareTestDatabase()) | ||||
headRepo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) | |||||
pr := models.AssertExistsAndLoadBean(t, &models.PullRequest{ID: 1}).(*models.PullRequest) | pr := models.AssertExistsAndLoadBean(t, &models.PullRequest{ID: 1}).(*models.PullRequest) | ||||
assert.NoError(t, pr.LoadAttributes()) | assert.NoError(t, pr.LoadAttributes()) | ||||
assert.NoError(t, pr.LoadIssue()) | assert.NoError(t, pr.LoadIssue()) | ||||
apiPullRequest := ToAPIPullRequest(pr) | apiPullRequest := ToAPIPullRequest(pr) | ||||
assert.NotNil(t, apiPullRequest) | assert.NotNil(t, apiPullRequest) | ||||
assert.Nil(t, apiPullRequest.Head) | |||||
assert.EqualValues(t, &structs.PRBranchInfo{ | |||||
Name: "branch1", | |||||
Ref: "refs/pull/2/head", | |||||
Sha: "4a357436d925b5c974181ff12a994538ddc5a269", | |||||
RepoID: 1, | |||||
Repository: headRepo.APIFormat(models.AccessModeNone), | |||||
}, apiPullRequest.Head) | |||||
//withOut HeadRepo | //withOut HeadRepo | ||||
pr = models.AssertExistsAndLoadBean(t, &models.PullRequest{ID: 1}).(*models.PullRequest) | pr = models.AssertExistsAndLoadBean(t, &models.PullRequest{ID: 1}).(*models.PullRequest) |
ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) | ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) | ||||
return | return | ||||
} | } | ||||
if err = prs[i].GetBaseRepo(); err != nil { | |||||
ctx.Error(http.StatusInternalServerError, "GetBaseRepo", err) | |||||
if err = prs[i].LoadBaseRepo(); err != nil { | |||||
ctx.Error(http.StatusInternalServerError, "LoadBaseRepo", err) | |||||
return | return | ||||
} | } | ||||
if err = prs[i].GetHeadRepo(); err != nil { | |||||
ctx.Error(http.StatusInternalServerError, "GetHeadRepo", err) | |||||
if err = prs[i].LoadHeadRepo(); err != nil { | |||||
ctx.Error(http.StatusInternalServerError, "LoadHeadRepo", err) | |||||
return | return | ||||
} | } | ||||
apiPrs[i] = convert.ToAPIPullRequest(prs[i]) | apiPrs[i] = convert.ToAPIPullRequest(prs[i]) | ||||
return | return | ||||
} | } | ||||
if err = pr.GetBaseRepo(); err != nil { | |||||
ctx.Error(http.StatusInternalServerError, "GetBaseRepo", err) | |||||
if err = pr.LoadBaseRepo(); err != nil { | |||||
ctx.Error(http.StatusInternalServerError, "LoadBaseRepo", err) | |||||
return | return | ||||
} | } | ||||
if err = pr.GetHeadRepo(); err != nil { | |||||
ctx.Error(http.StatusInternalServerError, "GetHeadRepo", err) | |||||
if err = pr.LoadHeadRepo(); err != nil { | |||||
ctx.Error(http.StatusInternalServerError, "LoadHeadRepo", err) | |||||
return | return | ||||
} | } | ||||
ctx.JSON(http.StatusOK, convert.ToAPIPullRequest(pr)) | ctx.JSON(http.StatusOK, convert.ToAPIPullRequest(pr)) | ||||
return | return | ||||
} | } | ||||
if err = pr.GetHeadRepo(); err != nil { | |||||
ctx.ServerError("GetHeadRepo", err) | |||||
if err = pr.LoadHeadRepo(); err != nil { | |||||
ctx.ServerError("LoadHeadRepo", err) | |||||
return | return | ||||
} | } | ||||
ctx.Data["AllowMerge"] = false | ctx.Data["AllowMerge"] = false | ||||
if ctx.IsSigned { | if ctx.IsSigned { | ||||
if err := pull.GetHeadRepo(); err != nil { | |||||
log.Error("GetHeadRepo: %v", err) | |||||
if err := pull.LoadHeadRepo(); err != nil { | |||||
log.Error("LoadHeadRepo: %v", err) | |||||
} else if pull.HeadRepo != nil && pull.HeadBranch != pull.HeadRepo.DefaultBranch { | } else if pull.HeadRepo != nil && pull.HeadBranch != pull.HeadRepo.DefaultBranch { | ||||
perm, err := models.GetUserRepoPermission(pull.HeadRepo, ctx.User) | perm, err := models.GetUserRepoPermission(pull.HeadRepo, ctx.User) | ||||
if err != nil { | if err != nil { | ||||
} | } | ||||
} | } | ||||
if err := pull.GetBaseRepo(); err != nil { | |||||
log.Error("GetBaseRepo: %v", err) | |||||
if err := pull.LoadBaseRepo(); err != nil { | |||||
log.Error("LoadBaseRepo: %v", err) | |||||
} | } | ||||
perm, err := models.GetUserRepoPermission(pull.BaseRepo, ctx.User) | perm, err := models.GetUserRepoPermission(pull.BaseRepo, ctx.User) | ||||
if err != nil { | if err != nil { |
return nil | return nil | ||||
} | } | ||||
if err = issue.PullRequest.GetHeadRepo(); err != nil { | |||||
ctx.ServerError("GetHeadRepo", err) | |||||
if err = issue.PullRequest.LoadHeadRepo(); err != nil { | |||||
ctx.ServerError("LoadHeadRepo", err) | |||||
return nil | return nil | ||||
} | } | ||||
if err != nil { | if err != nil { | ||||
if strings.Contains(err.Error(), "fatal: Not a valid object name") { | if strings.Contains(err.Error(), "fatal: Not a valid object name") { | ||||
ctx.Data["IsPullRequestBroken"] = true | ctx.Data["IsPullRequestBroken"] = true | ||||
ctx.Data["BaseTarget"] = "deleted" | |||||
ctx.Data["BaseTarget"] = pull.BaseBranch | |||||
ctx.Data["NumCommits"] = 0 | ctx.Data["NumCommits"] = 0 | ||||
ctx.Data["NumFiles"] = 0 | ctx.Data["NumFiles"] = 0 | ||||
return nil | return nil | ||||
repo := ctx.Repo.Repository | repo := ctx.Repo.Repository | ||||
pull := issue.PullRequest | pull := issue.PullRequest | ||||
if err := pull.GetHeadRepo(); err != nil { | |||||
ctx.ServerError("GetHeadRepo", err) | |||||
if err := pull.LoadHeadRepo(); err != nil { | |||||
ctx.ServerError("LoadHeadRepo", err) | |||||
return nil | return nil | ||||
} | } | ||||
if err := pull.GetBaseRepo(); err != nil { | |||||
ctx.ServerError("GetBaseRepo", err) | |||||
if err := pull.LoadBaseRepo(); err != nil { | |||||
ctx.ServerError("LoadBaseRepo", err) | |||||
return nil | return nil | ||||
} | } | ||||
if pull.HeadRepo == nil || !headBranchExist || headBranchSha != sha { | if pull.HeadRepo == nil || !headBranchExist || headBranchSha != sha { | ||||
ctx.Data["IsPullRequestBroken"] = true | ctx.Data["IsPullRequestBroken"] = true | ||||
ctx.Data["HeadTarget"] = "deleted" | |||||
if pull.IsSameRepo() { | |||||
ctx.Data["HeadTarget"] = pull.HeadBranch | |||||
} else { | |||||
if pull.HeadRepo == nil { | |||||
ctx.Data["HeadTarget"] = "<deleted>:" + pull.HeadBranch | |||||
} else { | |||||
ctx.Data["HeadTarget"] = pull.HeadRepo.OwnerName + ":" + pull.HeadBranch | |||||
} | |||||
} | |||||
} | } | ||||
compareInfo, err := baseGitRepo.GetCompareInfo(pull.BaseRepo.RepoPath(), | compareInfo, err := baseGitRepo.GetCompareInfo(pull.BaseRepo.RepoPath(), | ||||
if err != nil { | if err != nil { | ||||
if strings.Contains(err.Error(), "fatal: Not a valid object name") { | if strings.Contains(err.Error(), "fatal: Not a valid object name") { | ||||
ctx.Data["IsPullRequestBroken"] = true | ctx.Data["IsPullRequestBroken"] = true | ||||
ctx.Data["BaseTarget"] = "deleted" | |||||
ctx.Data["BaseTarget"] = pull.BaseBranch | |||||
ctx.Data["NumCommits"] = 0 | ctx.Data["NumCommits"] = 0 | ||||
ctx.Data["NumFiles"] = 0 | ctx.Data["NumFiles"] = 0 | ||||
return nil | return nil | ||||
return | return | ||||
} | } | ||||
if err := pr.GetHeadRepo(); err != nil { | |||||
ctx.ServerError("GetHeadRepo", err) | |||||
if err := pr.LoadHeadRepo(); err != nil { | |||||
ctx.ServerError("LoadHeadRepo", err) | |||||
return | return | ||||
} else if pr.HeadRepo == nil { | } else if pr.HeadRepo == nil { | ||||
// Forked repository has already been deleted | // Forked repository has already been deleted | ||||
ctx.NotFound("CleanUpPullRequest", nil) | ctx.NotFound("CleanUpPullRequest", nil) | ||||
return | return | ||||
} else if err = pr.GetBaseRepo(); err != nil { | |||||
ctx.ServerError("GetBaseRepo", err) | |||||
} else if err = pr.LoadBaseRepo(); err != nil { | |||||
ctx.ServerError("LoadBaseRepo", err) | |||||
return | return | ||||
} else if err = pr.HeadRepo.GetOwner(); err != nil { | } else if err = pr.HeadRepo.GetOwner(); err != nil { | ||||
ctx.ServerError("HeadRepo.GetOwner", err) | ctx.ServerError("HeadRepo.GetOwner", err) |
// Caller should check PR is ready to be merged (review and status checks) | // Caller should check PR is ready to be merged (review and status checks) | ||||
// FIXME: add repoWorkingPull make sure two merges does not happen at same time. | // FIXME: add repoWorkingPull make sure two merges does not happen at same time. | ||||
func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repository, mergeStyle models.MergeStyle, message string) (err error) { | func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repository, mergeStyle models.MergeStyle, message string) (err error) { | ||||
if err = pr.GetHeadRepo(); err != nil { | |||||
log.Error("GetHeadRepo: %v", err) | |||||
return fmt.Errorf("GetHeadRepo: %v", err) | |||||
} else if err = pr.GetBaseRepo(); err != nil { | |||||
log.Error("GetBaseRepo: %v", err) | |||||
return fmt.Errorf("GetBaseRepo: %v", err) | |||||
if err = pr.LoadHeadRepo(); err != nil { | |||||
log.Error("LoadHeadRepo: %v", err) | |||||
return fmt.Errorf("LoadHeadRepo: %v", err) | |||||
} else if err = pr.LoadBaseRepo(); err != nil { | |||||
log.Error("LoadBaseRepo: %v", err) | |||||
return fmt.Errorf("LoadBaseRepo: %v", err) | |||||
} | } | ||||
prUnit, err := pr.BaseRepo.GetUnit(models.UnitTypePullRequests) | prUnit, err := pr.BaseRepo.GetUnit(models.UnitTypePullRequests) | ||||
// CheckPRReadyToMerge checks whether the PR is ready to be merged (reviews and status checks) | // CheckPRReadyToMerge checks whether the PR is ready to be merged (reviews and status checks) | ||||
func CheckPRReadyToMerge(pr *models.PullRequest) (err error) { | func CheckPRReadyToMerge(pr *models.PullRequest) (err error) { | ||||
if pr.BaseRepo == nil { | |||||
if err = pr.GetBaseRepo(); err != nil { | |||||
return fmt.Errorf("GetBaseRepo: %v", err) | |||||
} | |||||
if err = pr.LoadBaseRepo(); err != nil { | |||||
return fmt.Errorf("LoadBaseRepo: %v", err) | |||||
} | |||||
if err = pr.LoadProtectedBranch(); err != nil { | |||||
return fmt.Errorf("LoadProtectedBranch: %v", err) | |||||
} | } | ||||
if pr.ProtectedBranch == nil { | if pr.ProtectedBranch == nil { | ||||
if err = pr.LoadProtectedBranch(); err != nil { | |||||
return fmt.Errorf("LoadProtectedBranch: %v", err) | |||||
} | |||||
if pr.ProtectedBranch == nil { | |||||
return nil | |||||
} | |||||
return nil | |||||
} | } | ||||
isPass, err := IsPullCommitStatusPass(pr) | isPass, err := IsPullCommitStatusPass(pr) |
// checkIfPRContentChanged checks if diff to target branch has changed by push | // checkIfPRContentChanged checks if diff to target branch has changed by push | ||||
// A commit can be considered to leave the PR untouched if the patch/diff with its merge base is unchanged | // A commit can be considered to leave the PR untouched if the patch/diff with its merge base is unchanged | ||||
func checkIfPRContentChanged(pr *models.PullRequest, oldCommitID, newCommitID string) (hasChanged bool, err error) { | func checkIfPRContentChanged(pr *models.PullRequest, oldCommitID, newCommitID string) (hasChanged bool, err error) { | ||||
if err = pr.GetHeadRepo(); err != nil { | |||||
return false, fmt.Errorf("GetHeadRepo: %v", err) | |||||
if err = pr.LoadHeadRepo(); err != nil { | |||||
return false, fmt.Errorf("LoadHeadRepo: %v", err) | |||||
} else if pr.HeadRepo == nil { | } else if pr.HeadRepo == nil { | ||||
// corrupt data assumed changed | // corrupt data assumed changed | ||||
return true, nil | return true, nil | ||||
} | } | ||||
if err = pr.GetBaseRepo(); err != nil { | |||||
return false, fmt.Errorf("GetBaseRepo: %v", err) | |||||
if err = pr.LoadBaseRepo(); err != nil { | |||||
return false, fmt.Errorf("LoadBaseRepo: %v", err) | |||||
} | } | ||||
headGitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath()) | headGitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath()) |
return nil, fmt.Errorf("GetPullRequestByIssueID: %v", err) | return nil, fmt.Errorf("GetPullRequestByIssueID: %v", err) | ||||
} | } | ||||
pr := issue.PullRequest | pr := issue.PullRequest | ||||
if err := pr.GetBaseRepo(); err != nil { | |||||
return nil, fmt.Errorf("GetHeadRepo: %v", err) | |||||
if err := pr.LoadBaseRepo(); err != nil { | |||||
return nil, fmt.Errorf("LoadHeadRepo: %v", err) | |||||
} | } | ||||
gitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath()) | gitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath()) | ||||
if err != nil { | if err != nil { |
) | ) | ||||
func createTemporaryRepo(pr *models.PullRequest) (string, error) { | func createTemporaryRepo(pr *models.PullRequest) (string, error) { | ||||
if err := pr.GetHeadRepo(); err != nil { | |||||
log.Error("GetHeadRepo: %v", err) | |||||
return "", fmt.Errorf("GetHeadRepo: %v", err) | |||||
if err := pr.LoadHeadRepo(); err != nil { | |||||
log.Error("LoadHeadRepo: %v", err) | |||||
return "", fmt.Errorf("LoadHeadRepo: %v", err) | |||||
} else if pr.HeadRepo == nil { | } else if pr.HeadRepo == nil { | ||||
log.Error("Pr %d HeadRepo %d does not exist", pr.ID, pr.HeadRepoID) | log.Error("Pr %d HeadRepo %d does not exist", pr.ID, pr.HeadRepoID) | ||||
return "", &models.ErrRepoNotExist{ | return "", &models.ErrRepoNotExist{ | ||||
ID: pr.HeadRepoID, | ID: pr.HeadRepoID, | ||||
} | } | ||||
} else if err := pr.GetBaseRepo(); err != nil { | |||||
log.Error("GetBaseRepo: %v", err) | |||||
return "", fmt.Errorf("GetBaseRepo: %v", err) | |||||
} else if err := pr.LoadBaseRepo(); err != nil { | |||||
log.Error("LoadBaseRepo: %v", err) | |||||
return "", fmt.Errorf("LoadBaseRepo: %v", err) | |||||
} else if pr.BaseRepo == nil { | } else if pr.BaseRepo == nil { | ||||
log.Error("Pr %d BaseRepo %d does not exist", pr.ID, pr.BaseRepoID) | log.Error("Pr %d BaseRepo %d does not exist", pr.ID, pr.BaseRepoID) | ||||
return "", &models.ErrRepoNotExist{ | return "", &models.ErrRepoNotExist{ |