2020-12-02 04:56:04 +00:00
// Copyright 2020 The Gitea Authors. All rights reserved.
2022-11-27 13:20:29 -05:00
// SPDX-License-Identifier: MIT
2020-12-02 04:56:04 +00:00
package doctor
import (
2022-01-19 23:26:57 +00:00
"context"
2020-12-02 04:56:04 +00:00
"fmt"
"strings"
2021-09-19 19:49:59 +08:00
"code.gitea.io/gitea/models/db"
2022-06-13 17:37:59 +08:00
issues_model "code.gitea.io/gitea/models/issues"
2021-12-10 09:27:50 +08:00
repo_model "code.gitea.io/gitea/models/repo"
2020-12-02 04:56:04 +00:00
"code.gitea.io/gitea/modules/git"
2025-09-15 23:33:12 -07:00
"code.gitea.io/gitea/modules/git/gitcmd"
2025-10-07 02:06:51 -07:00
"code.gitea.io/gitea/modules/gitrepo"
2020-12-02 04:56:04 +00:00
"code.gitea.io/gitea/modules/log"
2021-09-19 19:49:59 +08:00
2020-12-02 04:56:04 +00:00
"xorm.io/builder"
)
2022-06-13 17:37:59 +08:00
func iteratePRs ( ctx context . Context , repo * repo_model . Repository , each func ( * repo_model . Repository , * issues_model . PullRequest ) error ) error {
2021-09-19 19:49:59 +08:00
return db . Iterate (
2022-03-22 23:22:54 +08:00
ctx ,
2020-12-02 04:56:04 +00:00
builder . Eq { "base_repo_id" : repo . ID } ,
2022-10-31 23:51:14 +08:00
func ( ctx context . Context , bean * issues_model . PullRequest ) error {
return each ( repo , bean )
2020-12-02 04:56:04 +00:00
} ,
)
}
2022-01-19 23:26:57 +00:00
func checkPRMergeBase ( ctx context . Context , logger log . Logger , autofix bool ) error {
2020-12-02 04:56:04 +00:00
numRepos := 0
numPRs := 0
numPRsUpdated := 0
2022-03-22 23:22:54 +08:00
err := iterateRepositories ( ctx , func ( repo * repo_model . Repository ) error {
2020-12-02 04:56:04 +00:00
numRepos ++
2022-06-13 17:37:59 +08:00
return iteratePRs ( ctx , repo , func ( repo * repo_model . Repository , pr * issues_model . PullRequest ) error {
2020-12-02 04:56:04 +00:00
numPRs ++
pr . BaseRepo = repo
oldMergeBase := pr . MergeBase
if ! pr . HasMerged {
var err error
2026-01-19 07:10:33 +08:00
pr . MergeBase , _ , err = gitrepo . RunCmdString ( ctx , repo , gitcmd . NewCommand ( "merge-base" ) . AddDashesAndList ( pr . BaseBranch , pr . GetGitHeadRefName ( ) ) )
2020-12-02 04:56:04 +00:00
if err != nil {
var err2 error
2026-01-19 07:10:33 +08:00
pr . MergeBase , _ , err2 = gitrepo . RunCmdString ( ctx , repo , gitcmd . NewCommand ( "rev-parse" ) . AddDynamicArguments ( git . BranchPrefix + pr . BaseBranch ) )
2020-12-02 04:56:04 +00:00
if err2 != nil {
logger . Warn ( "Unable to get merge base for PR ID %d, #%d onto %s in %s/%s. Error: %v & %v" , pr . ID , pr . Index , pr . BaseBranch , pr . BaseRepo . OwnerName , pr . BaseRepo . Name , err , err2 )
return nil
}
}
} else {
2026-01-19 07:10:33 +08:00
parentsString , _ , err := gitrepo . RunCmdString ( ctx , repo , gitcmd . NewCommand ( "rev-list" , "--parents" , "-n" , "1" ) . AddDynamicArguments ( pr . MergedCommitID ) )
2020-12-02 04:56:04 +00:00
if err != nil {
logger . Warn ( "Unable to get parents for merged PR ID %d, #%d onto %s in %s/%s. Error: %v" , pr . ID , pr . Index , pr . BaseBranch , pr . BaseRepo . OwnerName , pr . BaseRepo . Name , err )
return nil
}
parents := strings . Split ( strings . TrimSpace ( parentsString ) , " " )
if len ( parents ) < 2 {
return nil
}
2022-10-23 22:44:45 +08:00
refs := append ( [ ] string { } , parents [ 1 : ] ... )
2025-07-16 21:33:33 +08:00
refs = append ( refs , pr . GetGitHeadRefName ( ) )
2025-09-15 23:33:12 -07:00
cmd := gitcmd . NewCommand ( "merge-base" ) . AddDashesAndList ( refs ... )
2026-01-19 07:10:33 +08:00
pr . MergeBase , _ , err = gitrepo . RunCmdString ( ctx , repo , cmd )
2020-12-02 04:56:04 +00:00
if err != nil {
logger . Warn ( "Unable to get merge base for merged PR ID %d, #%d onto %s in %s/%s. Error: %v" , pr . ID , pr . Index , pr . BaseBranch , pr . BaseRepo . OwnerName , pr . BaseRepo . Name , err )
return nil
}
}
pr . MergeBase = strings . TrimSpace ( pr . MergeBase )
if pr . MergeBase != oldMergeBase {
if autofix {
2023-10-11 06:24:07 +02:00
if err := pr . UpdateCols ( ctx , "merge_base" ) ; err != nil {
2020-12-02 04:56:04 +00:00
logger . Critical ( "Failed to update merge_base. ERROR: %v" , err )
2022-10-24 21:29:17 +02:00
return fmt . Errorf ( "Failed to update merge_base. ERROR: %w" , err )
2020-12-02 04:56:04 +00:00
}
} else {
logger . Info ( "#%d onto %s in %s/%s: MergeBase should be %s but is %s" , pr . Index , pr . BaseBranch , pr . BaseRepo . OwnerName , pr . BaseRepo . Name , oldMergeBase , pr . MergeBase )
}
numPRsUpdated ++
}
return nil
} )
} )
if autofix {
logger . Info ( "%d PR mergebases updated of %d PRs total in %d repos" , numPRsUpdated , numPRs , numRepos )
} else {
2022-05-31 18:49:40 +00:00
if numPRsUpdated == 0 {
logger . Info ( "All %d PRs in %d repos have a correct mergebase" , numPRs , numRepos )
} else if err == nil {
2020-12-02 04:56:04 +00:00
logger . Critical ( "%d PRs with incorrect mergebases of %d PRs total in %d repos" , numPRsUpdated , numPRs , numRepos )
return fmt . Errorf ( "%d PRs with incorrect mergebases of %d PRs total in %d repos" , numPRsUpdated , numPRs , numRepos )
2022-05-31 18:49:40 +00:00
} else {
logger . Warn ( "%d PRs with incorrect mergebases of %d PRs total in %d repos" , numPRsUpdated , numPRs , numRepos )
2020-12-02 04:56:04 +00:00
}
}
return err
}
func init ( ) {
Register ( & Check {
Title : "Recalculate merge bases" ,
Name : "recalculate-merge-bases" ,
IsDefault : false ,
Run : checkPRMergeBase ,
Priority : 7 ,
} )
}