Skip to content

Commit 09adc26

Browse files
Gustedzeripath
andauthored
Set correct PR status on 3way on conflict checking (#19457) (#19458)
- Backport #19457 - When 3-way merge is enabled for conflict checking, it has a new interesting behavior that it doesn't return any error when it found a conflict, so we change the condition to not check for the error, but instead check if conflictedfiles is populated, this fixes a issue whereby PR status wasn't correctly on conflicted PR's. - Refactor the mergeable property(which was incorrectly set and lead me this bug) to be more maintainable. - Add a dedicated test for conflicting checking, so it should prevent future issues with this. - Ref: Fix the latest error for https://gitea.com/gitea/go-sdk/pulls/579 Co-authored-by: zeripath <[email protected]>
1 parent 297346a commit 09adc26

File tree

4 files changed

+93
-10
lines changed

4 files changed

+93
-10
lines changed

integrations/pull_merge_test.go

Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import (
2525
api "code.gitea.io/gitea/modules/structs"
2626
"code.gitea.io/gitea/modules/test"
2727
"code.gitea.io/gitea/services/pull"
28+
repo_service "code.gitea.io/gitea/services/repository"
29+
files_service "code.gitea.io/gitea/services/repository/files"
2830

2931
"github.com/stretchr/testify/assert"
3032
"github.com/unknwon/i18n"
@@ -65,7 +67,7 @@ func testPullCleanUp(t *testing.T, session *TestSession, user, repo, pullnum str
6567

6668
func TestPullMerge(t *testing.T) {
6769
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
68-
hookTasks, err := webhook.HookTasks(1, 1) //Retrieve previous hook number
70+
hookTasks, err := webhook.HookTasks(1, 1) // Retrieve previous hook number
6971
assert.NoError(t, err)
7072
hookTasksLenBefore := len(hookTasks)
7173

@@ -87,7 +89,7 @@ func TestPullMerge(t *testing.T) {
8789

8890
func TestPullRebase(t *testing.T) {
8991
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
90-
hookTasks, err := webhook.HookTasks(1, 1) //Retrieve previous hook number
92+
hookTasks, err := webhook.HookTasks(1, 1) // Retrieve previous hook number
9193
assert.NoError(t, err)
9294
hookTasksLenBefore := len(hookTasks)
9395

@@ -109,7 +111,7 @@ func TestPullRebase(t *testing.T) {
109111

110112
func TestPullRebaseMerge(t *testing.T) {
111113
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
112-
hookTasks, err := webhook.HookTasks(1, 1) //Retrieve previous hook number
114+
hookTasks, err := webhook.HookTasks(1, 1) // Retrieve previous hook number
113115
assert.NoError(t, err)
114116
hookTasksLenBefore := len(hookTasks)
115117

@@ -131,7 +133,7 @@ func TestPullRebaseMerge(t *testing.T) {
131133

132134
func TestPullSquash(t *testing.T) {
133135
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
134-
hookTasks, err := webhook.HookTasks(1, 1) //Retrieve previous hook number
136+
hookTasks, err := webhook.HookTasks(1, 1) // Retrieve previous hook number
135137
assert.NoError(t, err)
136138
hookTasksLenBefore := len(hookTasks)
137139

@@ -335,3 +337,74 @@ func TestCantMergeUnrelated(t *testing.T) {
335337
gitRepo.Close()
336338
})
337339
}
340+
341+
func TestConflictChecking(t *testing.T) {
342+
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
343+
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
344+
345+
// Create new clean repo to test conflict checking.
346+
baseRepo, err := repo_service.CreateRepository(user, user, models.CreateRepoOptions{
347+
Name: "conflict-checking",
348+
Description: "Tempo repo",
349+
AutoInit: true,
350+
Readme: "Default",
351+
DefaultBranch: "main",
352+
})
353+
assert.NoError(t, err)
354+
assert.NotEmpty(t, baseRepo)
355+
356+
// create a commit on new branch.
357+
_, err = files_service.CreateOrUpdateRepoFile(baseRepo, user, &files_service.UpdateRepoFileOptions{
358+
TreePath: "important_file",
359+
Message: "Add a important file",
360+
Content: "Just a non-important file",
361+
IsNewFile: true,
362+
OldBranch: "main",
363+
NewBranch: "important-secrets",
364+
})
365+
assert.NoError(t, err)
366+
367+
// create a commit on main branch.
368+
_, err = files_service.CreateOrUpdateRepoFile(baseRepo, user, &files_service.UpdateRepoFileOptions{
369+
TreePath: "important_file",
370+
Message: "Add a important file",
371+
Content: "Not the same content :P",
372+
IsNewFile: true,
373+
OldBranch: "main",
374+
NewBranch: "main",
375+
})
376+
assert.NoError(t, err)
377+
378+
// create Pull to merge the important-secrets branch into main branch.
379+
pullIssue := &models.Issue{
380+
RepoID: baseRepo.ID,
381+
Title: "PR with conflict!",
382+
PosterID: user.ID,
383+
Poster: user,
384+
IsPull: true,
385+
}
386+
387+
pullRequest := &models.PullRequest{
388+
HeadRepoID: baseRepo.ID,
389+
BaseRepoID: baseRepo.ID,
390+
HeadBranch: "important-secrets",
391+
BaseBranch: "main",
392+
HeadRepo: baseRepo,
393+
BaseRepo: baseRepo,
394+
Type: models.PullRequestGitea,
395+
}
396+
err = pull.NewPullRequest(baseRepo, pullIssue, nil, nil, pullRequest, nil)
397+
assert.NoError(t, err)
398+
399+
issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{Title: "PR with conflict!"}).(*models.Issue)
400+
conflictingPR, err := models.GetPullRequestByIssueID(issue.ID)
401+
assert.NoError(t, err)
402+
403+
// Ensure conflictedFiles is populated.
404+
assert.Equal(t, 1, len(conflictingPR.ConflictedFiles))
405+
// Check if status is correct.
406+
assert.Equal(t, models.PullRequestStatusConflict, conflictingPR.Status)
407+
// Ensure that mergeable returns false
408+
assert.False(t, conflictingPR.Mergeable())
409+
})
410+
}

models/pull.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,3 +697,14 @@ func (pr *PullRequest) GetHeadBranchHTMLURL() string {
697697
}
698698
return pr.HeadRepo.HTMLURL() + "/src/branch/" + util.PathEscapeSegments(pr.HeadBranch)
699699
}
700+
701+
// Mergeable returns if the pullrequest is mergeable.
702+
func (pr *PullRequest) Mergeable() bool {
703+
// If a pull request isn't mergable if it's:
704+
// - Being conflict checked.
705+
// - Has a conflict.
706+
// - Received a error while being conflict checked.
707+
// - Is a work-in-progress pull request.
708+
return pr.Status != PullRequestStatusChecking && pr.Status != PullRequestStatusConflict &&
709+
pr.Status != PullRequestStatusError && !pr.IsWorkInProgress()
710+
}

modules/convert/pull.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ func ToAPIPullRequest(pr *models.PullRequest, doer *user_model.User) *api.PullRe
6767
PatchURL: pr.Issue.PatchURL(),
6868
HasMerged: pr.HasMerged,
6969
MergeBase: pr.MergeBase,
70+
Mergeable: pr.Mergeable(),
7071
Deadline: apiIssue.Deadline,
7172
Created: pr.Issue.CreatedUnix.AsTimePtr(),
7273
Updated: pr.Issue.UpdatedUnix.AsTimePtr(),
@@ -190,10 +191,6 @@ func ToAPIPullRequest(pr *models.PullRequest, doer *user_model.User) *api.PullRe
190191
}
191192
}
192193

193-
if pr.Status != models.PullRequestStatusChecking {
194-
mergeable := !(pr.Status == models.PullRequestStatusConflict || pr.Status == models.PullRequestStatusError) && !pr.IsWorkInProgress()
195-
apiPullRequest.Mergeable = mergeable
196-
}
197194
if pr.HasMerged {
198195
apiPullRequest.Merged = pr.MergedUnix.AsTimePtr()
199196
apiPullRequest.MergedCommitID = &pr.MergedCommitID

services/pull/patch.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -431,14 +431,16 @@ func checkConflicts(pr *models.PullRequest, gitRepo *git.Repository, tmpBasePath
431431
return nil
432432
})
433433

434-
// 8. If there is a conflict the `git apply` command will return a non-zero error code - so there will be a positive error.
435-
if err != nil {
434+
// 9. Check if the found conflictedfiles is non-zero, "err" could be non-nil, so we should ignore it if we found conflicts.
435+
// Note: `"err" could be non-nil` is due that if enable 3-way merge, it doesn't return any error on found conflicts.
436+
if len(pr.ConflictedFiles) > 0 {
436437
if conflict {
437438
pr.Status = models.PullRequestStatusConflict
438439
log.Trace("Found %d files conflicted: %v", len(pr.ConflictedFiles), pr.ConflictedFiles)
439440

440441
return true, nil
441442
}
443+
} else if err != nil {
442444
return false, fmt.Errorf("git apply --check: %v", err)
443445
}
444446
return false, nil

0 commit comments

Comments
 (0)