Skip to content

Commit 28a95e8

Browse files
committed
Retarget branches to next branch in chain, rather than defaultBranch
Previously, RETARGET_CHILDREN_ON_MERGE incorrectly retargeted all following PR's to the defaultBranch when merging a PR in the middle of a chain. Fixes #35138
1 parent f04b9aa commit 28a95e8

File tree

3 files changed

+77
-36
lines changed

3 files changed

+77
-36
lines changed

services/pull/pull.go

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -643,10 +643,10 @@ func UpdateRef(ctx context.Context, pr *issues_model.PullRequest) (err error) {
643643
return err
644644
}
645645

646-
// retargetBranchPulls change target branch for all pull requests whose base branch is the branch
647-
// Both branch and targetBranch must be in the same repo (for security reasons)
648-
func retargetBranchPulls(ctx context.Context, doer *user_model.User, repoID int64, branch, targetBranch string) error {
649-
prs, err := issues_model.GetUnmergedPullRequestsByBaseInfo(ctx, repoID, branch)
646+
// RetargetBranchPulls change target branch for all pull requests whose base branch is oldBranch
647+
// Both oldBranch and newTargetBranch must be in the same repo (for security reasons)
648+
func RetargetBranchPulls(ctx context.Context, doer *user_model.User, repoID int64, oldBranch, newTargetBranch string) error {
649+
prs, err := issues_model.GetUnmergedPullRequestsByBaseInfo(ctx, repoID, oldBranch)
650650
if err != nil {
651651
return err
652652
}
@@ -659,7 +659,7 @@ func retargetBranchPulls(ctx context.Context, doer *user_model.User, repoID int6
659659
for _, pr := range prs {
660660
if err = pr.Issue.LoadRepo(ctx); err != nil {
661661
errs = append(errs, err)
662-
} else if err = ChangeTargetBranch(ctx, pr, doer, targetBranch); err != nil &&
662+
} else if err = ChangeTargetBranch(ctx, pr, doer, newTargetBranch); err != nil &&
663663
!issues_model.IsErrIssueIsClosed(err) && !IsErrPullRequestHasMerged(err) &&
664664
!issues_model.IsErrPullRequestAlreadyExists(err) {
665665
errs = append(errs, err)
@@ -668,9 +668,10 @@ func retargetBranchPulls(ctx context.Context, doer *user_model.User, repoID int6
668668
return errors.Join(errs...)
669669
}
670670

671-
// AdjustPullsCausedByBranchDeleted close all the pull requests who's head branch is the branch
672-
// Or Close all the plls who's base branch is the branch if setting.Repository.PullRequest.RetargetChildrenOnMerge is false.
673-
// If it's true, Retarget all these pulls to the default branch.
671+
// AdjustPullsCausedByBranchDeleted close all the pull requests with the deleted branch as head
672+
// Retarget all PR's with the deleted branch as target to the default branch
673+
// We don't handle setting.Repository.PullRequest.RetargetChildrenOnMerge here,
674+
// because we would need the PR that was merged to determine which branch to redirect to
674675
func AdjustPullsCausedByBranchDeleted(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, branch string) error {
675676
// branch as head branch
676677
prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(ctx, repo.ID, branch)
@@ -698,12 +699,7 @@ func AdjustPullsCausedByBranchDeleted(ctx context.Context, doer *user_model.User
698699
}
699700
}
700701
}
701-
702-
if setting.Repository.PullRequest.RetargetChildrenOnMerge {
703-
if err := retargetBranchPulls(ctx, doer, repo.ID, branch, repo.DefaultBranch); err != nil {
704-
log.Error("retargetBranchPulls failed: %v", err)
705-
errs = append(errs, err)
706-
}
702+
if len(errs) > 0 {
707703
return errors.Join(errs...)
708704
}
709705

services/repository/branch.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,13 @@ import (
2727
"code.gitea.io/gitea/modules/queue"
2828
repo_module "code.gitea.io/gitea/modules/repository"
2929
"code.gitea.io/gitea/modules/reqctx"
30+
"code.gitea.io/gitea/modules/setting"
3031
"code.gitea.io/gitea/modules/timeutil"
3132
"code.gitea.io/gitea/modules/util"
3233
webhook_module "code.gitea.io/gitea/modules/webhook"
3334
actions_service "code.gitea.io/gitea/services/actions"
3435
notify_service "code.gitea.io/gitea/services/notify"
36+
pull_service "code.gitea.io/gitea/services/pull"
3537
release_service "code.gitea.io/gitea/services/release"
3638
files_service "code.gitea.io/gitea/services/repository/files"
3739

@@ -538,6 +540,13 @@ func DeleteBranch(ctx context.Context, doer *user_model.User, repo *repo_model.R
538540
}
539541

540542
if err := db.WithTx(ctx, func(ctx context.Context) error {
543+
if pr != nil && setting.Repository.PullRequest.RetargetChildrenOnMerge {
544+
if err := pull_service.RetargetBranchPulls(ctx, doer, repo.ID, branchName, pr.BaseBranch); err != nil {
545+
log.Error("retargetBranchPulls failed: %v", err)
546+
return err
547+
}
548+
}
549+
541550
if !notExist {
542551
if err := git_model.AddDeletedBranch(ctx, repo.ID, branchName, doer.ID); err != nil {
543552
return err

tests/integration/pull_merge_test.go

Lines changed: 58 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -538,35 +538,71 @@ func TestConflictChecking(t *testing.T) {
538538

539539
func TestPullRetargetChildOnBranchDelete(t *testing.T) {
540540
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
541-
session := loginUser(t, "user1")
542-
testEditFileToNewBranch(t, session, "user2", "repo1", "master", "base-pr", "README.md", "Hello, World\n(Edited - TestPullRetargetOnCleanup - base PR)\n")
543-
testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
544-
testEditFileToNewBranch(t, session, "user1", "repo1", "base-pr", "child-pr", "README.md", "Hello, World\n(Edited - TestPullRetargetOnCleanup - base PR)\n(Edited - TestPullRetargetOnCleanup - child PR)")
541+
session := loginUser(t, "user2")
542+
testEditFileToNewBranch(t, session, "user2", "repo1", "master", "branch-1", "README.md", "1")
543+
testEditFileToNewBranch(t, session, "user2", "repo1", "branch-1", "branch-2", "README.md", "2")
544+
testEditFileToNewBranch(t, session, "user2", "repo1", "branch-2", "branch-3", "README.md", "3")
545+
testEditFileToNewBranch(t, session, "user2", "repo1", "branch-3", "branch-4", "README.md", "4")
545546

546-
respBasePR := testPullCreate(t, session, "user2", "repo1", true, "master", "base-pr", "Base Pull Request")
547-
elemBasePR := strings.Split(test.RedirectURL(respBasePR), "/")
548-
assert.Equal(t, "pulls", elemBasePR[3])
547+
respPR1 := testPullCreate(t, session, "user2", "repo1", false, "master", "branch-1", "PR branch-1 > master")
548+
respPR2 := testPullCreate(t, session, "user2", "repo1", false, "branch-1", "branch-2", "PR branch-2 > branch-1")
549+
respPR3 := testPullCreate(t, session, "user2", "repo1", false, "branch-2", "branch-3", "PR branch-3 > branch-2")
550+
respPR4 := testPullCreate(t, session, "user2", "repo1", false, "branch-3", "branch-4", "PR branch-4 > branch-3")
549551

550-
respChildPR := testPullCreate(t, session, "user1", "repo1", false, "base-pr", "child-pr", "Child Pull Request")
551-
elemChildPR := strings.Split(test.RedirectURL(respChildPR), "/")
552-
assert.Equal(t, "pulls", elemChildPR[3])
553-
554-
testPullMerge(t, session, elemBasePR[1], elemBasePR[2], elemBasePR[4], repo_model.MergeStyleMerge, true)
552+
elemPR2 := strings.Split(test.RedirectURL(respPR2), "/")
553+
testPullMerge(t, session, elemPR2[1], elemPR2[2], elemPR2[4], repo_model.MergeStyleMerge, true)
555554

556555
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user2", Name: "repo1"})
557-
branchBasePR := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "base-pr"})
558-
assert.True(t, branchBasePR.IsDeleted)
556+
mergedPR := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch-2"})
557+
assert.True(t, mergedPR.IsDeleted)
558+
{
559+
// Check PR branch-1 > master is unchanged
560+
req := NewRequest(t, "GET", test.RedirectURL(respPR1))
561+
resp := session.MakeRequest(t, req, http.StatusOK)
562+
563+
htmlDoc := NewHTMLParser(t, resp.Body)
564+
targetBranch := htmlDoc.doc.Find("#branch_target>a").Text()
565+
prStatus := strings.TrimSpace(htmlDoc.doc.Find(".issue-title-meta>.issue-state-label").Text())
566+
567+
assert.Equal(t, "master", targetBranch)
568+
assert.Equal(t, "Open", prStatus)
569+
}
570+
{
571+
// Check PR branch-2 > branch-1 is merged
572+
req := NewRequest(t, "GET", test.RedirectURL(respPR2))
573+
resp := session.MakeRequest(t, req, http.StatusOK)
559574

560-
// Check child PR
561-
req := NewRequest(t, "GET", test.RedirectURL(respChildPR))
562-
resp := session.MakeRequest(t, req, http.StatusOK)
575+
htmlDoc := NewHTMLParser(t, resp.Body)
576+
targetBranch := htmlDoc.doc.Find("#branch_target>a").Text()
577+
prStatus := strings.TrimSpace(htmlDoc.doc.Find(".issue-title-meta>.issue-state-label").Text())
563578

564-
htmlDoc := NewHTMLParser(t, resp.Body)
565-
targetBranch := htmlDoc.doc.Find("#branch_target>a").Text()
566-
prStatus := strings.TrimSpace(htmlDoc.doc.Find(".issue-title-meta>.issue-state-label").Text())
579+
assert.Empty(t, targetBranch)
580+
assert.Equal(t, "Merged", prStatus)
581+
}
582+
{
583+
// Check PR branch-3 > branch-2 is rerouted to branch-1
584+
req := NewRequest(t, "GET", test.RedirectURL(respPR3))
585+
resp := session.MakeRequest(t, req, http.StatusOK)
567586

568-
assert.Equal(t, "master", targetBranch)
569-
assert.Equal(t, "Open", prStatus)
587+
htmlDoc := NewHTMLParser(t, resp.Body)
588+
targetBranch := htmlDoc.doc.Find("#branch_target>a").Text()
589+
prStatus := strings.TrimSpace(htmlDoc.doc.Find(".issue-title-meta>.issue-state-label").Text())
590+
591+
assert.Equal(t, "branch-1", targetBranch)
592+
assert.Equal(t, "Open", prStatus)
593+
}
594+
{
595+
// Check PR branch-4 > branch-3 is unchanged
596+
req := NewRequest(t, "GET", test.RedirectURL(respPR4))
597+
resp := session.MakeRequest(t, req, http.StatusOK)
598+
599+
htmlDoc := NewHTMLParser(t, resp.Body)
600+
targetBranch := htmlDoc.doc.Find("#branch_target>a").Text()
601+
prStatus := strings.TrimSpace(htmlDoc.doc.Find(".issue-title-meta>.issue-state-label").Text())
602+
603+
assert.Equal(t, "branch-3", targetBranch)
604+
assert.Equal(t, "Open", prStatus)
605+
}
570606
})
571607
}
572608

0 commit comments

Comments
 (0)