From 047933af1c14c7589098d4f70a0dfb415872ad2a Mon Sep 17 00:00:00 2001 From: Nicolas Date: Tue, 28 Apr 2026 17:38:18 +0200 Subject: [PATCH 01/13] Implement merge base error handling and UI updates for branch comparisons - Introduced `ErrNoMergeBase` type to handle cases where two branches do not share a common merge base. - Updated `MergeBase` function to return this error type when applicable. - Enhanced the compare functionality to display a warning message in the UI when no common merge base is found. - Added tests to verify behavior when comparing branches with no common history. - Updated locale file to include a message for the no common history scenario. --- modules/gitrepo/compare_test.go | 63 +++++++++++++++++++++++++ modules/gitrepo/merge.go | 27 +++++++++++ options/locale/locale_en-US.json | 1 + routers/web/repo/compare.go | 12 +++++ templates/repo/diff/compare.tmpl | 6 ++- tests/integration/compare_test.go | 76 +++++++++++++++++++++++++++++++ 6 files changed, 184 insertions(+), 1 deletion(-) diff --git a/modules/gitrepo/compare_test.go b/modules/gitrepo/compare_test.go index 2d2af0934db7d..d77b52cae9a23 100644 --- a/modules/gitrepo/compare_test.go +++ b/modules/gitrepo/compare_test.go @@ -4,9 +4,17 @@ package gitrepo import ( + "bytes" + "os" + "path/filepath" + "strings" "testing" + "time" + + "code.gitea.io/gitea/modules/git/gitcmd" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) type mockRepository struct { @@ -17,6 +25,61 @@ func (r *mockRepository) RelativePath() string { return r.path } +func commitRootTree(t *testing.T, repoDir, fileName, content, message string) string { + t.Helper() + + require.NoError(t, gitcmd.NewCommand("read-tree", "--empty").WithDir(repoDir).Run(t.Context())) + + stdout, _, err := gitcmd.NewCommand("hash-object", "-w", "--stdin"). + WithDir(repoDir). + WithStdinBytes([]byte(content)). + RunStdString(t.Context()) + require.NoError(t, err) + blobSHA := strings.TrimSpace(stdout) + + _, _, err = gitcmd.NewCommand("update-index", "--add", "--replace", "--cacheinfo"). + AddDynamicArguments("100644", blobSHA, fileName). + WithDir(repoDir). + RunStdString(t.Context()) + require.NoError(t, err) + + stdout, _, err = gitcmd.NewCommand("write-tree").WithDir(repoDir).RunStdString(t.Context()) + require.NoError(t, err) + treeSHA := strings.TrimSpace(stdout) + + commitTimeStr := time.Now().Format(time.RFC3339) + env := append(os.Environ(), + "GIT_AUTHOR_NAME=Test", + "GIT_AUTHOR_EMAIL=test@example.com", + "GIT_AUTHOR_DATE="+commitTimeStr, + "GIT_COMMITTER_NAME=Test", + "GIT_COMMITTER_EMAIL=test@example.com", + "GIT_COMMITTER_DATE="+commitTimeStr, + ) + + messageBytes := bytes.NewBufferString(message + "\n") + stdout, _, err = gitcmd.NewCommand("commit-tree").AddDynamicArguments(treeSHA). + WithEnv(env). + WithDir(repoDir). + WithStdinBytes(messageBytes.Bytes()). + RunStdString(t.Context()) + require.NoError(t, err) + + return strings.TrimSpace(stdout) +} + +func TestMergeBaseNoCommonHistory(t *testing.T) { + repoDir := filepath.Join(t.TempDir(), "repo.git") + require.NoError(t, gitcmd.NewCommand("init").AddDynamicArguments(repoDir).Run(t.Context())) + + baseCommit := commitRootTree(t, repoDir, "base.txt", "base", "base") + headCommit := commitRootTree(t, repoDir, "head.txt", "head", "head") + + mergeBase, err := MergeBase(t.Context(), &mockRepository{path: repoDir}, baseCommit, headCommit) + assert.Empty(t, mergeBase) + assert.True(t, IsErrNoMergeBase(err), "expected no merge base error, got %v", err) +} + func TestRepoGetDivergingCommits(t *testing.T) { repo := &mockRepository{path: "repo1_bare"} do, err := GetDivergingCommits(t.Context(), repo, "master", "branch2") diff --git a/modules/gitrepo/merge.go b/modules/gitrepo/merge.go index 8d58e21c8dbb6..3603481a48689 100644 --- a/modules/gitrepo/merge.go +++ b/modules/gitrepo/merge.go @@ -5,17 +5,44 @@ package gitrepo import ( "context" + "errors" "fmt" "strings" "code.gitea.io/gitea/modules/git/gitcmd" ) +type ErrNoMergeBase struct { + BaseCommitID string + HeadCommitID string + Err error +} + +func IsErrNoMergeBase(err error) bool { + var noMergeBase ErrNoMergeBase + return errors.As(err, &noMergeBase) +} + +func (err ErrNoMergeBase) Error() string { + return fmt.Sprintf("get merge-base of %s and %s failed: %v", err.BaseCommitID, err.HeadCommitID, err.Err) +} + +func (err ErrNoMergeBase) Unwrap() error { + return err.Err +} + // MergeBase checks and returns merge base of two commits. func MergeBase(ctx context.Context, repo Repository, baseCommitID, headCommitID string) (string, error) { mergeBase, _, err := RunCmdString(ctx, repo, gitcmd.NewCommand("merge-base"). AddDashesAndList(baseCommitID, headCommitID)) if err != nil { + if gitcmd.IsErrorExitCode(err, 1) { + return "", ErrNoMergeBase{ + BaseCommitID: baseCommitID, + HeadCommitID: headCommitID, + Err: err, + } + } return "", fmt.Errorf("get merge-base of %s and %s failed: %w", baseCommitID, headCommitID, err) } return strings.TrimSpace(mergeBase), nil diff --git a/options/locale/locale_en-US.json b/options/locale/locale_en-US.json index d6c733b50b3c5..0d31e045c80b8 100644 --- a/options/locale/locale_en-US.json +++ b/options/locale/locale_en-US.json @@ -1781,6 +1781,7 @@ "repo.pulls.review_only_possible_for_full_diff": "Review is only possible when viewing the full diff", "repo.pulls.filter_changes_by_commit": "Filter by commit", "repo.pulls.nothing_to_compare": "These branches are equal. There is no need to create a pull request.", + "repo.pulls.no_common_history": "These branches do not share a common merge base. Select a different base or compare branch.", "repo.pulls.nothing_to_compare_have_tag": "The selected branches/tags are equal.", "repo.pulls.nothing_to_compare_and_allow_empty_pr": "These branches are equal. This PR will be empty.", "repo.pulls.has_pull_request": "A pull request between these branches already exists: %[2]s#%[3]d", diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index 285f3968d4181..100e6d44cad04 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -413,6 +413,12 @@ func ParseCompareInfo(ctx *context.Context) *git_service.CompareInfo { compareInfo, err := git_service.GetCompareInfo(ctx, baseRepo, headRepo, headGitRepo, baseRef, headRef, compareReq.DirectComparison(), fileOnly) if err != nil { + if gitrepo.IsErrNoMergeBase(err) { + ctx.Data["NoMergeBase"] = true + ctx.Data["BeforeCommitID"] = compareInfo.BaseCommitID + ctx.Data["AfterCommitID"] = compareInfo.HeadCommitID + return &compareInfo + } ctx.ServerError("GetCompareInfo", err) return nil } @@ -474,6 +480,12 @@ func PrepareCompareDiff( ctx.Data["TitleQuery"] = newPrFormTitle ctx.Data["BodyQuery"] = newPrFormBody + if ctx.Data["NoMergeBase"] == true { + ctx.Data["CommitCount"] = 0 + ctx.Data["Commits"] = []*git_model.SignCommitWithStatuses{} + return true + } + if (headCommitID == ci.MergeBase && !ci.DirectComparison()) || headCommitID == ci.BaseCommitID { ctx.Data["IsNothingToCompare"] = true diff --git a/templates/repo/diff/compare.tmpl b/templates/repo/diff/compare.tmpl index afd44f26a4736..467cdbd548753 100644 --- a/templates/repo/diff/compare.tmpl +++ b/templates/repo/diff/compare.tmpl @@ -168,7 +168,11 @@ {{$showDiffBox := and .CommitCount (not .IsNothingToCompare)}} - {{if and .IsSigned .PageIsComparePull}} + {{if .NoMergeBase}} +
+ {{ctx.Locale.Tr "repo.pulls.no_common_history"}} +
+ {{else if and .IsSigned .PageIsComparePull}} {{$allowCreatePR := and ($.CompareInfo.BaseRef.IsBranch) ($.CompareInfo.HeadRef.IsBranch) (not $.CompareInfo.DirectComparison) (or $.AllowEmptyPr (not .IsNothingToCompare))}} {{if .IsNothingToCompare}}
diff --git a/tests/integration/compare_test.go b/tests/integration/compare_test.go index a3cb538d5b045..f926f106967ad 100644 --- a/tests/integration/compare_test.go +++ b/tests/integration/compare_test.go @@ -4,19 +4,25 @@ package integration import ( + "bytes" "fmt" "net/http" "net/url" + "os" "strings" "testing" + "time" + repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/test" repo_service "code.gitea.io/gitea/services/repository" "code.gitea.io/gitea/tests" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestCompareTag(t *testing.T) { @@ -124,6 +130,76 @@ func TestCompareBranches(t *testing.T) { inspectCompare(t, htmlDoc, diffCount, diffChanges) } +func createUnrelatedBranch(t *testing.T, repo *repo_model.Repository, user *user_model.User, branchName string) { + t.Helper() + + repoPath := repo_model.RepoPath(user.Name, repo.Name) + require.NoError(t, gitcmd.NewCommand("read-tree", "--empty").WithDir(repoPath).Run(t.Context())) + + stdout, _, err := gitcmd.NewCommand("hash-object", "-w", "--stdin"). + WithDir(repoPath). + WithStdinBytes([]byte("Unrelated File")). + RunStdString(t.Context()) + require.NoError(t, err) + blobSHA := strings.TrimSpace(stdout) + + _, _, err = gitcmd.NewCommand("update-index", "--add", "--replace", "--cacheinfo"). + AddDynamicArguments("100644", blobSHA, "unrelated.txt"). + WithDir(repoPath). + RunStdString(t.Context()) + require.NoError(t, err) + + stdout, _, err = gitcmd.NewCommand("write-tree").WithDir(repoPath).RunStdString(t.Context()) + require.NoError(t, err) + treeSHA := strings.TrimSpace(stdout) + + commitTimeStr := time.Now().Format(time.RFC3339) + doerSig := user.NewGitSig() + env := append(os.Environ(), + "GIT_AUTHOR_NAME="+doerSig.Name, + "GIT_AUTHOR_EMAIL="+doerSig.Email, + "GIT_AUTHOR_DATE="+commitTimeStr, + "GIT_COMMITTER_NAME="+doerSig.Name, + "GIT_COMMITTER_EMAIL="+doerSig.Email, + "GIT_COMMITTER_DATE="+commitTimeStr, + ) + + messageBytes := bytes.NewBufferString("Unrelated\n") + stdout, _, err = gitcmd.NewCommand("commit-tree").AddDynamicArguments(treeSHA). + WithEnv(env). + WithDir(repoPath). + WithStdinBytes(messageBytes.Bytes()). + RunStdString(t.Context()) + require.NoError(t, err) + commitSHA := strings.TrimSpace(stdout) + + _, _, err = gitcmd.NewCommand("branch").AddDynamicArguments(branchName, commitSHA). + WithDir(repoPath). + RunStdString(t.Context()) + require.NoError(t, err) +} + +func TestCompareBranchesNoCommonMergeBase(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"}) + repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: user2.ID, Name: "repo1"}) + createUnrelatedBranch(t, repo1, user2, "unrelated-history") + + session := loginUser(t, "user2") + req := NewRequest(t, "GET", "/user2/repo1/compare/master...unrelated-history") + resp := session.MakeRequest(t, req, http.StatusOK) + body := resp.Body.String() + htmlDoc := NewHTMLParser(t, resp.Body) + + selection := htmlDoc.doc.Find(".ui.dropdown.select-branch") + assert.Lenf(t, selection.Nodes, 2, "The template has changed") + assert.Contains(t, body, "These branches do not share a common merge base") + assert.Equal(t, 1, htmlDoc.doc.Find(`a.item[href="/user2/repo1/compare/master...unrelated-history"]`).Length()) + assert.Equal(t, 1, htmlDoc.doc.Find(`a.item[href="/user2/repo1/compare/master...master"]`).Length()) + assert.Equal(t, 0, htmlDoc.doc.Find(".pullrequest-form").Length()) +} + func TestCompareCodeExpand(t *testing.T) { onGiteaRun(t, func(t *testing.T, u *url.URL) { user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) From 10fb4c0a403cb83c1c401dac0ee685a950d955c5 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Tue, 28 Apr 2026 18:23:07 +0200 Subject: [PATCH 02/13] Address compare no merge base review feedback Co-Authored-By: Codex --- modules/gitrepo/compare_test.go | 3 ++- modules/gitrepo/merge.go | 6 ------ routers/web/repo/compare.go | 34 +++++++++++++++++++++----------- templates/repo/diff/compare.tmpl | 9 +++------ 4 files changed, 27 insertions(+), 25 deletions(-) diff --git a/modules/gitrepo/compare_test.go b/modules/gitrepo/compare_test.go index d77b52cae9a23..fe0aa4dc551b7 100644 --- a/modules/gitrepo/compare_test.go +++ b/modules/gitrepo/compare_test.go @@ -76,8 +76,9 @@ func TestMergeBaseNoCommonHistory(t *testing.T) { headCommit := commitRootTree(t, repoDir, "head.txt", "head", "head") mergeBase, err := MergeBase(t.Context(), &mockRepository{path: repoDir}, baseCommit, headCommit) + var noMergeBase ErrNoMergeBase assert.Empty(t, mergeBase) - assert.True(t, IsErrNoMergeBase(err), "expected no merge base error, got %v", err) + assert.ErrorAs(t, err, &noMergeBase) } func TestRepoGetDivergingCommits(t *testing.T) { diff --git a/modules/gitrepo/merge.go b/modules/gitrepo/merge.go index 3603481a48689..8b548bba9d8e7 100644 --- a/modules/gitrepo/merge.go +++ b/modules/gitrepo/merge.go @@ -5,7 +5,6 @@ package gitrepo import ( "context" - "errors" "fmt" "strings" @@ -18,11 +17,6 @@ type ErrNoMergeBase struct { Err error } -func IsErrNoMergeBase(err error) bool { - var noMergeBase ErrNoMergeBase - return errors.As(err, &noMergeBase) -} - func (err ErrNoMergeBase) Error() string { return fmt.Sprintf("get merge-base of %s and %s failed: %v", err.BaseCommitID, err.HeadCommitID, err.Err) } diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index 100e6d44cad04..9dce9ee53419b 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -413,10 +413,9 @@ func ParseCompareInfo(ctx *context.Context) *git_service.CompareInfo { compareInfo, err := git_service.GetCompareInfo(ctx, baseRepo, headRepo, headGitRepo, baseRef, headRef, compareReq.DirectComparison(), fileOnly) if err != nil { - if gitrepo.IsErrNoMergeBase(err) { + var noMergeBase gitrepo.ErrNoMergeBase + if errors.As(err, &noMergeBase) { ctx.Data["NoMergeBase"] = true - ctx.Data["BeforeCommitID"] = compareInfo.BaseCommitID - ctx.Data["AfterCommitID"] = compareInfo.HeadCommitID return &compareInfo } ctx.ServerError("GetCompareInfo", err) @@ -431,6 +430,13 @@ func ParseCompareInfo(ctx *context.Context) *git_service.CompareInfo { return compareInfo } +func prepareNoMergeBaseCompare(ctx *context.Context) { + ctx.Flash.Error(ctx.Tr("repo.pulls.no_common_history"), true) + ctx.Data["PageIsComparePull"] = false + ctx.Data["CommitCount"] = 0 + ctx.Data["HideCompareNothingMessage"] = true +} + func prepareNewPullRequestTitleContent(ci *git_service.CompareInfo, commits []*git_model.SignCommitWithStatuses) (title, content string) { title = ci.HeadRef.ShortName() @@ -480,12 +486,6 @@ func PrepareCompareDiff( ctx.Data["TitleQuery"] = newPrFormTitle ctx.Data["BodyQuery"] = newPrFormBody - if ctx.Data["NoMergeBase"] == true { - ctx.Data["CommitCount"] = 0 - ctx.Data["Commits"] = []*git_model.SignCommitWithStatuses{} - return true - } - if (headCommitID == ci.MergeBase && !ci.DirectComparison()) || headCommitID == ci.BaseCommitID { ctx.Data["IsNothingToCompare"] = true @@ -616,9 +616,14 @@ func CompareDiff(ctx *context.Context) { ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes ctx.Data["CompareInfo"] = ci - nothingToCompare := PrepareCompareDiff(ctx, ci, gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string))) - if ctx.Written() { - return + nothingToCompare := true + if ctx.Data["NoMergeBase"] == true { + prepareNoMergeBaseCompare(ctx) + } else { + nothingToCompare = PrepareCompareDiff(ctx, ci, gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string))) + if ctx.Written() { + return + } } baseTags, err := repo_model.GetTagNamesByRepoID(ctx, ctx.Repo.Repository.ID) @@ -658,6 +663,11 @@ func CompareDiff(ctx *context.Context) { } ctx.Data["HeadTags"] = headTags + if ctx.Data["NoMergeBase"] == true { + ctx.HTML(http.StatusOK, tplCompare) + return + } + if ctx.Data["PageIsComparePull"] == true { pr, err := issues_model.GetUnmergedPullRequest(ctx, ci.HeadRepo.ID, ctx.Repo.Repository.ID, ci.HeadRef.ShortName(), ci.BaseRef.ShortName(), issues_model.PullRequestFlowGithub) if err != nil { diff --git a/templates/repo/diff/compare.tmpl b/templates/repo/diff/compare.tmpl index 467cdbd548753..0a68d05bc3a7b 100644 --- a/templates/repo/diff/compare.tmpl +++ b/templates/repo/diff/compare.tmpl @@ -13,6 +13,7 @@ {{ctx.Locale.Tr "action.compare_commits_general"}} {{end}} + {{template "base/alert" .}} {{$BaseCompareName := $.Repository.FullName -}} {{$HeadCompareName := $.HeadRepo.FullName -}} {{$OwnForkCompareName := "" -}} @@ -168,11 +169,7 @@
{{$showDiffBox := and .CommitCount (not .IsNothingToCompare)}} - {{if .NoMergeBase}} -
- {{ctx.Locale.Tr "repo.pulls.no_common_history"}} -
- {{else if and .IsSigned .PageIsComparePull}} + {{if and .IsSigned .PageIsComparePull}} {{$allowCreatePR := and ($.CompareInfo.BaseRef.IsBranch) ($.CompareInfo.HeadRef.IsBranch) (not $.CompareInfo.DirectComparison) (or $.AllowEmptyPr (not .IsNothingToCompare))}} {{if .IsNothingToCompare}}
@@ -214,7 +211,7 @@
{{end}} {{else}}{{/* not singed-in or not for pull-request */}} - {{if not .CommitCount}} + {{if and (not .CommitCount) (not .HideCompareNothingMessage)}}
{{ctx.Locale.Tr "repo.commits.nothing_to_compare"}}
{{end}} {{end}} From af5e488a320bbf9bc86888c523ef55e1ebaca25c Mon Sep 17 00:00:00 2001 From: Nicolas Date: Tue, 28 Apr 2026 18:32:34 +0200 Subject: [PATCH 03/13] fixes --- routers/web/repo/compare.go | 43 +++++++++++++++----------------- routers/web/repo/pull.go | 2 +- templates/repo/diff/compare.tmpl | 2 +- 3 files changed, 22 insertions(+), 25 deletions(-) diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index 9dce9ee53419b..61801548ddb6a 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -190,7 +190,7 @@ func setCsvCompareContext(ctx *context.Context) { } // ParseCompareInfo parse compare info between two commit for preparing comparing references -func ParseCompareInfo(ctx *context.Context) *git_service.CompareInfo { +func ParseCompareInfo(ctx *context.Context) (*git_service.CompareInfo, bool) { baseRepo := ctx.Repo.Repository fileOnly := ctx.FormBool("file-only") @@ -200,7 +200,7 @@ func ParseCompareInfo(ctx *context.Context) *git_service.CompareInfo { // remove the check when we support compare with carets if compareReq.BaseOriRefSuffix != "" { ctx.HTTPError(http.StatusBadRequest, "Unsupported comparison syntax: ref with suffix") - return nil + return nil, false } // 2 get repository and owner for head @@ -208,13 +208,13 @@ func ParseCompareInfo(ctx *context.Context) *git_service.CompareInfo { switch { case errors.Is(err, util.ErrInvalidArgument): ctx.HTTPError(http.StatusBadRequest, err.Error()) - return nil + return nil, false case errors.Is(err, util.ErrNotExist): ctx.NotFound(nil) - return nil + return nil, false case err != nil: ctx.ServerError("GetHeadOwnerAndRepo", err) - return nil + return nil, false } isSameRepo := baseRepo.ID == headRepo.ID @@ -229,7 +229,7 @@ func ParseCompareInfo(ctx *context.Context) *git_service.CompareInfo { permHead, err := access_model.GetDoerRepoPermission(ctx, headRepo, ctx.Doer) if err != nil { ctx.ServerError("GetDoerRepoPermission", err) - return nil + return nil, false } if !permHead.CanRead(unit.TypeCode) { if log.IsTrace() { @@ -239,7 +239,7 @@ func ParseCompareInfo(ctx *context.Context) *git_service.CompareInfo { permHead) } ctx.NotFound(nil) - return nil + return nil, false } ctx.Data["CanWriteToHeadRepo"] = permHead.CanWrite(unit.TypeCode) } @@ -251,7 +251,7 @@ func ParseCompareInfo(ctx *context.Context) *git_service.CompareInfo { baseRef := ctx.Repo.GitRepo.UnstableGuessRefByShortName(baseRefName) if baseRef == "" { ctx.NotFound(nil) - return nil + return nil, false } var headGitRepo *git.Repository if isSameRepo { @@ -260,14 +260,14 @@ func ParseCompareInfo(ctx *context.Context) *git_service.CompareInfo { headGitRepo, err = gitrepo.OpenRepository(ctx, headRepo) if err != nil { ctx.ServerError("OpenRepository", err) - return nil + return nil, false } defer headGitRepo.Close() } headRef := headGitRepo.UnstableGuessRefByShortName(headRefName) if headRef == "" { ctx.NotFound(nil) - return nil + return nil, false } ctx.Data["BaseName"] = baseRepo.OwnerName @@ -294,7 +294,7 @@ func ParseCompareInfo(ctx *context.Context) *git_service.CompareInfo { if err != nil { if !repo_model.IsErrRepoNotExist(err) { ctx.ServerError("Unable to find root repo", err) - return nil + return nil, false } } else { rootRepo = baseRepo.BaseRepo @@ -362,7 +362,7 @@ func ParseCompareInfo(ctx *context.Context) *git_service.CompareInfo { branches, tags, err := getBranchesAndTagsForRepo(ctx, rootRepo) if err != nil { ctx.ServerError("GetBranchesForRepo", err) - return nil + return nil, false } ctx.Data["RootRepoBranches"] = branches @@ -387,7 +387,7 @@ func ParseCompareInfo(ctx *context.Context) *git_service.CompareInfo { branches, tags, err := getBranchesAndTagsForRepo(ctx, ownForkRepo) if err != nil { ctx.ServerError("GetBranchesForRepo", err) - return nil + return nil, false } ctx.Data["OwnForkRepoBranches"] = branches ctx.Data["OwnForkRepoTags"] = tags @@ -408,33 +408,30 @@ func ParseCompareInfo(ctx *context.Context) *git_service.CompareInfo { permBase) } ctx.NotFound(nil) - return nil + return nil, false } compareInfo, err := git_service.GetCompareInfo(ctx, baseRepo, headRepo, headGitRepo, baseRef, headRef, compareReq.DirectComparison(), fileOnly) if err != nil { var noMergeBase gitrepo.ErrNoMergeBase if errors.As(err, &noMergeBase) { - ctx.Data["NoMergeBase"] = true - return &compareInfo + return &compareInfo, true } ctx.ServerError("GetCompareInfo", err) - return nil + return nil, false } if compareReq.DirectComparison() { ctx.Data["BeforeCommitID"] = compareInfo.BaseCommitID } else { ctx.Data["BeforeCommitID"] = compareInfo.MergeBase } - - return compareInfo + return &compareInfo, false } func prepareNoMergeBaseCompare(ctx *context.Context) { ctx.Flash.Error(ctx.Tr("repo.pulls.no_common_history"), true) ctx.Data["PageIsComparePull"] = false ctx.Data["CommitCount"] = 0 - ctx.Data["HideCompareNothingMessage"] = true } func prepareNewPullRequestTitleContent(ci *git_service.CompareInfo, commits []*git_model.SignCommitWithStatuses) (title, content string) { @@ -607,7 +604,7 @@ func getBranchesAndTagsForRepo(ctx gocontext.Context, repo *repo_model.Repositor // CompareDiff show different from one commit to another commit func CompareDiff(ctx *context.Context) { - ci := ParseCompareInfo(ctx) + ci, noMergeBase := ParseCompareInfo(ctx) if ctx.Written() { return } @@ -617,7 +614,7 @@ func CompareDiff(ctx *context.Context) { ctx.Data["CompareInfo"] = ci nothingToCompare := true - if ctx.Data["NoMergeBase"] == true { + if noMergeBase { prepareNoMergeBaseCompare(ctx) } else { nothingToCompare = PrepareCompareDiff(ctx, ci, gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string))) @@ -663,7 +660,7 @@ func CompareDiff(ctx *context.Context) { } ctx.Data["HeadTags"] = headTags - if ctx.Data["NoMergeBase"] == true { + if noMergeBase { ctx.HTML(http.StatusOK, tplCompare) return } diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index e312fc9d2a816..1c301ad6da752 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -1362,7 +1362,7 @@ func CompareAndPullRequestPost(ctx *context.Context) { attachments []string ) - ci := ParseCompareInfo(ctx) + ci, _ := ParseCompareInfo(ctx) if ctx.Written() { return } diff --git a/templates/repo/diff/compare.tmpl b/templates/repo/diff/compare.tmpl index 0a68d05bc3a7b..9ed4b73174d3b 100644 --- a/templates/repo/diff/compare.tmpl +++ b/templates/repo/diff/compare.tmpl @@ -211,7 +211,7 @@ {{end}} {{else}}{{/* not singed-in or not for pull-request */}} - {{if and (not .CommitCount) (not .HideCompareNothingMessage)}} + {{if not .CommitCount}}
{{ctx.Locale.Tr "repo.commits.nothing_to_compare"}}
{{end}} {{end}} From d9ec35d8466f8e8ca249c9e5950f108d1f0a764a Mon Sep 17 00:00:00 2001 From: Nicolas Date: Tue, 28 Apr 2026 18:36:56 +0200 Subject: [PATCH 04/13] simplify --- routers/web/repo/compare.go | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index 61801548ddb6a..c9109d194f000 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -428,12 +428,6 @@ func ParseCompareInfo(ctx *context.Context) (*git_service.CompareInfo, bool) { return &compareInfo, false } -func prepareNoMergeBaseCompare(ctx *context.Context) { - ctx.Flash.Error(ctx.Tr("repo.pulls.no_common_history"), true) - ctx.Data["PageIsComparePull"] = false - ctx.Data["CommitCount"] = 0 -} - func prepareNewPullRequestTitleContent(ci *git_service.CompareInfo, commits []*git_model.SignCommitWithStatuses) (title, content string) { title = ci.HeadRef.ShortName() @@ -615,7 +609,9 @@ func CompareDiff(ctx *context.Context) { nothingToCompare := true if noMergeBase { - prepareNoMergeBaseCompare(ctx) + ctx.Flash.Error(ctx.Tr("repo.pulls.no_common_history"), true) + ctx.Data["PageIsComparePull"] = false + ctx.Data["CommitCount"] = 0 } else { nothingToCompare = PrepareCompareDiff(ctx, ci, gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string))) if ctx.Written() { @@ -636,16 +632,13 @@ func CompareDiff(ctx *context.Context) { return } - headBranches, err := git_model.FindBranchNames(ctx, git_model.FindBranchOptions{ - RepoID: ci.HeadRepo.ID, - ListOptions: db.ListOptionsAll, - IsDeletedBranch: optional.Some(false), - }) + headBranches, headTags, err := getBranchesAndTagsForRepo(ctx, ci.HeadRepo) if err != nil { - ctx.ServerError("GetBranches", err) + ctx.ServerError("GetBranchesAndTagsForRepo", err) return } ctx.Data["HeadBranches"] = headBranches + ctx.Data["HeadTags"] = headTags // For compare repo branches PrepareBranchList(ctx) @@ -653,13 +646,6 @@ func CompareDiff(ctx *context.Context) { return } - headTags, err := repo_model.GetTagNamesByRepoID(ctx, ci.HeadRepo.ID) - if err != nil { - ctx.ServerError("GetTagNamesByRepoID", err) - return - } - ctx.Data["HeadTags"] = headTags - if noMergeBase { ctx.HTML(http.StatusOK, tplCompare) return From 53b08fcd7b1938a2de2e0ecc1e0b498543c61537 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Tue, 28 Apr 2026 23:24:04 +0200 Subject: [PATCH 05/13] fix --- routers/web/repo/compare.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index c9109d194f000..123421cc1b15e 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -415,7 +415,7 @@ func ParseCompareInfo(ctx *context.Context) (*git_service.CompareInfo, bool) { if err != nil { var noMergeBase gitrepo.ErrNoMergeBase if errors.As(err, &noMergeBase) { - return &compareInfo, true + return compareInfo, true } ctx.ServerError("GetCompareInfo", err) return nil, false @@ -425,7 +425,7 @@ func ParseCompareInfo(ctx *context.Context) (*git_service.CompareInfo, bool) { } else { ctx.Data["BeforeCommitID"] = compareInfo.MergeBase } - return &compareInfo, false + return compareInfo, false } func prepareNewPullRequestTitleContent(ci *git_service.CompareInfo, commits []*git_model.SignCommitWithStatuses) (title, content string) { From 331c797467748a9f150f4cada76d3d84109b78f3 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Fri, 8 May 2026 17:01:42 +0200 Subject: [PATCH 06/13] cleanup --- routers/web/repo/compare.go | 38 +++++++++++++++++++------------------ routers/web/repo/pull.go | 2 +- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index e4f96f93564a4..f075be76c9f19 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -190,7 +190,7 @@ func setCsvCompareContext(ctx *context.Context) { } // ParseCompareInfo parse compare info between two commit for preparing comparing references -func ParseCompareInfo(ctx *context.Context) (*git_service.CompareInfo, bool) { +func ParseCompareInfo(ctx *context.Context) *git_service.CompareInfo { baseRepo := ctx.Repo.Repository fileOnly := ctx.FormBool("file-only") @@ -200,7 +200,7 @@ func ParseCompareInfo(ctx *context.Context) (*git_service.CompareInfo, bool) { // remove the check when we support compare with carets if compareReq.BaseOriRefSuffix != "" { ctx.HTTPError(http.StatusBadRequest, "Unsupported comparison syntax: ref with suffix") - return nil, false + return nil } // 2 get repository and owner for head @@ -208,13 +208,13 @@ func ParseCompareInfo(ctx *context.Context) (*git_service.CompareInfo, bool) { switch { case errors.Is(err, util.ErrInvalidArgument): ctx.HTTPError(http.StatusBadRequest, err.Error()) - return nil, false + return nil case errors.Is(err, util.ErrNotExist): ctx.NotFound(nil) - return nil, false + return nil case err != nil: ctx.ServerError("GetHeadOwnerAndRepo", err) - return nil, false + return nil } isSameRepo := baseRepo.ID == headRepo.ID @@ -229,7 +229,7 @@ func ParseCompareInfo(ctx *context.Context) (*git_service.CompareInfo, bool) { permHead, err := access_model.GetDoerRepoPermission(ctx, headRepo, ctx.Doer) if err != nil { ctx.ServerError("GetDoerRepoPermission", err) - return nil, false + return nil } if !permHead.CanRead(unit.TypeCode) { if log.IsTrace() { @@ -239,7 +239,7 @@ func ParseCompareInfo(ctx *context.Context) (*git_service.CompareInfo, bool) { permHead) } ctx.NotFound(nil) - return nil, false + return nil } ctx.Data["CanWriteToHeadRepo"] = permHead.CanWrite(unit.TypeCode) } @@ -251,7 +251,7 @@ func ParseCompareInfo(ctx *context.Context) (*git_service.CompareInfo, bool) { baseRef := ctx.Repo.GitRepo.UnstableGuessRefByShortName(baseRefName) if baseRef == "" { ctx.NotFound(nil) - return nil, false + return nil } var headGitRepo *git.Repository if isSameRepo { @@ -260,14 +260,14 @@ func ParseCompareInfo(ctx *context.Context) (*git_service.CompareInfo, bool) { headGitRepo, err = gitrepo.OpenRepository(ctx, headRepo) if err != nil { ctx.ServerError("OpenRepository", err) - return nil, false + return nil } defer headGitRepo.Close() } headRef := headGitRepo.UnstableGuessRefByShortName(headRefName) if headRef == "" { ctx.NotFound(nil) - return nil, false + return nil } ctx.Data["BaseName"] = baseRepo.OwnerName @@ -294,7 +294,7 @@ func ParseCompareInfo(ctx *context.Context) (*git_service.CompareInfo, bool) { if err != nil { if !repo_model.IsErrRepoNotExist(err) { ctx.ServerError("Unable to find root repo", err) - return nil, false + return nil } } else { rootRepo = baseRepo.BaseRepo @@ -362,7 +362,7 @@ func ParseCompareInfo(ctx *context.Context) (*git_service.CompareInfo, bool) { branches, tags, err := getBranchesAndTagsForRepo(ctx, rootRepo) if err != nil { ctx.ServerError("GetBranchesForRepo", err) - return nil, false + return nil } ctx.Data["RootRepoBranches"] = branches @@ -387,7 +387,7 @@ func ParseCompareInfo(ctx *context.Context) (*git_service.CompareInfo, bool) { branches, tags, err := getBranchesAndTagsForRepo(ctx, ownForkRepo) if err != nil { ctx.ServerError("GetBranchesForRepo", err) - return nil, false + return nil } ctx.Data["OwnForkRepoBranches"] = branches ctx.Data["OwnForkRepoTags"] = tags @@ -408,24 +408,25 @@ func ParseCompareInfo(ctx *context.Context) (*git_service.CompareInfo, bool) { permBase) } ctx.NotFound(nil) - return nil, false + return nil } compareInfo, err := git_service.GetCompareInfo(ctx, baseRepo, headRepo, headGitRepo, baseRef, headRef, compareReq.DirectComparison(), fileOnly) if err != nil { var noMergeBase gitrepo.ErrNoMergeBase if errors.As(err, &noMergeBase) { - return compareInfo, true + ctx.Data["IsNoMergeBase"] = true + return compareInfo } ctx.ServerError("GetCompareInfo", err) - return nil, false + return nil } if compareReq.DirectComparison() { ctx.Data["BeforeCommitID"] = compareInfo.BaseCommitID } else { ctx.Data["BeforeCommitID"] = compareInfo.MergeBase } - return compareInfo, false + return compareInfo } func prepareNewPullRequestTitleContent(ci *git_service.CompareInfo, commits []*git_model.SignCommitWithStatuses) (title, content string) { @@ -597,7 +598,7 @@ func getBranchesAndTagsForRepo(ctx gocontext.Context, repo *repo_model.Repositor // CompareDiff show different from one commit to another commit func CompareDiff(ctx *context.Context) { - ci, noMergeBase := ParseCompareInfo(ctx) + ci := ParseCompareInfo(ctx) if ctx.Written() { return } @@ -606,6 +607,7 @@ func CompareDiff(ctx *context.Context) { ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes ctx.Data["CompareInfo"] = ci + noMergeBase, _ := ctx.Data["IsNoMergeBase"].(bool) nothingToCompare := true if noMergeBase { ctx.Flash.Error(ctx.Tr("repo.pulls.no_common_history"), true) diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index b6f40f049ae64..3933da8764e2c 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -1362,7 +1362,7 @@ func CompareAndPullRequestPost(ctx *context.Context) { attachments []string ) - ci, _ := ParseCompareInfo(ctx) + ci := ParseCompareInfo(ctx) if ctx.Written() { return } From 7a79e457a329791f4f47f41734e83d3c1867b8be Mon Sep 17 00:00:00 2001 From: Nicolas Date: Fri, 8 May 2026 17:06:14 +0200 Subject: [PATCH 07/13] fixes from review --- routers/web/repo/pull.go | 4 ++++ services/git/compare.go | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index 3933da8764e2c..efdb7162f4936 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -1366,6 +1366,10 @@ func CompareAndPullRequestPost(ctx *context.Context) { if ctx.Written() { return } + if noMergeBase, _ := ctx.Data["IsNoMergeBase"].(bool); noMergeBase { + ctx.JSONError(ctx.Tr("repo.pulls.no_common_history")) + return + } validateRet := ValidateRepoMetasForNewIssue(ctx, *form, true) if ctx.Written() { diff --git a/services/git/compare.go b/services/git/compare.go index 251a03505859a..c6687ec22a6cf 100644 --- a/services/git/compare.go +++ b/services/git/compare.go @@ -5,6 +5,7 @@ package git import ( "context" + "errors" "fmt" repo_model "code.gitea.io/gitea/models/repo" @@ -76,6 +77,10 @@ func GetCompareInfo(ctx context.Context, baseRepo, headRepo *repo_model.Reposito if !directComparison { compareInfo.MergeBase, err = gitrepo.MergeBase(ctx, headRepo, compareInfo.BaseCommitID, compareInfo.HeadCommitID) if err != nil { + var noMergeBase gitrepo.ErrNoMergeBase + if errors.As(err, &noMergeBase) { + return compareInfo, err + } return nil, fmt.Errorf("MergeBase: %w", err) } } else { From 13a362764c498e802a661d5be8752901e43238f0 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 8 May 2026 23:09:49 +0800 Subject: [PATCH 08/13] fix --- modules/gitrepo/merge.go | 21 ++------------------- routers/web/repo/compare.go | 3 +-- 2 files changed, 3 insertions(+), 21 deletions(-) diff --git a/modules/gitrepo/merge.go b/modules/gitrepo/merge.go index 8b548bba9d8e7..92803da6fde61 100644 --- a/modules/gitrepo/merge.go +++ b/modules/gitrepo/merge.go @@ -9,33 +9,16 @@ import ( "strings" "code.gitea.io/gitea/modules/git/gitcmd" + "code.gitea.io/gitea/modules/util" ) -type ErrNoMergeBase struct { - BaseCommitID string - HeadCommitID string - Err error -} - -func (err ErrNoMergeBase) Error() string { - return fmt.Sprintf("get merge-base of %s and %s failed: %v", err.BaseCommitID, err.HeadCommitID, err.Err) -} - -func (err ErrNoMergeBase) Unwrap() error { - return err.Err -} - // MergeBase checks and returns merge base of two commits. func MergeBase(ctx context.Context, repo Repository, baseCommitID, headCommitID string) (string, error) { mergeBase, _, err := RunCmdString(ctx, repo, gitcmd.NewCommand("merge-base"). AddDashesAndList(baseCommitID, headCommitID)) if err != nil { if gitcmd.IsErrorExitCode(err, 1) { - return "", ErrNoMergeBase{ - BaseCommitID: baseCommitID, - HeadCommitID: headCommitID, - Err: err, - } + return "", util.NewNotExistErrorf("get merge-base of %s and %s failed", baseCommitID, headCommitID) } return "", fmt.Errorf("get merge-base of %s and %s failed: %w", baseCommitID, headCommitID, err) } diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index f075be76c9f19..2bba4b5fa9474 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -413,8 +413,7 @@ func ParseCompareInfo(ctx *context.Context) *git_service.CompareInfo { compareInfo, err := git_service.GetCompareInfo(ctx, baseRepo, headRepo, headGitRepo, baseRef, headRef, compareReq.DirectComparison(), fileOnly) if err != nil { - var noMergeBase gitrepo.ErrNoMergeBase - if errors.As(err, &noMergeBase) { + if errors.Is(err, util.ErrNotEmpty) { ctx.Data["IsNoMergeBase"] = true return compareInfo } From 643bdc8a533eb6e3c603be3b0983abb832220bb6 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 8 May 2026 23:12:11 +0800 Subject: [PATCH 09/13] fix --- modules/gitrepo/compare_test.go | 4 ++-- routers/web/repo/compare.go | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/gitrepo/compare_test.go b/modules/gitrepo/compare_test.go index fe0aa4dc551b7..3f7cc4f6a7e98 100644 --- a/modules/gitrepo/compare_test.go +++ b/modules/gitrepo/compare_test.go @@ -12,6 +12,7 @@ import ( "time" "code.gitea.io/gitea/modules/git/gitcmd" + "code.gitea.io/gitea/modules/util" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -76,9 +77,8 @@ func TestMergeBaseNoCommonHistory(t *testing.T) { headCommit := commitRootTree(t, repoDir, "head.txt", "head", "head") mergeBase, err := MergeBase(t.Context(), &mockRepository{path: repoDir}, baseCommit, headCommit) - var noMergeBase ErrNoMergeBase assert.Empty(t, mergeBase) - assert.ErrorAs(t, err, &noMergeBase) + assert.ErrorIs(t, err, util.ErrNotExist) } func TestRepoGetDivergingCommits(t *testing.T) { diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index 2bba4b5fa9474..51e3e7934dc8b 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -607,11 +607,12 @@ func CompareDiff(ctx *context.Context) { ctx.Data["CompareInfo"] = ci noMergeBase, _ := ctx.Data["IsNoMergeBase"].(bool) - nothingToCompare := true + var nothingToCompare bool if noMergeBase { ctx.Flash.Error(ctx.Tr("repo.pulls.no_common_history"), true) ctx.Data["PageIsComparePull"] = false ctx.Data["CommitCount"] = 0 + nothingToCompare = true } else { nothingToCompare = PrepareCompareDiff(ctx, ci, gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string))) if ctx.Written() { From 3c671cbeee6aa50a1bb4789e708e0854c0ce7f1d Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 8 May 2026 23:13:15 +0800 Subject: [PATCH 10/13] fix --- routers/web/repo/compare.go | 2 +- services/pull/pull.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index 51e3e7934dc8b..1250b9578e168 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -413,7 +413,7 @@ func ParseCompareInfo(ctx *context.Context) *git_service.CompareInfo { compareInfo, err := git_service.GetCompareInfo(ctx, baseRepo, headRepo, headGitRepo, baseRef, headRef, compareReq.DirectComparison(), fileOnly) if err != nil { - if errors.Is(err, util.ErrNotEmpty) { + if errors.Is(err, util.ErrNotExist) { ctx.Data["IsNoMergeBase"] = true return compareInfo } diff --git a/services/pull/pull.go b/services/pull/pull.go index 34ca967646f54..90581b869c892 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -530,7 +530,7 @@ func checkIfPRContentChanged(ctx context.Context, pr *issues_model.PullRequest, return util.IsEmptyReader(stdoutReader) }). RunWithStderr(ctx); err != nil { - if errors.Is(err, util.ErrNotEmpty) { + if errors.Is(err, util.ErrNotExist) { return true, mergeBase, nil } From 5c049e71527d6d6bdc55c18cf14a1ba41e715d88 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 8 May 2026 23:16:57 +0800 Subject: [PATCH 11/13] fix --- routers/web/repo/compare.go | 1 + services/git/compare.go | 7 +------ services/pull/pull.go | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index 1250b9578e168..155d40a543bb2 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -425,6 +425,7 @@ func ParseCompareInfo(ctx *context.Context) *git_service.CompareInfo { } else { ctx.Data["BeforeCommitID"] = compareInfo.MergeBase } + return compareInfo } diff --git a/services/git/compare.go b/services/git/compare.go index c6687ec22a6cf..71bacfb6bda30 100644 --- a/services/git/compare.go +++ b/services/git/compare.go @@ -5,7 +5,6 @@ package git import ( "context" - "errors" "fmt" repo_model "code.gitea.io/gitea/models/repo" @@ -77,11 +76,7 @@ func GetCompareInfo(ctx context.Context, baseRepo, headRepo *repo_model.Reposito if !directComparison { compareInfo.MergeBase, err = gitrepo.MergeBase(ctx, headRepo, compareInfo.BaseCommitID, compareInfo.HeadCommitID) if err != nil { - var noMergeBase gitrepo.ErrNoMergeBase - if errors.As(err, &noMergeBase) { - return compareInfo, err - } - return nil, fmt.Errorf("MergeBase: %w", err) + return compareInfo, fmt.Errorf("MergeBase: %w", err) } } else { compareInfo.MergeBase = compareInfo.BaseCommitID diff --git a/services/pull/pull.go b/services/pull/pull.go index 90581b869c892..34ca967646f54 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -530,7 +530,7 @@ func checkIfPRContentChanged(ctx context.Context, pr *issues_model.PullRequest, return util.IsEmptyReader(stdoutReader) }). RunWithStderr(ctx); err != nil { - if errors.Is(err, util.ErrNotExist) { + if errors.Is(err, util.ErrNotEmpty) { return true, mergeBase, nil } From 3c6f2b8b628d606415cd42aa22c4d413dc0ab856 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 8 May 2026 23:21:46 +0800 Subject: [PATCH 12/13] fix --- routers/web/repo/compare.go | 3 +-- routers/web/repo/pull.go | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index 155d40a543bb2..d3cd1e1909ba9 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -607,9 +607,8 @@ func CompareDiff(ctx *context.Context) { ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes ctx.Data["CompareInfo"] = ci - noMergeBase, _ := ctx.Data["IsNoMergeBase"].(bool) var nothingToCompare bool - if noMergeBase { + if ctx.Data["IsNoMergeBase"] == true { ctx.Flash.Error(ctx.Tr("repo.pulls.no_common_history"), true) ctx.Data["PageIsComparePull"] = false ctx.Data["CommitCount"] = 0 diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index efdb7162f4936..aa3e6b02b111f 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -1366,7 +1366,7 @@ func CompareAndPullRequestPost(ctx *context.Context) { if ctx.Written() { return } - if noMergeBase, _ := ctx.Data["IsNoMergeBase"].(bool); noMergeBase { + if ctx.Data["IsNoMergeBase"] == true { ctx.JSONError(ctx.Tr("repo.pulls.no_common_history")) return } From f85b5a8271c00d0275ebb797eff610533f787f47 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 8 May 2026 23:25:11 +0800 Subject: [PATCH 13/13] fix --- routers/web/repo/compare.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index d3cd1e1909ba9..3f004cee88d11 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -608,7 +608,8 @@ func CompareDiff(ctx *context.Context) { ctx.Data["CompareInfo"] = ci var nothingToCompare bool - if ctx.Data["IsNoMergeBase"] == true { + noMergeBase := ctx.Data["IsNoMergeBase"] == true + if noMergeBase { ctx.Flash.Error(ctx.Tr("repo.pulls.no_common_history"), true) ctx.Data["PageIsComparePull"] = false ctx.Data["CommitCount"] = 0