[v12.0/forgejo] fix: prevent pull requests from being merged multiple times (#8862)

Backport of https://codeberg.org/forgejo/forgejo/pulls/8842

Contains a partial cherry-pick of 184e068f37, for the parts the PR depends on. The whole commit is way too involved to cherry-pick as a whole.

Co-authored-by: Danko Aleksejevs <danko@very.lv>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8862
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: BtbN <btbn@btbn.de>
Co-committed-by: BtbN <btbn@btbn.de>
This commit is contained in:
BtbN 2025-08-11 23:08:46 +02:00 committed by Earl Warren
commit 53c4c6bda8
6 changed files with 92 additions and 4 deletions

View file

@ -208,7 +208,30 @@ func AddCommitMessageTrailer(message, tailerKey, tailerValue string) string {
// Merge merges pull request to base repository.
// Caller should check PR is ready to be merged (review and status checks)
func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, baseGitRepo *git.Repository, mergeStyle repo_model.MergeStyle, expectedHeadCommitID, message string, wasAutoMerged bool) error {
if err := pr.LoadBaseRepo(ctx); err != nil {
pullWorkingPool.CheckIn(fmt.Sprint(pr.ID))
defer pullWorkingPool.CheckOut(fmt.Sprint(pr.ID))
pr, err := issues_model.GetPullRequestByID(ctx, pr.ID)
if err != nil {
log.Error("Unable to load pull request itself: %v", err)
return fmt.Errorf("unable to load pull request itself: %w", err)
}
if pr.HasMerged {
return models.ErrPullRequestHasMerged{
ID: pr.ID,
IssueID: pr.IssueID,
HeadRepoID: pr.HeadRepoID,
BaseRepoID: pr.BaseRepoID,
HeadBranch: pr.HeadBranch,
BaseBranch: pr.BaseBranch,
}
}
if err := pr.LoadIssue(ctx); err != nil {
log.Error("Unable to load issue: %v", err)
return fmt.Errorf("unable to load issue: %w", err)
} else if err := pr.LoadBaseRepo(ctx); err != nil {
log.Error("Unable to load base repo: %v", err)
return fmt.Errorf("unable to load base repo: %w", err)
} else if err := pr.LoadHeadRepo(ctx); err != nil {
@ -216,9 +239,6 @@ func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.U
return fmt.Errorf("unable to load head repo: %w", err)
}
pullWorkingPool.CheckIn(fmt.Sprint(pr.ID))
defer pullWorkingPool.CheckOut(fmt.Sprint(pr.ID))
prUnit, err := pr.BaseRepo.GetUnit(ctx, unit.TypePullRequests)
if err != nil {
log.Error("pr.BaseRepo.GetUnit(unit.TypePullRequests): %v", err)