mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 08:30:25 +08:00 
			
		
		
		
	Add option to update pull request by rebase (#16125)
				
					
				
			* add option to update pull request by `rebase` Signed-off-by: a1012112796 <1012112796@qq.com>
This commit is contained in:
		@@ -253,6 +253,8 @@ func rawMerge(pr *models.PullRequest, doer *models.User, mergeStyle models.Merge
 | 
			
		||||
		}
 | 
			
		||||
	case models.MergeStyleRebase:
 | 
			
		||||
		fallthrough
 | 
			
		||||
	case models.MergeStyleRebaseUpdate:
 | 
			
		||||
		fallthrough
 | 
			
		||||
	case models.MergeStyleRebaseMerge:
 | 
			
		||||
		// Checkout head branch
 | 
			
		||||
		if err := git.NewCommand("checkout", "-b", stagingBranch, trackingBranch).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil {
 | 
			
		||||
@@ -305,6 +307,11 @@ func rawMerge(pr *models.PullRequest, doer *models.User, mergeStyle models.Merge
 | 
			
		||||
		outbuf.Reset()
 | 
			
		||||
		errbuf.Reset()
 | 
			
		||||
 | 
			
		||||
		// not need merge, just update by rebase. so skip
 | 
			
		||||
		if mergeStyle == models.MergeStyleRebaseUpdate {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Checkout base branch again
 | 
			
		||||
		if err := git.NewCommand("checkout", baseBranch).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil {
 | 
			
		||||
			log.Error("git checkout base prior to merge post staging rebase [%s:%s -> %s:%s]: %v\n%s\n%s", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), pr.BaseBranch, err, outbuf.String(), errbuf.String())
 | 
			
		||||
@@ -410,8 +417,16 @@ func rawMerge(pr *models.PullRequest, doer *models.User, mergeStyle models.Merge
 | 
			
		||||
		pr.ID,
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	var pushCmd *git.Command
 | 
			
		||||
	if mergeStyle == models.MergeStyleRebaseUpdate {
 | 
			
		||||
		// force push the rebase result to head brach
 | 
			
		||||
		pushCmd = git.NewCommand("push", "-f", "head_repo", stagingBranch+":refs/heads/"+pr.HeadBranch)
 | 
			
		||||
	} else {
 | 
			
		||||
		pushCmd = git.NewCommand("push", "origin", baseBranch+":refs/heads/"+pr.BaseBranch)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Push back to upstream.
 | 
			
		||||
	if err := git.NewCommand("push", "origin", baseBranch+":refs/heads/"+pr.BaseBranch).RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, &outbuf, &errbuf); err != nil {
 | 
			
		||||
	if err := pushCmd.RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, &outbuf, &errbuf); err != nil {
 | 
			
		||||
		if strings.Contains(errbuf.String(), "non-fast-forward") {
 | 
			
		||||
			return "", &git.ErrPushOutOfDate{
 | 
			
		||||
				StdOut: outbuf.String(),
 | 
			
		||||
 
 | 
			
		||||
@@ -13,13 +13,24 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Update updates pull request with base branch.
 | 
			
		||||
func Update(pull *models.PullRequest, doer *models.User, message string) error {
 | 
			
		||||
	//use merge functions but switch repo's and branch's
 | 
			
		||||
	pr := &models.PullRequest{
 | 
			
		||||
		HeadRepoID: pull.BaseRepoID,
 | 
			
		||||
		BaseRepoID: pull.HeadRepoID,
 | 
			
		||||
		HeadBranch: pull.BaseBranch,
 | 
			
		||||
		BaseBranch: pull.HeadBranch,
 | 
			
		||||
func Update(pull *models.PullRequest, doer *models.User, message string, rebase bool) error {
 | 
			
		||||
	var (
 | 
			
		||||
		pr    *models.PullRequest
 | 
			
		||||
		style models.MergeStyle
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	if rebase {
 | 
			
		||||
		pr = pull
 | 
			
		||||
		style = models.MergeStyleRebaseUpdate
 | 
			
		||||
	} else {
 | 
			
		||||
		//use merge functions but switch repo's and branch's
 | 
			
		||||
		pr = &models.PullRequest{
 | 
			
		||||
			HeadRepoID: pull.BaseRepoID,
 | 
			
		||||
			BaseRepoID: pull.HeadRepoID,
 | 
			
		||||
			HeadBranch: pull.BaseBranch,
 | 
			
		||||
			BaseBranch: pull.HeadBranch,
 | 
			
		||||
		}
 | 
			
		||||
		style = models.MergeStyleMerge
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if pull.Flow == models.PullRequestFlowAGit {
 | 
			
		||||
@@ -42,9 +53,13 @@ func Update(pull *models.PullRequest, doer *models.User, message string) error {
 | 
			
		||||
		return fmt.Errorf("HeadBranch of PR %d is up to date", pull.Index)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err = rawMerge(pr, doer, models.MergeStyleMerge, message)
 | 
			
		||||
	_, err = rawMerge(pr, doer, style, message)
 | 
			
		||||
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if rebase {
 | 
			
		||||
			go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false, "", "")
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		go AddTestPullRequestTask(doer, pr.HeadRepo.ID, pr.HeadBranch, false, "", "")
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
@@ -52,17 +67,17 @@ func Update(pull *models.PullRequest, doer *models.User, message string) error {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsUserAllowedToUpdate check if user is allowed to update PR with given permissions and branch protections
 | 
			
		||||
func IsUserAllowedToUpdate(pull *models.PullRequest, user *models.User) (bool, error) {
 | 
			
		||||
func IsUserAllowedToUpdate(pull *models.PullRequest, user *models.User) (mergeAllowed, rebaseAllowed bool, err error) {
 | 
			
		||||
	if pull.Flow == models.PullRequestFlowAGit {
 | 
			
		||||
		return false, nil
 | 
			
		||||
		return false, false, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if user == nil {
 | 
			
		||||
		return false, nil
 | 
			
		||||
		return false, false, nil
 | 
			
		||||
	}
 | 
			
		||||
	headRepoPerm, err := models.GetUserRepoPermission(pull.HeadRepo, user)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
		return false, false, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pr := &models.PullRequest{
 | 
			
		||||
@@ -74,15 +89,25 @@ func IsUserAllowedToUpdate(pull *models.PullRequest, user *models.User) (bool, e
 | 
			
		||||
 | 
			
		||||
	err = pr.LoadProtectedBranch()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
		return false, false, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// can't do rebase on protected branch because need force push
 | 
			
		||||
	if pr.ProtectedBranch == nil {
 | 
			
		||||
		rebaseAllowed = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Update function need push permission
 | 
			
		||||
	if pr.ProtectedBranch != nil && !pr.ProtectedBranch.CanUserPush(user.ID) {
 | 
			
		||||
		return false, nil
 | 
			
		||||
		return false, false, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return IsUserAllowedToMerge(pr, headRepoPerm, user)
 | 
			
		||||
	mergeAllowed, err = IsUserAllowedToMerge(pr, headRepoPerm, user)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, false, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return mergeAllowed, rebaseAllowed, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetDiverging determines how many commits a PR is ahead or behind the PR base branch
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user