Skip to content

Commit ebe569a

Browse files
author
Gusted
authored
Set correct PR status on 3way on conflict checking (#19457)
* Set correct PR status on 3way on conflict checking - 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. * Fix linter
1 parent 3ec1b6c commit ebe569a

File tree

4 files changed

+89
-6
lines changed

4 files changed

+89
-6
lines changed

integrations/pull_merge_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import (
2626
"code.gitea.io/gitea/modules/test"
2727
"code.gitea.io/gitea/modules/translation/i18n"
2828
"code.gitea.io/gitea/services/pull"
29+
repo_service "code.gitea.io/gitea/services/repository"
30+
files_service "code.gitea.io/gitea/services/repository/files"
2931

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

models/pull.go

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

modules/convert/pull.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ func ToAPIPullRequest(ctx context.Context, pr *models.PullRequest, doer *user_mo
6868
PatchURL: pr.Issue.PatchURL(),
6969
HasMerged: pr.HasMerged,
7070
MergeBase: pr.MergeBase,
71+
Mergeable: pr.Mergeable(),
7172
Deadline: apiIssue.Deadline,
7273
Created: pr.Issue.CreatedUnix.AsTimePtr(),
7374
Updated: pr.Issue.UpdatedUnix.AsTimePtr(),
@@ -191,10 +192,6 @@ func ToAPIPullRequest(ctx context.Context, pr *models.PullRequest, doer *user_mo
191192
}
192193
}
193194

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

services/pull/patch.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -444,14 +444,16 @@ func checkConflicts(ctx context.Context, pr *models.PullRequest, gitRepo *git.Re
444444
},
445445
})
446446

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

453454
return true, nil
454455
}
456+
} else if err != nil {
455457
return false, fmt.Errorf("git apply --check: %v", err)
456458
}
457459
return false, nil

0 commit comments

Comments
 (0)