Skip to content

Commit 0d7d00a

Browse files
lunnywxiaoguang
authored andcommitted
Add rebase push display wrong comments bug (go-gitea#35560)
Fix go-gitea#35518 --------- Co-authored-by: wxiaoguang <[email protected]>
1 parent b04f209 commit 0d7d00a

File tree

8 files changed

+178
-22
lines changed

8 files changed

+178
-22
lines changed

models/fixtures/branch.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,3 +213,15 @@
213213
is_deleted: false
214214
deleted_by_id: 0
215215
deleted_unix: 0
216+
217+
-
218+
id: 26
219+
repo_id: 10
220+
name: 'feature/1'
221+
commit_id: '65f1bf27bc3bf70f64657658635e66094edbcb4d'
222+
commit_message: 'Initial commit'
223+
commit_time: 1489950479
224+
pusher_id: 2
225+
is_deleted: false
226+
deleted_by_id: 0
227+
deleted_unix: 0

routers/web/repo/pull.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1583,7 +1583,16 @@ func UpdatePullRequestTarget(ctx *context.Context) {
15831583
}
15841584

15851585
if err := pull_service.ChangeTargetBranch(ctx, pr, ctx.Doer, targetBranch); err != nil {
1586-
if issues_model.IsErrPullRequestAlreadyExists(err) {
1586+
switch {
1587+
case git_model.IsErrBranchNotExist(err):
1588+
errorMessage := ctx.Tr("form.target_branch_not_exist")
1589+
1590+
ctx.Flash.Error(errorMessage)
1591+
ctx.JSON(http.StatusBadRequest, map[string]any{
1592+
"error": err.Error(),
1593+
"user_error": errorMessage,
1594+
})
1595+
case issues_model.IsErrPullRequestAlreadyExists(err):
15871596
err := err.(issues_model.ErrPullRequestAlreadyExists)
15881597

15891598
RepoRelPath := ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name
@@ -1594,31 +1603,31 @@ func UpdatePullRequestTarget(ctx *context.Context) {
15941603
"error": err.Error(),
15951604
"user_error": errorMessage,
15961605
})
1597-
} else if issues_model.IsErrIssueIsClosed(err) {
1606+
case issues_model.IsErrIssueIsClosed(err):
15981607
errorMessage := ctx.Tr("repo.pulls.is_closed")
15991608

16001609
ctx.Flash.Error(errorMessage)
16011610
ctx.JSON(http.StatusConflict, map[string]any{
16021611
"error": err.Error(),
16031612
"user_error": errorMessage,
16041613
})
1605-
} else if pull_service.IsErrPullRequestHasMerged(err) {
1614+
case pull_service.IsErrPullRequestHasMerged(err):
16061615
errorMessage := ctx.Tr("repo.pulls.has_merged")
16071616

16081617
ctx.Flash.Error(errorMessage)
16091618
ctx.JSON(http.StatusConflict, map[string]any{
16101619
"error": err.Error(),
16111620
"user_error": errorMessage,
16121621
})
1613-
} else if git_model.IsErrBranchesEqual(err) {
1622+
case git_model.IsErrBranchesEqual(err):
16141623
errorMessage := ctx.Tr("repo.pulls.nothing_to_compare")
16151624

16161625
ctx.Flash.Error(errorMessage)
16171626
ctx.JSON(http.StatusBadRequest, map[string]any{
16181627
"error": err.Error(),
16191628
"user_error": errorMessage,
16201629
})
1621-
} else {
1630+
default:
16221631
ctx.ServerError("UpdatePullRequestTarget", err)
16231632
}
16241633
return

services/pull/comment.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ func CreatePushPullComment(ctx context.Context, pusher *user_model.User, pr *iss
6363
var data issues_model.PushActionContent
6464
if opts.IsForcePush {
6565
data.CommitIDs = []string{oldCommitID, newCommitID}
66+
data.IsForcePush = true
6667
} else {
6768
data.CommitIDs, err = getCommitIDsFromRepo(ctx, pr.BaseRepo, oldCommitID, newCommitID, pr.BaseBranch)
6869
if err != nil {

services/pull/pull.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,17 @@ func ChangeTargetBranch(ctx context.Context, pr *issues_model.PullRequest, doer
243243
}
244244
}
245245

246+
exist, err := git_model.IsBranchExist(ctx, pr.BaseRepoID, targetBranch)
247+
if err != nil {
248+
return err
249+
}
250+
if !exist {
251+
return git_model.ErrBranchNotExist{
252+
RepoID: pr.BaseRepoID,
253+
BranchName: targetBranch,
254+
}
255+
}
256+
246257
// Check if branches are equal
247258
branchesEqual, err := IsHeadEqualWithBranch(ctx, pr, targetBranch)
248259
if err != nil {

tests/integration/git_helper_for_declarative_test.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"code.gitea.io/gitea/tests"
2424

2525
"github.com/stretchr/testify/assert"
26+
"github.com/stretchr/testify/require"
2627
)
2728

2829
func withKeyFile(t *testing.T, keyname string, callback func(string)) {
@@ -160,20 +161,27 @@ func doGitPushTestRepositoryFail(dstPath string, args ...string) func(*testing.T
160161
}
161162
}
162163

163-
func doGitAddSomeCommits(dstPath, branch string) func(*testing.T) {
164-
return func(t *testing.T) {
165-
doGitCheckoutBranch(dstPath, branch)(t)
164+
type localGitAddCommitOptions struct {
165+
LocalRepoPath string
166+
CheckoutBranch string
167+
TreeFilePath string
168+
TreeFileContent string
169+
}
166170

167-
assert.NoError(t, os.WriteFile(filepath.Join(dstPath, fmt.Sprintf("file-%s.txt", branch)), []byte("file "+branch), 0o644))
168-
assert.NoError(t, git.AddChanges(t.Context(), dstPath, true))
171+
func doGitCheckoutWriteFileCommit(opts localGitAddCommitOptions) func(*testing.T) {
172+
return func(t *testing.T) {
173+
doGitCheckoutBranch(opts.LocalRepoPath, opts.CheckoutBranch)(t)
174+
localFilePath := filepath.Join(opts.LocalRepoPath, opts.TreeFilePath)
175+
require.NoError(t, os.WriteFile(localFilePath, []byte(opts.TreeFileContent), 0o644))
176+
require.NoError(t, git.AddChanges(t.Context(), opts.LocalRepoPath, true))
169177
signature := git.Signature{
170178
171179
Name: "test",
172180
}
173-
assert.NoError(t, git.CommitChanges(t.Context(), dstPath, git.CommitChangesOptions{
181+
require.NoError(t, git.CommitChanges(t.Context(), opts.LocalRepoPath, git.CommitChangesOptions{
174182
Committer: &signature,
175183
Author: &signature,
176-
Message: "update " + branch,
184+
Message: fmt.Sprintf("update %s @ %s", opts.TreeFilePath, opts.CheckoutBranch),
177185
}))
178186
}
179187
}

tests/integration/git_push_test.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,12 @@ func testGitPush(t *testing.T, u *url.URL) {
5858
for i := range 5 {
5959
branchName := fmt.Sprintf("branch-%d", i)
6060
pushed = append(pushed, branchName)
61-
62-
doGitAddSomeCommits(gitPath, branchName)(t)
61+
doGitCheckoutWriteFileCommit(localGitAddCommitOptions{
62+
LocalRepoPath: gitPath,
63+
CheckoutBranch: branchName,
64+
TreeFilePath: fmt.Sprintf("file-%s.txt", branchName),
65+
TreeFileContent: "file " + branchName,
66+
})(t)
6367
}
6468

6569
for i := 5; i < 10; i++ {
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// Copyright 2025 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package integration
5+
6+
import (
7+
"fmt"
8+
"net/http"
9+
"net/url"
10+
"os"
11+
"testing"
12+
"time"
13+
14+
issues_model "code.gitea.io/gitea/models/issues"
15+
"code.gitea.io/gitea/models/unittest"
16+
issues_service "code.gitea.io/gitea/services/issue"
17+
18+
"github.com/stretchr/testify/assert"
19+
"github.com/stretchr/testify/require"
20+
)
21+
22+
func testWaitForPullRequestStatus(t *testing.T, prIssue *issues_model.Issue, expectedStatus issues_model.PullRequestStatus) (retIssue *issues_model.Issue) {
23+
require.Eventually(t, func() bool {
24+
prIssueCond := *prIssue
25+
retIssue = unittest.AssertExistsAndLoadBean(t, &prIssueCond)
26+
require.NoError(t, retIssue.LoadPullRequest(t.Context()))
27+
return retIssue.PullRequest.Status == expectedStatus
28+
}, 5*time.Second, 20*time.Millisecond)
29+
return retIssue
30+
}
31+
32+
func testPullCommentRebase(t *testing.T, u *url.URL, session *TestSession) {
33+
testPRTitle := "Test PR for rebase comment"
34+
// make a change on forked branch
35+
testEditFile(t, session, "user1", "repo1", "test-branch/rebase", "README.md", "Hello, World (Edited)\n")
36+
testPullCreate(t, session, "user1", "repo1", false, "test-branch/rebase", "test-branch/rebase", testPRTitle)
37+
// create a conflict on base repo branch
38+
testEditFile(t, session, "user2", "repo1", "test-branch/rebase", "README.md", "Hello, World (Edited Conflicted)\n")
39+
40+
// Now the pull request status should be conflicted
41+
testWaitForPullRequestStatus(t, &issues_model.Issue{Title: testPRTitle}, issues_model.PullRequestStatusConflict)
42+
43+
dstPath := t.TempDir()
44+
u.Path = "/user2/repo1.git"
45+
doGitClone(dstPath, u)(t)
46+
doGitCheckoutBranch(dstPath, "test-branch/rebase")(t)
47+
doGitCreateBranch(dstPath, "local-branch/rebase")(t)
48+
content, _ := os.ReadFile(dstPath + "/README.md")
49+
require.Equal(t, "Hello, World (Edited Conflicted)\n", string(content))
50+
51+
doGitCheckoutWriteFileCommit(localGitAddCommitOptions{
52+
LocalRepoPath: dstPath,
53+
CheckoutBranch: "local-branch/rebase",
54+
TreeFilePath: "README.md",
55+
TreeFileContent: "Hello, World (Edited Conflict Resolved)\n",
56+
})(t)
57+
58+
// do force push
59+
u.Path = "/user1/repo1.git"
60+
u.User = url.UserPassword("user1", userPassword)
61+
doGitAddRemote(dstPath, "base-repo", u)(t)
62+
doGitPushTestRepositoryFail(dstPath, "base-repo", "local-branch/rebase:test-branch/rebase")(t)
63+
doGitPushTestRepository(dstPath, "--force", "base-repo", "local-branch/rebase:test-branch/rebase")(t)
64+
65+
// reload the pr
66+
prIssue := testWaitForPullRequestStatus(t, &issues_model.Issue{Title: testPRTitle}, issues_model.PullRequestStatusMergeable)
67+
comments, err := issues_model.FindComments(t.Context(), &issues_model.FindCommentsOptions{
68+
IssueID: prIssue.ID,
69+
Type: issues_model.CommentTypeUndefined, // get all comments type
70+
})
71+
require.NoError(t, err)
72+
lastComment := comments[len(comments)-1]
73+
assert.NoError(t, issues_service.LoadCommentPushCommits(t.Context(), lastComment))
74+
assert.True(t, lastComment.IsForcePush)
75+
}
76+
77+
func testPullCommentRetarget(t *testing.T, u *url.URL, session *TestSession) {
78+
testPRTitle := "Test PR for retarget comment"
79+
// keep a non-conflict branch
80+
testCreateBranch(t, session, "user2", "repo1", "branch/test-branch/retarget", "test-branch/retarget-no-conflict", http.StatusSeeOther)
81+
// make a change on forked branch
82+
testEditFile(t, session, "user1", "repo1", "test-branch/retarget", "README.md", "Hello, World (Edited)\n")
83+
testPullCreate(t, session, "user1", "repo1", false, "test-branch/retarget", "test-branch/retarget", testPRTitle)
84+
// create a conflict line on user2/repo1 README.md
85+
testEditFile(t, session, "user2", "repo1", "test-branch/retarget", "README.md", "Hello, World (Edited Conflicted)\n")
86+
87+
// Now the pull request status should be conflicted
88+
prIssue := testWaitForPullRequestStatus(t, &issues_model.Issue{Title: testPRTitle}, issues_model.PullRequestStatusConflict)
89+
90+
// do retarget
91+
req := NewRequestWithValues(t, "POST", fmt.Sprintf("/user2/repo1/pull/%d/target_branch", prIssue.PullRequest.Index), map[string]string{
92+
"_csrf": GetUserCSRFToken(t, session),
93+
"target_branch": "test-branch/retarget-no-conflict",
94+
})
95+
session.MakeRequest(t, req, http.StatusOK)
96+
testWaitForPullRequestStatus(t, &issues_model.Issue{Title: testPRTitle}, issues_model.PullRequestStatusMergeable)
97+
}
98+
99+
func TestPullComment(t *testing.T) {
100+
onGiteaRun(t, func(t *testing.T, u *url.URL) {
101+
session := loginUser(t, "user1")
102+
testCreateBranch(t, session, "user2", "repo1", "branch/master", "test-branch/rebase", http.StatusSeeOther)
103+
testCreateBranch(t, session, "user2", "repo1", "branch/master", "test-branch/retarget", http.StatusSeeOther)
104+
testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
105+
106+
t.Run("RebaseComment", func(t *testing.T) { testPullCommentRebase(t, u, session) })
107+
t.Run("RetargetComment", func(t *testing.T) { testPullCommentRetarget(t, u, session) })
108+
})
109+
}

tests/integration/pull_create_test.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -260,14 +260,16 @@ func TestCreateAgitPullWithReadPermission(t *testing.T) {
260260
u.Path = "user2/repo1.git"
261261
u.User = url.UserPassword("user4", userPassword)
262262

263-
t.Run("Clone", doGitClone(dstPath, u))
264-
265-
t.Run("add commit", doGitAddSomeCommits(dstPath, "master"))
266-
267-
t.Run("do agit pull create", func(t *testing.T) {
268-
err := gitcmd.NewCommand("push", "origin", "HEAD:refs/for/master", "-o").AddDynamicArguments("topic="+"test-topic").Run(t.Context(), &gitcmd.RunOpts{Dir: dstPath})
269-
assert.NoError(t, err)
270-
})
263+
doGitClone(dstPath, u)(t)
264+
doGitCheckoutWriteFileCommit(localGitAddCommitOptions{
265+
LocalRepoPath: dstPath,
266+
CheckoutBranch: "master",
267+
TreeFilePath: "new-file-for-agit.txt",
268+
TreeFileContent: "temp content",
269+
})(t)
270+
271+
err := gitcmd.NewCommand("push", "origin", "HEAD:refs/for/master", "-o").AddDynamicArguments("topic="+"test-topic").Run(t.Context(), &gitcmd.RunOpts{Dir: dstPath})
272+
assert.NoError(t, err)
271273
})
272274
}
273275

0 commit comments

Comments
 (0)