{{template "repo/header" .}} + {{$compareSeparator := $.CompareInfo.CompareSeparator}} + {{$compareSeparatorSwitch := Iif $.CompareInfo.DirectComparison "..." ".."}}

{{if and $.PageIsComparePull $.IsSigned (not .Repository.IsArchived)}} @@ -29,7 +31,7 @@ {{- end -}}
- {{svg "octicon-git-compare"}} + {{svg "octicon-git-compare"}}
- {{svg "octicon-arrow-left" 16}}
{{.CompareSeparator}}
+ {{svg "octicon-arrow-left" 16}}
{{$compareSeparator}}
{{else if $allowCreatePR}} -
- - {{ctx.Locale.Tr "repo.pulls.new.description"}} - +
+ {{ctx.Locale.Tr "repo.pulls.new.description"}} {{ctx.Locale.Tr "repo.pulls.new"}}
From 05f431025eee440218fcb0e57b857a172b248601 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Tue, 30 Dec 2025 12:53:33 +0800 Subject: [PATCH 09/11] fixme --- routers/common/compare.go | 2 ++ services/git/compare.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/routers/common/compare.go b/routers/common/compare.go index c99686a5fe9ea..30bfb7ec60bde 100644 --- a/routers/common/compare.go +++ b/routers/common/compare.go @@ -21,6 +21,8 @@ type CompareRouterReq struct { } func (cr *CompareRouterReq) DirectComparison() bool { + // FIXME: the design of "DirectComparison" is wrong, it loses the information of `^` + // To correctly handle the comparison, developers should use `ci.CompareSeparator` directly, all "DirectComparison" related code should be rewritten. return cr.CompareSeparator == ".." } diff --git a/services/git/compare.go b/services/git/compare.go index 711eb3db121a4..ee397fe947714 100644 --- a/services/git/compare.go +++ b/services/git/compare.go @@ -41,6 +41,8 @@ func (ci *CompareInfo) IsSameRef() bool { } func (ci *CompareInfo) DirectComparison() bool { + // FIXME: the design of "DirectComparison" is wrong, it loses the information of `^` + // To correctly handle the comparison, developers should use `ci.CompareSeparator` directly, all "DirectComparison" related code should be rewritten. return ci.CompareSeparator == ".." } From 63416501652d869b81fe9f8cf14ed9c361005c71 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Tue, 30 Dec 2025 15:13:30 +0800 Subject: [PATCH 10/11] https://git-scm.com/docs/gitrevisions --- modules/git/ref.go | 11 +++++++++++ routers/api/v1/repo/pull.go | 4 ++-- routers/common/compare.go | 28 +++++++++++----------------- routers/common/compare_test.go | 21 ++++++++++++--------- routers/web/repo/compare.go | 4 ++-- 5 files changed, 38 insertions(+), 30 deletions(-) diff --git a/modules/git/ref.go b/modules/git/ref.go index 56b2db858ad63..7b63d06b3885b 100644 --- a/modules/git/ref.go +++ b/modules/git/ref.go @@ -220,3 +220,14 @@ func (ref RefName) RefWebLinkPath() string { } return string(refType) + "/" + util.PathEscapeSegments(ref.ShortName()) } + +func ParseRefSuffix(ref string) (string, string) { + // Partially support https://git-scm.com/docs/gitrevisions + if idx := strings.Index(ref, "@{"); idx != -1 { + return ref[:idx], ref[idx:] + } + if idx := strings.Index(ref, "^"); idx != -1 { + return ref[:idx], ref[idx:] + } + return ref, "" +} diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index 09fcac1a63004..9b1eb3fc85fca 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -1065,8 +1065,8 @@ func parseCompareInfo(ctx *context.APIContext, compareParam string) (result *git compareReq := common.ParseCompareRouterParam(compareParam) // remove the check when we support compare with carets - if compareReq.CaretTimes() > 0 { - ctx.APIError(http.StatusBadRequest, "Unsupported compare syntax with carets") + if compareReq.BaseOriRefSuffix != "" { + ctx.APIError(http.StatusBadRequest, "Unsupported comparison syntax: ref with suffix") return nil, nil } diff --git a/routers/common/compare.go b/routers/common/compare.go index 30bfb7ec60bde..5b6fdba4e040e 100644 --- a/routers/common/compare.go +++ b/routers/common/compare.go @@ -9,15 +9,19 @@ import ( repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/util" ) type CompareRouterReq struct { BaseOriRef string - HeadOwner string - HeadRepoName string - HeadOriRef string + BaseOriRefSuffix string + CompareSeparator string + + HeadOwner string + HeadRepoName string + HeadOriRef string } func (cr *CompareRouterReq) DirectComparison() bool { @@ -26,10 +30,6 @@ func (cr *CompareRouterReq) DirectComparison() bool { return cr.CompareSeparator == ".." } -func (cr *CompareRouterReq) CaretTimes() int { - return strings.Count(cr.CompareSeparator, "^") -} - func parseHead(head string) (headOwnerName, headRepoName, headRef string) { paths := strings.SplitN(head, ":", 2) if len(paths) == 1 { @@ -89,16 +89,10 @@ func ParseCompareRouterParam(routerParam string) *CompareRouterReq { } } - baseRef := strings.TrimRight(basePart, "^") - basePartCaret := strings.TrimPrefix(basePart, baseRef) - headOwnerName, headRepoName, headRef := parseHead(headPart) - return &CompareRouterReq{ - BaseOriRef: baseRef, - HeadOriRef: headRef, - HeadOwner: headOwnerName, - HeadRepoName: headRepoName, - CompareSeparator: basePartCaret + sep, - } + ci := &CompareRouterReq{CompareSeparator: sep} + ci.BaseOriRef, ci.BaseOriRefSuffix = git.ParseRefSuffix(basePart) + ci.HeadOwner, ci.HeadRepoName, ci.HeadOriRef = parseHead(headPart) + return ci } // maxForkTraverseLevel defines the maximum levels to traverse when searching for the head repository. diff --git a/routers/common/compare_test.go b/routers/common/compare_test.go index 2ddafec22fa26..e4e24a03cfa09 100644 --- a/routers/common/compare_test.go +++ b/routers/common/compare_test.go @@ -22,76 +22,79 @@ func TestCompareRouterReq(t *testing.T) { input: "v1.0...v1.1", CompareRouterReq: &CompareRouterReq{ BaseOriRef: "v1.0", - HeadOriRef: "v1.1", CompareSeparator: "...", + HeadOriRef: "v1.1", }, }, { input: "main..develop", CompareRouterReq: &CompareRouterReq{ BaseOriRef: "main", - HeadOriRef: "develop", CompareSeparator: "..", + HeadOriRef: "develop", }, }, { input: "main^...develop", CompareRouterReq: &CompareRouterReq{ BaseOriRef: "main", + BaseOriRefSuffix: "^", + CompareSeparator: "...", HeadOriRef: "develop", - CompareSeparator: "^...", }, }, { input: "main^^^^^...develop", CompareRouterReq: &CompareRouterReq{ BaseOriRef: "main", + BaseOriRefSuffix: "^^^^^", + CompareSeparator: "...", HeadOriRef: "develop", - CompareSeparator: "^^^^^...", }, }, { input: "develop", CompareRouterReq: &CompareRouterReq{ - HeadOriRef: "develop", CompareSeparator: "...", + HeadOriRef: "develop", }, }, { input: "teabot:feature1", CompareRouterReq: &CompareRouterReq{ + CompareSeparator: "...", HeadOwner: "teabot", HeadOriRef: "feature1", - CompareSeparator: "...", }, }, { input: "lunny/forked_repo:develop", CompareRouterReq: &CompareRouterReq{ + CompareSeparator: "...", HeadOwner: "lunny", HeadRepoName: "forked_repo", HeadOriRef: "develop", - CompareSeparator: "...", }, }, { input: "main...lunny/forked_repo:develop", CompareRouterReq: &CompareRouterReq{ BaseOriRef: "main", + CompareSeparator: "...", HeadOwner: "lunny", HeadRepoName: "forked_repo", HeadOriRef: "develop", - CompareSeparator: "...", }, }, { input: "main^...lunny/forked_repo:develop", CompareRouterReq: &CompareRouterReq{ BaseOriRef: "main", + BaseOriRefSuffix: "^", + CompareSeparator: "...", HeadOwner: "lunny", HeadRepoName: "forked_repo", HeadOriRef: "develop", - CompareSeparator: "^...", }, }, } diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index ae0c55656f430..bbe1ed3b5e6f0 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -200,8 +200,8 @@ func ParseCompareInfo(ctx *context.Context) *git_service.CompareInfo { compareReq := common.ParseCompareRouterParam(ctx.PathParam("*")) // remove the check when we support compare with carets - if compareReq.CaretTimes() > 0 { - ctx.HTTPError(http.StatusBadRequest, "Unsupported compare syntax with carets") + if compareReq.BaseOriRefSuffix != "" { + ctx.HTTPError(http.StatusBadRequest, "Unsupported comparison syntax: ref with suffix") return nil } From e5a002bea418ccedbfad65fa860326e3c201a7a0 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 30 Dec 2025 21:18:56 -0800 Subject: [PATCH 11/11] Revert wrong change --- options/locale/locale_en-US.json | 1 + 1 file changed, 1 insertion(+) diff --git a/options/locale/locale_en-US.json b/options/locale/locale_en-US.json index 9c411d75206d5..480aafe87951c 100644 --- a/options/locale/locale_en-US.json +++ b/options/locale/locale_en-US.json @@ -1746,6 +1746,7 @@ "repo.pulls.new.already_existed": "A pull request between these branches already exists", "repo.pulls.edit.already_changed": "Unable to save changes to the pull request. It appears the content has already been changed by another user. Please refresh the page and try editing again to avoid overwriting their changes.", "repo.pulls.view": "View Pull Request", + "repo.pulls.compare_changes": "New Pull Request", "repo.pulls.allow_edits_from_maintainers": "Allow edits from maintainers", "repo.pulls.allow_edits_from_maintainers_desc": "Users with write access to the base branch can also push to this branch", "repo.pulls.allow_edits_from_maintainers_err": "Updating failed",