* fix bug on pull requests when transfer head repository * add migration and fix lint * fix tests and add a cache check on LoadBaseRepotags/v1.10.0-rc2
index: 2 | index: 2 | ||||
head_repo_id: 1 | head_repo_id: 1 | ||||
base_repo_id: 1 | base_repo_id: 1 | ||||
head_user_name: user1 | |||||
head_branch: branch1 | head_branch: branch1 | ||||
base_branch: master | base_branch: master | ||||
merge_base: 1234567890abcdef | merge_base: 1234567890abcdef | ||||
index: 3 | index: 3 | ||||
head_repo_id: 1 | head_repo_id: 1 | ||||
base_repo_id: 1 | base_repo_id: 1 | ||||
head_user_name: user1 | |||||
head_branch: branch2 | head_branch: branch2 | ||||
base_branch: master | base_branch: master | ||||
merge_base: fedcba9876543210 | merge_base: fedcba9876543210 | ||||
index: 1 | index: 1 | ||||
head_repo_id: 11 | head_repo_id: 11 | ||||
base_repo_id: 10 | base_repo_id: 10 | ||||
head_user_name: user13 | |||||
head_branch: branch2 | head_branch: branch2 | ||||
base_branch: master | base_branch: master | ||||
merge_base: 0abcb056019adb83 | merge_base: 0abcb056019adb83 |
NewMigration("update migration repositories' service type", updateMigrationServiceTypes), | NewMigration("update migration repositories' service type", updateMigrationServiceTypes), | ||||
// v101 -> v102 | // v101 -> v102 | ||||
NewMigration("change length of some external login users columns", changeSomeColumnsLengthOfExternalLoginUser), | NewMigration("change length of some external login users columns", changeSomeColumnsLengthOfExternalLoginUser), | ||||
// v102 -> v103 | |||||
NewMigration("update migration repositories' service type", dropColumnHeadUserNameOnPullRequest), | |||||
} | } | ||||
// Migrate database to current version | // Migrate database to current version |
// Copyright 2019 The Gitea Authors. All rights reserved. | |||||
// Use of this source code is governed by a MIT-style | |||||
// license that can be found in the LICENSE file. | |||||
package migrations | |||||
import ( | |||||
"github.com/go-xorm/xorm" | |||||
) | |||||
func dropColumnHeadUserNameOnPullRequest(x *xorm.Engine) error { | |||||
sess := x.NewSession() | |||||
defer sess.Close() | |||||
return dropTableColumns(sess, "pull_request", "head_user_name") | |||||
} |
HeadRepo *Repository `xorm:"-"` | HeadRepo *Repository `xorm:"-"` | ||||
BaseRepoID int64 `xorm:"INDEX"` | BaseRepoID int64 `xorm:"INDEX"` | ||||
BaseRepo *Repository `xorm:"-"` | BaseRepo *Repository `xorm:"-"` | ||||
HeadUserName string | |||||
HeadBranch string | HeadBranch string | ||||
BaseBranch string | BaseBranch string | ||||
ProtectedBranch *ProtectedBranch `xorm:"-"` | ProtectedBranch *ProtectedBranch `xorm:"-"` | ||||
MergedUnix timeutil.TimeStamp `xorm:"updated INDEX"` | MergedUnix timeutil.TimeStamp `xorm:"updated INDEX"` | ||||
} | } | ||||
// MustHeadUserName returns the HeadRepo's username if failed return blank | |||||
func (pr *PullRequest) MustHeadUserName() string { | |||||
if err := pr.LoadHeadRepo(); err != nil { | |||||
log.Error("LoadHeadRepo: %v", err) | |||||
return "" | |||||
} | |||||
return pr.HeadRepo.MustOwnerName() | |||||
} | |||||
// Note: don't try to get Issue because will end up recursive querying. | // Note: don't try to get Issue because will end up recursive querying. | ||||
func (pr *PullRequest) loadAttributes(e Engine) (err error) { | func (pr *PullRequest) loadAttributes(e Engine) (err error) { | ||||
if pr.HasMerged && pr.Merger == nil { | if pr.HasMerged && pr.Merger == nil { | ||||
// LoadBaseRepo loads pull request base repository from database | // LoadBaseRepo loads pull request base repository from database | ||||
func (pr *PullRequest) LoadBaseRepo() error { | func (pr *PullRequest) LoadBaseRepo() error { | ||||
if pr.BaseRepo == nil { | if pr.BaseRepo == nil { | ||||
if pr.HeadRepoID == pr.BaseRepoID && pr.HeadRepo != nil { | |||||
pr.BaseRepo = pr.HeadRepo | |||||
return nil | |||||
} | |||||
var repo Repository | var repo Repository | ||||
if has, err := x.ID(pr.BaseRepoID).Get(&repo); err != nil { | if has, err := x.ID(pr.BaseRepoID).Get(&repo); err != nil { | ||||
return err | return err | ||||
return nil | return nil | ||||
} | } | ||||
// LoadHeadRepo loads pull request head repository from database | |||||
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.BaseRepoID} | |||||
} | |||||
pr.HeadRepo = &repo | |||||
} | |||||
return nil | |||||
} | |||||
// LoadIssue loads issue information from database | // LoadIssue loads issue information from database | ||||
func (pr *PullRequest) LoadIssue() (err error) { | func (pr *PullRequest) LoadIssue() (err error) { | ||||
return pr.loadIssue(x) | return pr.loadIssue(x) | ||||
return "" | return "" | ||||
} | } | ||||
} | } | ||||
return fmt.Sprintf("Merge branch '%s' of %s/%s into %s", pr.HeadBranch, pr.HeadUserName, pr.HeadRepo.Name, pr.BaseBranch) | |||||
return fmt.Sprintf("Merge branch '%s' of %s/%s into %s", pr.HeadBranch, pr.MustHeadUserName(), pr.HeadRepo.Name, pr.BaseBranch) | |||||
} | } | ||||
// GetDefaultSquashMessage returns default message used when squash and merging pull request | // GetDefaultSquashMessage returns default message used when squash and merging pull request | ||||
return nil | return nil | ||||
} | } | ||||
// ChangeUsernameInPullRequests changes the name of head_user_name | |||||
func ChangeUsernameInPullRequests(oldUserName, newUserName string) error { | |||||
pr := PullRequest{ | |||||
HeadUserName: strings.ToLower(newUserName), | |||||
} | |||||
_, err := x. | |||||
Cols("head_user_name"). | |||||
Where("head_user_name = ?", strings.ToLower(oldUserName)). | |||||
Update(pr) | |||||
return err | |||||
} | |||||
// checkAndUpdateStatus checks if pull request is possible to leaving checking status, | // checkAndUpdateStatus checks if pull request is possible to leaving checking status, | ||||
// and set to be either conflict or mergeable. | // and set to be either conflict or mergeable. | ||||
func (pr *PullRequest) checkAndUpdateStatus() { | func (pr *PullRequest) checkAndUpdateStatus() { |
// TODO TestAddTestPullRequestTask | // TODO TestAddTestPullRequestTask | ||||
func TestChangeUsernameInPullRequests(t *testing.T) { | |||||
assert.NoError(t, PrepareTestDatabase()) | |||||
const newUsername = "newusername" | |||||
assert.NoError(t, ChangeUsernameInPullRequests("user1", newUsername)) | |||||
prs := make([]*PullRequest, 0, 10) | |||||
assert.NoError(t, x.Where("head_user_name = ?", newUsername).Find(&prs)) | |||||
assert.Len(t, prs, 2) | |||||
for _, pr := range prs { | |||||
assert.Equal(t, newUsername, pr.HeadUserName) | |||||
} | |||||
CheckConsistencyFor(t, &PullRequest{}) | |||||
} | |||||
func TestPullRequest_IsWorkInProgress(t *testing.T) { | func TestPullRequest_IsWorkInProgress(t *testing.T) { | ||||
assert.NoError(t, PrepareTestDatabase()) | assert.NoError(t, PrepareTestDatabase()) | ||||
return ErrUserAlreadyExist{newUserName} | return ErrUserAlreadyExist{newUserName} | ||||
} | } | ||||
if err = ChangeUsernameInPullRequests(u.Name, newUserName); err != nil { | |||||
return fmt.Errorf("ChangeUsernameInPullRequests: %v", err) | |||||
} | |||||
// Do not fail if directory does not exist | // Do not fail if directory does not exist | ||||
if err = os.Rename(UserPath(u.Name), UserPath(newUserName)); err != nil && !os.IsNotExist(err) { | if err = os.Rename(UserPath(u.Name), UserPath(newUserName)); err != nil && !os.IsNotExist(err) { | ||||
return fmt.Errorf("Rename user directory: %v", err) | return fmt.Errorf("Rename user directory: %v", err) |
} | } | ||||
var pullRequest = models.PullRequest{ | var pullRequest = models.PullRequest{ | ||||
HeadRepoID: g.repo.ID, | |||||
HeadBranch: head, | |||||
HeadUserName: g.repoOwner, | |||||
BaseRepoID: g.repo.ID, | |||||
BaseBranch: pr.Base.Ref, | |||||
MergeBase: pr.Base.SHA, | |||||
Index: pr.Number, | |||||
HasMerged: pr.Merged, | |||||
HeadRepoID: g.repo.ID, | |||||
HeadBranch: head, | |||||
BaseRepoID: g.repo.ID, | |||||
BaseBranch: pr.Base.Ref, | |||||
MergeBase: pr.Base.SHA, | |||||
Index: pr.Number, | |||||
HasMerged: pr.Merged, | |||||
Issue: &issue, | Issue: &issue, | ||||
} | } |
) | ) | ||||
// Get repo/branch information | // Get repo/branch information | ||||
headUser, headRepo, headGitRepo, compareInfo, baseBranch, headBranch := parseCompareInfo(ctx, form) | |||||
_, headRepo, headGitRepo, compareInfo, baseBranch, headBranch := parseCompareInfo(ctx, form) | |||||
if ctx.Written() { | if ctx.Written() { | ||||
return | return | ||||
} | } | ||||
DeadlineUnix: deadlineUnix, | DeadlineUnix: deadlineUnix, | ||||
} | } | ||||
pr := &models.PullRequest{ | pr := &models.PullRequest{ | ||||
HeadRepoID: headRepo.ID, | |||||
BaseRepoID: repo.ID, | |||||
HeadUserName: headUser.Name, | |||||
HeadBranch: headBranch, | |||||
BaseBranch: baseBranch, | |||||
HeadRepo: headRepo, | |||||
BaseRepo: repo, | |||||
MergeBase: compareInfo.MergeBase, | |||||
Type: models.PullRequestGitea, | |||||
HeadRepoID: headRepo.ID, | |||||
BaseRepoID: repo.ID, | |||||
HeadBranch: headBranch, | |||||
BaseBranch: baseBranch, | |||||
HeadRepo: headRepo, | |||||
BaseRepo: repo, | |||||
MergeBase: compareInfo.MergeBase, | |||||
Type: models.PullRequestGitea, | |||||
} | } | ||||
// Get all assignee IDs | // Get all assignee IDs |
} | } | ||||
func setMergeTarget(ctx *context.Context, pull *models.PullRequest) { | func setMergeTarget(ctx *context.Context, pull *models.PullRequest) { | ||||
if ctx.Repo.Owner.Name == pull.HeadUserName { | |||||
if ctx.Repo.Owner.Name == pull.MustHeadUserName() { | |||||
ctx.Data["HeadTarget"] = pull.HeadBranch | ctx.Data["HeadTarget"] = pull.HeadBranch | ||||
} else if pull.HeadRepo == nil { | } else if pull.HeadRepo == nil { | ||||
ctx.Data["HeadTarget"] = pull.HeadUserName + ":" + pull.HeadBranch | |||||
ctx.Data["HeadTarget"] = pull.MustHeadUserName() + ":" + pull.HeadBranch | |||||
} else { | } else { | ||||
ctx.Data["HeadTarget"] = pull.HeadUserName + "/" + pull.HeadRepo.Name + ":" + pull.HeadBranch | |||||
ctx.Data["HeadTarget"] = pull.MustHeadUserName() + "/" + pull.HeadRepo.Name + ":" + pull.HeadBranch | |||||
} | } | ||||
ctx.Data["BaseTarget"] = pull.BaseBranch | ctx.Data["BaseTarget"] = pull.BaseBranch | ||||
} | } | ||||
ctx.NotFound("ViewPullCommits", nil) | ctx.NotFound("ViewPullCommits", nil) | ||||
return | return | ||||
} | } | ||||
ctx.Data["Username"] = pull.HeadUserName | |||||
ctx.Data["Username"] = pull.MustHeadUserName() | |||||
ctx.Data["Reponame"] = pull.HeadRepo.Name | ctx.Data["Reponame"] = pull.HeadRepo.Name | ||||
commits = prInfo.Commits | commits = prInfo.Commits | ||||
} | } | ||||
return | return | ||||
} | } | ||||
headRepoPath := models.RepoPath(pull.HeadUserName, pull.HeadRepo.Name) | |||||
headRepoPath := pull.HeadRepo.RepoPath() | |||||
headGitRepo, err := git.OpenRepository(headRepoPath) | headGitRepo, err := git.OpenRepository(headRepoPath) | ||||
if err != nil { | if err != nil { | ||||
endCommitID = headCommitID | endCommitID = headCommitID | ||||
gitRepo = headGitRepo | gitRepo = headGitRepo | ||||
headTarget = path.Join(pull.HeadUserName, pull.HeadRepo.Name) | |||||
ctx.Data["Username"] = pull.HeadUserName | |||||
headTarget = path.Join(pull.MustHeadUserName(), pull.HeadRepo.Name) | |||||
ctx.Data["Username"] = pull.MustHeadUserName() | |||||
ctx.Data["Reponame"] = pull.HeadRepo.Name | ctx.Data["Reponame"] = pull.HeadRepo.Name | ||||
} | } | ||||
Content: form.Content, | Content: form.Content, | ||||
} | } | ||||
pullRequest := &models.PullRequest{ | pullRequest := &models.PullRequest{ | ||||
HeadRepoID: headRepo.ID, | |||||
BaseRepoID: repo.ID, | |||||
HeadUserName: headUser.Name, | |||||
HeadBranch: headBranch, | |||||
BaseBranch: baseBranch, | |||||
HeadRepo: headRepo, | |||||
BaseRepo: repo, | |||||
MergeBase: prInfo.MergeBase, | |||||
Type: models.PullRequestGitea, | |||||
HeadRepoID: headRepo.ID, | |||||
BaseRepoID: repo.ID, | |||||
HeadBranch: headBranch, | |||||
BaseBranch: baseBranch, | |||||
HeadRepo: headRepo, | |||||
BaseRepo: repo, | |||||
MergeBase: prInfo.MergeBase, | |||||
Type: models.PullRequestGitea, | |||||
} | } | ||||
// FIXME: check error in the case two people send pull request at almost same time, give nice error prompt | // FIXME: check error in the case two people send pull request at almost same time, give nice error prompt | ||||
// instead of 500. | // instead of 500. |
} | } | ||||
}() | }() | ||||
headRepoPath := models.RepoPath(pr.HeadUserName, pr.HeadRepo.Name) | |||||
headRepoPath := pr.HeadRepo.RepoPath() | |||||
if err := git.InitRepository(tmpBasePath, false); err != nil { | if err := git.InitRepository(tmpBasePath, false); err != nil { | ||||
return fmt.Errorf("git init: %v", err) | return fmt.Errorf("git init: %v", err) | ||||
} | } | ||||
} | } | ||||
headUser, err := models.GetUserByName(pr.HeadUserName) | |||||
var headUser *models.User | |||||
err = pr.HeadRepo.GetOwner() | |||||
if err != nil { | if err != nil { | ||||
if !models.IsErrUserNotExist(err) { | if !models.IsErrUserNotExist(err) { | ||||
log.Error("Can't find user: %s for head repository - %v", pr.HeadUserName, err) | |||||
log.Error("Can't find user: %d for head repository - %v", pr.HeadRepo.OwnerID, err) | |||||
return err | return err | ||||
} | } | ||||
log.Error("Can't find user: %s for head repository - defaulting to doer: %s - %v", pr.HeadUserName, doer.Name, err) | |||||
log.Error("Can't find user: %d for head repository - defaulting to doer: %s - %v", pr.HeadRepo.OwnerID, doer.Name, err) | |||||
headUser = doer | headUser = doer | ||||
} else { | |||||
headUser = pr.HeadRepo.Owner | |||||
} | } | ||||
env := models.FullPushingEnvironment( | env := models.FullPushingEnvironment( |