2020-01-17 07:03:40 +01:00
// Copyright 2020 The Gitea Authors. All rights reserved.
2022-11-27 13:20:29 -05:00
// SPDX-License-Identifier: MIT
2020-01-17 07:03:40 +01:00
package pull
import (
2022-01-19 23:26:57 +00:00
"context"
2020-01-17 07:03:40 +01:00
"fmt"
2023-01-16 16:00:22 +08:00
git_model "code.gitea.io/gitea/models/git"
2022-06-13 17:37:59 +08:00
issues_model "code.gitea.io/gitea/models/issues"
2022-05-11 18:09:36 +08:00
access_model "code.gitea.io/gitea/models/perm/access"
2021-12-10 09:27:50 +08:00
repo_model "code.gitea.io/gitea/models/repo"
2022-03-04 09:30:49 +01:00
"code.gitea.io/gitea/models/unit"
2021-11-24 17:49:20 +08:00
user_model "code.gitea.io/gitea/models/user"
2020-01-17 07:03:40 +01:00
"code.gitea.io/gitea/modules/git"
2024-09-06 18:12:41 +08:00
"code.gitea.io/gitea/modules/globallock"
2020-01-17 07:03:40 +01:00
"code.gitea.io/gitea/modules/log"
2024-05-07 15:36:48 +08:00
"code.gitea.io/gitea/modules/repository"
2020-01-17 07:03:40 +01:00
)
// Update updates pull request with base branch.
2023-03-07 20:07:35 +00:00
func Update ( ctx context . Context , pr * issues_model . PullRequest , doer * user_model . User , message string , rebase bool ) error {
if pr . Flow == issues_model . PullRequestFlowAGit {
// TODO: update of agit flow pull request's head branch is unsupported
return fmt . Errorf ( "update of agit flow pull request's head branch is unsupported" )
}
2021-08-31 22:03:45 +08:00
2024-09-06 18:12:41 +08:00
releaser , err := globallock . Lock ( ctx , getPullWorkingLockKey ( pr . ID ) )
if err != nil {
log . Error ( "lock.Lock(): %v" , err )
return fmt . Errorf ( "lock.Lock: %w" , err )
}
defer releaser ( )
2022-05-04 18:06:23 +02:00
2023-03-07 20:07:35 +00:00
diffCount , err := GetDiverging ( ctx , pr )
if err != nil {
return err
} else if diffCount . Behind == 0 {
return fmt . Errorf ( "HeadBranch of PR %d is up to date" , pr . Index )
2020-01-17 07:03:40 +01:00
}
2023-03-07 20:07:35 +00:00
if rebase {
defer func ( ) {
go AddTestPullRequestTask ( doer , pr . BaseRepo . ID , pr . BaseBranch , false , "" , "" )
} ( )
2024-04-29 04:47:56 -04:00
return updateHeadByRebaseOnToBase ( ctx , pr , doer )
2021-07-28 17:42:56 +08:00
}
2023-03-07 20:07:35 +00:00
if err := pr . LoadBaseRepo ( ctx ) ; err != nil {
log . Error ( "unable to load BaseRepo for %-v during update-by-merge: %v" , pr , err )
return fmt . Errorf ( "unable to load BaseRepo for PR[%d] during update-by-merge: %w" , pr . ID , err )
}
2022-11-19 09:12:33 +01:00
if err := pr . LoadHeadRepo ( ctx ) ; err != nil {
2023-03-07 20:07:35 +00:00
log . Error ( "unable to load HeadRepo for PR %-v during update-by-merge: %v" , pr , err )
return fmt . Errorf ( "unable to load HeadRepo for PR[%d] during update-by-merge: %w" , pr . ID , err )
}
if pr . HeadRepo == nil {
// LoadHeadRepo will swallow ErrRepoNotExist so if pr.HeadRepo is still nil recreate the error
err := repo_model . ErrRepoNotExist {
ID : pr . HeadRepoID ,
}
log . Error ( "unable to load HeadRepo for PR %-v during update-by-merge: %v" , pr , err )
return fmt . Errorf ( "unable to load HeadRepo for PR[%d] during update-by-merge: %w" , pr . ID , err )
2020-01-17 07:03:40 +01:00
}
2024-12-07 05:10:35 +08:00
// TODO: FakePR: it is somewhat hacky, but it is the only way to "merge" at the moment
// ideally in the future the "merge" functions should be refactored to decouple from the PullRequest
// now use a fake reverse PR to switch head&base repos/branches
2023-03-07 20:07:35 +00:00
reversePR := & issues_model . PullRequest {
ID : pr . ID ,
HeadRepoID : pr . BaseRepoID ,
HeadRepo : pr . BaseRepo ,
HeadBranch : pr . BaseBranch ,
BaseRepoID : pr . HeadRepoID ,
BaseRepo : pr . HeadRepo ,
BaseBranch : pr . HeadBranch ,
2020-01-17 07:03:40 +01:00
}
2024-05-07 15:36:48 +08:00
_ , err = doMergeAndPush ( ctx , reversePR , doer , repo_model . MergeStyleMerge , "" , message , repository . PushTriggerPRUpdateWithBase )
2020-02-09 23:09:31 +00:00
2020-01-17 07:03:40 +01:00
defer func ( ) {
2023-03-07 20:07:35 +00:00
go AddTestPullRequestTask ( doer , reversePR . HeadRepo . ID , reversePR . HeadBranch , false , "" , "" )
2020-01-17 07:03:40 +01:00
} ( )
2020-02-09 23:09:31 +00:00
return err
2020-01-17 07:03:40 +01:00
}
// IsUserAllowedToUpdate check if user is allowed to update PR with given permissions and branch protections
2022-06-13 17:37:59 +08:00
func IsUserAllowedToUpdate ( ctx context . Context , pull * issues_model . PullRequest , user * user_model . User ) ( mergeAllowed , rebaseAllowed bool , err error ) {
if pull . Flow == issues_model . PullRequestFlowAGit {
2021-08-31 22:03:45 +08:00
return false , false , nil
2021-07-28 17:42:56 +08:00
}
2021-03-04 19:27:54 +01:00
if user == nil {
2021-08-31 22:03:45 +08:00
return false , false , nil
2021-03-04 19:27:54 +01:00
}
2022-05-11 18:09:36 +08:00
headRepoPerm , err := access_model . GetUserRepoPermission ( ctx , pull . HeadRepo , user )
2020-01-17 07:03:40 +01:00
if err != nil {
2022-08-18 11:31:15 +01:00
if repo_model . IsErrUnitTypeNotExist ( err ) {
return false , false , nil
}
2021-08-31 22:03:45 +08:00
return false , false , err
2020-01-17 07:03:40 +01:00
}
2023-01-16 16:00:22 +08:00
if err := pull . LoadBaseRepo ( ctx ) ; err != nil {
return false , false , err
}
2022-06-13 17:37:59 +08:00
pr := & issues_model . PullRequest {
2020-01-17 07:03:40 +01:00
HeadRepoID : pull . BaseRepoID ,
2023-01-16 16:00:22 +08:00
HeadRepo : pull . BaseRepo ,
2020-01-17 07:03:40 +01:00
BaseRepoID : pull . HeadRepoID ,
2023-01-16 16:00:22 +08:00
BaseRepo : pull . HeadRepo ,
2020-01-17 07:03:40 +01:00
HeadBranch : pull . BaseBranch ,
BaseBranch : pull . HeadBranch ,
}
2020-05-17 02:48:30 +02:00
2023-02-09 18:08:42 +01:00
pb , err := git_model . GetFirstMatchProtectedBranchRule ( ctx , pr . BaseRepoID , pr . BaseBranch )
2020-05-17 02:48:30 +02:00
if err != nil {
2021-08-31 22:03:45 +08:00
return false , false , err
}
2024-07-06 04:21:56 +10:00
if err := pr . LoadBaseRepo ( ctx ) ; err != nil {
return false , false , err
}
prUnit , err := pr . BaseRepo . GetUnit ( ctx , unit . TypePullRequests )
if err != nil {
if repo_model . IsErrUnitTypeNotExist ( err ) {
return false , false , nil
2022-03-04 09:30:49 +01:00
}
2024-07-06 04:21:56 +10:00
log . Error ( "pr.BaseRepo.GetUnit(unit.TypePullRequests): %v" , err )
return false , false , err
2020-05-17 02:48:30 +02:00
}
2024-07-06 04:21:56 +10:00
rebaseAllowed = prUnit . PullRequestsConfig ( ) . AllowRebaseUpdate
// If branch protected, disable rebase unless user is whitelisted to force push (which extends regular push)
2023-01-16 16:00:22 +08:00
if pb != nil {
pb . Repo = pull . BaseRepo
2024-07-06 04:21:56 +10:00
if ! pb . CanUserForcePush ( ctx , user ) {
rebaseAllowed = false
2023-01-16 16:00:22 +08:00
}
2021-08-31 22:03:45 +08:00
}
2022-05-11 18:09:36 +08:00
baseRepoPerm , err := access_model . GetUserRepoPermission ( ctx , pull . BaseRepo , user )
2022-04-28 17:45:33 +02:00
if err != nil {
return false , false , err
}
2022-05-03 21:46:28 +02:00
mergeAllowed , err = IsUserAllowedToMerge ( ctx , pr , headRepoPerm , user )
2021-08-31 22:03:45 +08:00
if err != nil {
return false , false , err
2020-05-17 02:48:30 +02:00
}
2022-04-28 17:45:33 +02:00
if pull . AllowMaintainerEdit {
2022-05-03 21:46:28 +02:00
mergeAllowedMaintainer , err := IsUserAllowedToMerge ( ctx , pr , baseRepoPerm , user )
2022-04-28 17:45:33 +02:00
if err != nil {
return false , false , err
}
mergeAllowed = mergeAllowed || mergeAllowedMaintainer
}
2021-08-31 22:03:45 +08:00
return mergeAllowed , rebaseAllowed , nil
2020-01-17 07:03:40 +01:00
}
// GetDiverging determines how many commits a PR is ahead or behind the PR base branch
2022-06-13 17:37:59 +08:00
func GetDiverging ( ctx context . Context , pr * issues_model . PullRequest ) ( * git . DivergeObject , error ) {
2023-03-07 20:07:35 +00:00
log . Trace ( "GetDiverging[%-v]: compare commits" , pr )
prCtx , cancel , err := createTemporaryRepoForPR ( ctx , pr )
2020-01-17 07:03:40 +01:00
if err != nil {
2023-06-29 18:03:20 +08:00
if ! git_model . IsErrBranchNotExist ( err ) {
2023-03-07 20:07:35 +00:00
log . Error ( "CreateTemporaryRepoForPR %-v: %v" , pr , err )
2021-07-13 01:26:25 +02:00
}
2020-04-01 21:03:08 +02:00
return nil , err
2020-01-17 07:03:40 +01:00
}
2023-03-07 20:07:35 +00:00
defer cancel ( )
2020-01-17 07:03:40 +01:00
2023-03-07 20:07:35 +00:00
diff , err := git . GetDivergingCommits ( ctx , prCtx . tmpBasePath , baseBranch , trackingBranch )
2020-04-01 21:03:08 +02:00
return & diff , err
2020-01-17 07:03:40 +01:00
}