@ -17,6 +17,7 @@ import (
"time"
"time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
user_model "code.gitea.io/gitea/models/user"
@ -34,11 +35,11 @@ import (
// Merge merges pull request to base repository.
// Merge merges pull request to base repository.
// 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 ( ctx context . Context , pr * models . PullRequest , doer * user_model . User , baseGitRepo * git . Repository , mergeStyle repo_model . MergeStyle , expectedHeadCommitID , message string ) ( err error ) {
func Merge ( pr * models . PullRequest , doer * user_model . User , baseGitRepo * git . Repository , mergeStyle repo_model . MergeStyle , expectedHeadCommitID , message string ) error {
if err = pr . LoadHeadRepoCtx ( ctx ) ; err != nil {
if err : = pr . LoadHeadRepo ( ) ; err != nil {
log . Error ( "LoadHeadRepo: %v" , err )
log . Error ( "LoadHeadRepo: %v" , err )
return fmt . Errorf ( "LoadHeadRepo: %v" , err )
return fmt . Errorf ( "LoadHeadRepo: %v" , err )
} else if err = pr . LoadBaseRepoCtx ( ctx ) ; err != nil {
} else if err : = pr . LoadBaseRepo ( ) ; err != nil {
log . Error ( "LoadBaseRepo: %v" , err )
log . Error ( "LoadBaseRepo: %v" , err )
return fmt . Errorf ( "LoadBaseRepo: %v" , err )
return fmt . Errorf ( "LoadBaseRepo: %v" , err )
}
}
@ -59,7 +60,9 @@ func Merge(ctx context.Context, pr *models.PullRequest, doer *user_model.User, b
go AddTestPullRequestTask ( doer , pr . BaseRepo . ID , pr . BaseBranch , false , "" , "" )
go AddTestPullRequestTask ( doer , pr . BaseRepo . ID , pr . BaseBranch , false , "" , "" )
} ( )
} ( )
pr . MergedCommitID , err = rawMerge ( ctx , pr , doer , mergeStyle , expectedHeadCommitID , message )
// TODO: make it able to do this in a database session
mergeCtx := context . Background ( )
pr . MergedCommitID , err = rawMerge ( mergeCtx , pr , doer , mergeStyle , expectedHeadCommitID , message )
if err != nil {
if err != nil {
return err
return err
}
}
@ -68,18 +71,18 @@ func Merge(ctx context.Context, pr *models.PullRequest, doer *user_model.User, b
pr . Merger = doer
pr . Merger = doer
pr . MergerID = doer . ID
pr . MergerID = doer . ID
if _ , err := pr . SetMerged ( ) ; err != nil {
if _ , err := pr . SetMerged ( db . DefaultContext ) ; err != nil {
log . Error ( "setMerged [%d]: %v" , pr . ID , err )
log . Error ( "setMerged [%d]: %v" , pr . ID , err )
}
}
if err := pr . LoadIssue ( ) ; err != nil {
if err := pr . LoadIssueCtx ( db . DefaultContext ) ; err != nil {
log . Error ( "loadIssue [%d]: %v" , pr . ID , err )
log . Error ( "loadIssue [%d]: %v" , pr . ID , err )
}
}
if err := pr . Issue . LoadRepo ( c tx) ; err != nil {
if err := pr . Issue . LoadRepo ( db . Defaul tConte xt ) ; err != nil {
log . Error ( "loadRepo for issue [%d]: %v" , pr . ID , err )
log . Error ( "loadRepo for issue [%d]: %v" , pr . ID , err )
}
}
if err := pr . Issue . Repo . GetOwner ( c tx) ; err != nil {
if err := pr . Issue . Repo . GetOwner ( db . Defaul tConte xt ) ; err != nil {
log . Error ( "GetOwner for issue repo [%d]: %v" , pr . ID , err )
log . Error ( "GetOwner for issue repo [%d]: %v" , pr . ID , err )
}
}
@ -89,17 +92,17 @@ func Merge(ctx context.Context, pr *models.PullRequest, doer *user_model.User, b
cache . Remove ( pr . Issue . Repo . GetCommitsCountCacheKey ( pr . BaseBranch , true ) )
cache . Remove ( pr . Issue . Repo . GetCommitsCountCacheKey ( pr . BaseBranch , true ) )
// Resolve cross references
// Resolve cross references
refs , err := pr . ResolveCrossReferences ( )
refs , err := pr . ResolveCrossReferences ( db . DefaultContext )
if err != nil {
if err != nil {
log . Error ( "ResolveCrossReferences: %v" , err )
log . Error ( "ResolveCrossReferences: %v" , err )
return nil
return nil
}
}
for _ , ref := range refs {
for _ , ref := range refs {
if err = ref . LoadIssue ( ) ; err != nil {
if err = ref . LoadIssueCtx ( db . DefaultContext ) ; err != nil {
return err
return err
}
}
if err = ref . Issue . LoadRepo ( c tx) ; err != nil {
if err = ref . Issue . LoadRepo ( db . Defaul tConte xt ) ; err != nil {
return err
return err
}
}
close := ref . RefAction == references . XRefActionCloses
close := ref . RefAction == references . XRefActionCloses
@ -112,7 +115,6 @@ func Merge(ctx context.Context, pr *models.PullRequest, doer *user_model.User, b
}
}
}
}
}
}
return nil
return nil
}
}
@ -504,6 +506,8 @@ func rawMerge(ctx context.Context, pr *models.PullRequest, doer *user_model.User
}
}
// Push back to upstream.
// Push back to upstream.
// TODO: this cause an api call to "/api/internal/hook/post-receive/...",
// that prevents us from doint the whole merge in one db transaction
if err := pushCmd . Run ( & git . RunOpts {
if err := pushCmd . Run ( & git . RunOpts {
Env : env ,
Env : env ,
Dir : tmpBasePath ,
Dir : tmpBasePath ,
@ -645,17 +649,17 @@ func getDiffTree(ctx context.Context, repoPath, baseBranch, headBranch string) (
}
}
// IsUserAllowedToMerge check if user is allowed to merge PR with given permissions and branch protections
// IsUserAllowedToMerge check if user is allowed to merge PR with given permissions and branch protections
func IsUserAllowedToMerge ( pr * models . PullRequest , p models . Permission , user * user_model . User ) ( bool , error ) {
func IsUserAllowedToMerge ( ctx context . Context , pr * models . PullRequest , p models . Permission , user * user_model . User ) ( bool , error ) {
if user == nil {
if user == nil {
return false , nil
return false , nil
}
}
err := pr . LoadProtectedBranch ( )
err := pr . LoadProtectedBranchCtx ( ctx )
if err != nil {
if err != nil {
return false , err
return false , err
}
}
if ( p . CanWrite ( unit . TypeCode ) && pr . ProtectedBranch == nil ) || ( pr . ProtectedBranch != nil && models . IsUserMergeWhitelisted ( pr . ProtectedBranch , user . ID , p ) ) {
if ( p . CanWrite ( unit . TypeCode ) && pr . ProtectedBranch == nil ) || ( pr . ProtectedBranch != nil && models . IsUserMergeWhitelisted ( ctx , pr . ProtectedBranch , user . ID , p ) ) {
return true , nil
return true , nil
}
}
@ -668,7 +672,7 @@ func CheckPullBranchProtections(ctx context.Context, pr *models.PullRequest, ski
return fmt . Errorf ( "LoadBaseRepo: %v" , err )
return fmt . Errorf ( "LoadBaseRepo: %v" , err )
}
}
if err = pr . LoadProtectedBranch ( ) ; err != nil {
if err = pr . LoadProtectedBranchCtx ( ctx ) ; err != nil {
return fmt . Errorf ( "LoadProtectedBranch: %v" , err )
return fmt . Errorf ( "LoadProtectedBranch: %v" , err )
}
}
if pr . ProtectedBranch == nil {
if pr . ProtectedBranch == nil {
@ -685,17 +689,17 @@ func CheckPullBranchProtections(ctx context.Context, pr *models.PullRequest, ski
}
}
}
}
if ! pr . ProtectedBranch . HasEnoughApprovals ( pr ) {
if ! pr . ProtectedBranch . HasEnoughApprovals ( ctx , pr ) {
return models . ErrDisallowedToMerge {
return models . ErrDisallowedToMerge {
Reason : "Does not have enough approvals" ,
Reason : "Does not have enough approvals" ,
}
}
}
}
if pr . ProtectedBranch . MergeBlockedByRejectedReview ( pr ) {
if pr . ProtectedBranch . MergeBlockedByRejectedReview ( ctx , pr ) {
return models . ErrDisallowedToMerge {
return models . ErrDisallowedToMerge {
Reason : "There are requested changes" ,
Reason : "There are requested changes" ,
}
}
}
}
if pr . ProtectedBranch . MergeBlockedByOfficialReviewRequests ( pr ) {
if pr . ProtectedBranch . MergeBlockedByOfficialReviewRequests ( ctx , pr ) {
return models . ErrDisallowedToMerge {
return models . ErrDisallowedToMerge {
Reason : "There are official review requests" ,
Reason : "There are official review requests" ,
}
}
@ -721,10 +725,11 @@ func CheckPullBranchProtections(ctx context.Context, pr *models.PullRequest, ski
}
}
// MergedManually mark pr as merged manually
// MergedManually mark pr as merged manually
func MergedManually ( pr * models . PullRequest , doer * user_model . User , baseGitRepo * git . Repository , commitID string ) ( err error ) {
func MergedManually ( pr * models . PullRequest , doer * user_model . User , baseGitRepo * git . Repository , commitID string ) error {
prUnit , err := pr . BaseRepo . GetUnit ( unit . TypePullRequests )
if err := db . WithTx ( func ( ctx context . Context ) error {
prUnit , err := pr . BaseRepo . GetUnitCtx ( ctx , unit . TypePullRequests )
if err != nil {
if err != nil {
return
return err
}
}
prConfig := prUnit . PullRequestsConfig ( )
prConfig := prUnit . PullRequestsConfig ( )
@ -742,12 +747,13 @@ func MergedManually(pr *models.PullRequest, doer *user_model.User, baseGitRepo *
if git . IsErrNotExist ( err ) {
if git . IsErrNotExist ( err ) {
return fmt . Errorf ( "Wrong commit ID" )
return fmt . Errorf ( "Wrong commit ID" )
}
}
return
return err
}
}
commitID = commit . ID . String ( )
ok , err := baseGitRepo . IsCommitInBranch ( commitID , pr . BaseBranch )
ok , err := baseGitRepo . IsCommitInBranch ( commitID , pr . BaseBranch )
if err != nil {
if err != nil {
return
return err
}
}
if ! ok {
if ! ok {
return fmt . Errorf ( "Wrong commit ID" )
return fmt . Errorf ( "Wrong commit ID" )
@ -760,13 +766,17 @@ func MergedManually(pr *models.PullRequest, doer *user_model.User, baseGitRepo *
pr . MergerID = doer . ID
pr . MergerID = doer . ID
merged := false
merged := false
if merged , err = pr . SetMerged ( ) ; err != nil {
if merged , err = pr . SetMerged ( ctx ) ; err != nil {
return
return err
} else if ! merged {
} else if ! merged {
return fmt . Errorf ( "SetMerged failed" )
return fmt . Errorf ( "SetMerged failed" )
}
}
return nil
} ) ; err != nil {
return err
}
notification . NotifyMergePullRequest ( pr , doer )
notification . NotifyMergePullRequest ( pr , doer )
log . Info ( "manuallyMerged[%d]: Marked as manually merged into %s/%s by commit id: %s" , pr . ID , pr . BaseRepo . Name , pr . BaseBranch , commit . ID . String ( ) )
log . Info ( "manuallyMerged[%d]: Marked as manually merged into %s/%s by commit id: %s" , pr . ID , pr . BaseRepo . Name , pr . BaseBranch , commitID )
return nil
return nil
}
}