Skip to content

Commit 66a9ef9

Browse files
Fix ref links in issue overviews for tags (#8742)
* Properly generate ref URLs Tags used to not generate correct URLs (src/branch/tags/1.0.0 instead of src/tags/1.0.0). Also cleans up some code around it with the created helper functions. * Fix formatting and create migration * Add copyright head to utils_test * Use a raw query for the ref migration * Remove semicolon * Quote column and table names in migration SQL * Change || to CONCAT, since MSSQL does not support || * Make migration engine aware * Add missing import * Move ref EndName and URL to the issue service * Fix tests * Add test for commit refs * Update issue.go * Use the right command for building JavaScript bundles * Prepare for merge * Check for refs/* before prepending in migration * Update services/issue/issue_test.go * Update modules/git/utils_test.go Co-authored-by: techknowlogick <[email protected]> Co-authored-by: techknowlogick <[email protected]>
1 parent 591ca03 commit 66a9ef9

File tree

14 files changed

+139
-21
lines changed

14 files changed

+139
-21
lines changed

models/migrations/migrations.go

+2
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,8 @@ var migrations = []Migration{
210210
NewMigration("Add Branch Protection Block Outdated Branch", addBlockOnOutdatedBranch),
211211
// v138 -> v139
212212
NewMigration("Add ResolveDoerID to Comment table", addResolveDoerIDCommentColumn),
213+
// v139 -> v140
214+
NewMigration("prepend refs/heads/ to issue refs", prependRefsHeadsToIssueRefs),
213215
}
214216

215217
// GetCurrentDBVersion returns the current db version

models/migrations/v139.go

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2019 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package migrations
6+
7+
import (
8+
"code.gitea.io/gitea/modules/setting"
9+
10+
"xorm.io/xorm"
11+
)
12+
13+
func prependRefsHeadsToIssueRefs(x *xorm.Engine) error {
14+
var query string
15+
16+
switch {
17+
case setting.Database.UseMSSQL:
18+
query = "UPDATE `issue` SET `ref` = 'refs/heads/' + `ref` WHERE `ref` IS NOT NULL AND `ref` <> '' AND `ref` NOT LIKE 'refs/%'"
19+
default:
20+
query = "UPDATE `issue` SET `ref` = 'refs/heads/' || `ref` WHERE `ref` IS NOT NULL AND `ref` <> '' AND `ref` NOT LIKE 'refs/%'"
21+
}
22+
23+
_, err := x.Exec(query)
24+
return err
25+
}

modules/git/utils.go

+13
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,19 @@ func RefEndName(refStr string) string {
8888
return refStr
8989
}
9090

91+
// RefURL returns the absolute URL for a ref in a repository
92+
func RefURL(repoURL, ref string) string {
93+
refName := RefEndName(ref)
94+
switch {
95+
case strings.HasPrefix(ref, BranchPrefix):
96+
return repoURL + "/src/branch/" + refName
97+
case strings.HasPrefix(ref, TagPrefix):
98+
return repoURL + "/src/tag/" + refName
99+
default:
100+
return repoURL + "/src/commit/" + refName
101+
}
102+
}
103+
91104
// SplitRefName splits a full refname to reftype and simple refname
92105
func SplitRefName(refStr string) (string, string) {
93106
if strings.HasPrefix(refStr, BranchPrefix) {

modules/git/utils_test.go

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2020 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package git
6+
7+
import (
8+
"testing"
9+
10+
"github.com/stretchr/testify/assert"
11+
)
12+
13+
func TestRefEndName(t *testing.T) {
14+
// Test branch names (with and without slash).
15+
assert.Equal(t, "foo", RefEndName("refs/heads/foo"))
16+
assert.Equal(t, "feature/foo", RefEndName("refs/heads/feature/foo"))
17+
18+
// Test tag names (with and without slash).
19+
assert.Equal(t, "foo", RefEndName("refs/tags/foo"))
20+
assert.Equal(t, "release/foo", RefEndName("refs/tags/release/foo"))
21+
22+
// Test commit hashes.
23+
assert.Equal(t, "c0ffee", RefEndName("c0ffee"))
24+
}
25+
26+
func TestRefURL(t *testing.T) {
27+
repoURL := "/user/repo"
28+
assert.Equal(t, repoURL+"/src/branch/foo", RefURL(repoURL, "refs/heads/foo"))
29+
assert.Equal(t, repoURL+"/src/tag/foo", RefURL(repoURL, "refs/tags/foo"))
30+
assert.Equal(t, repoURL+"/src/commit/c0ffee", RefURL(repoURL, "c0ffee"))
31+
}

modules/webhook/slack.go

+2-8
Original file line numberDiff line numberDiff line change
@@ -93,15 +93,9 @@ func SlackLinkFormatter(url string, text string) string {
9393

9494
// SlackLinkToRef slack-formatter link to a repo ref
9595
func SlackLinkToRef(repoURL, ref string) string {
96+
url := git.RefURL(repoURL, ref)
9697
refName := git.RefEndName(ref)
97-
switch {
98-
case strings.HasPrefix(ref, git.BranchPrefix):
99-
return SlackLinkFormatter(repoURL+"/src/branch/"+refName, refName)
100-
case strings.HasPrefix(ref, git.TagPrefix):
101-
return SlackLinkFormatter(repoURL+"/src/tag/"+refName, refName)
102-
default:
103-
return SlackLinkFormatter(repoURL+"/src/commit/"+refName, refName)
104-
}
98+
return SlackLinkFormatter(url, refName)
10599
}
106100

107101
func getSlackCreatePayload(p *api.CreatePayload, slack *SlackMeta) (*SlackPayload, error) {

routers/repo/issue.go

+4
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,9 @@ func issues(ctx *context.Context, milestoneID int64, isPullOption util.OptionalB
286286
assigneeID = 0 // Reset ID to prevent unexpected selection of assignee.
287287
}
288288

289+
ctx.Data["IssueRefEndNames"], ctx.Data["IssueRefURLs"] =
290+
issue_service.GetRefEndNamesAndURLs(issues, ctx.Repo.RepoLink)
291+
289292
ctx.Data["ApprovalCounts"] = func(issueID int64, typ string) int64 {
290293
counts, ok := approvalCounts[issueID]
291294
if !ok || len(counts) == 0 {
@@ -1127,6 +1130,7 @@ func ViewIssue(ctx *context.Context) {
11271130
ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)
11281131
ctx.Data["IsRepoAdmin"] = ctx.IsSigned && (ctx.Repo.IsAdmin() || ctx.User.IsAdmin)
11291132
ctx.Data["LockReasons"] = setting.Repository.Issue.LockReasons
1133+
ctx.Data["RefEndName"] = git.RefEndName(issue.Ref)
11301134
ctx.HTML(200, tplIssueView)
11311135
}
11321136

routers/user/home.go

+4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"code.gitea.io/gitea/modules/markup/markdown"
2323
"code.gitea.io/gitea/modules/setting"
2424
"code.gitea.io/gitea/modules/util"
25+
issue_service "code.gitea.io/gitea/services/issue"
2526
pull_service "code.gitea.io/gitea/services/pull"
2627

2728
"github.com/keybase/go-crypto/openpgp"
@@ -624,6 +625,9 @@ func Issues(ctx *context.Context) {
624625
totalIssues = int(allIssueStats.ClosedCount)
625626
}
626627

628+
ctx.Data["IssueRefEndNames"], ctx.Data["IssueRefURLs"] =
629+
issue_service.GetRefEndNamesAndURLs(issues, ctx.Query("RepoLink"))
630+
627631
ctx.Data["Issues"] = issues
628632
ctx.Data["ApprovalCounts"] = func(issueID int64, typ string) int64 {
629633
counts, ok := approvalCounts[issueID]

services/issue/issue.go

+16
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ package issue
66

77
import (
88
"code.gitea.io/gitea/models"
9+
"code.gitea.io/gitea/modules/git"
910
"code.gitea.io/gitea/modules/notification"
11+
"code.gitea.io/gitea/modules/util"
1012
)
1113

1214
// NewIssue creates new issue with labels for repository.
@@ -128,3 +130,17 @@ func AddAssigneeIfNotAssigned(issue *models.Issue, doer *models.User, assigneeID
128130

129131
return nil
130132
}
133+
134+
// GetRefEndNamesAndURLs retrieves the ref end names (e.g. refs/heads/branch-name -> branch-name)
135+
// and their respective URLs.
136+
func GetRefEndNamesAndURLs(issues []*models.Issue, repoLink string) (map[int64]string, map[int64]string) {
137+
var issueRefEndNames = make(map[int64]string, len(issues))
138+
var issueRefURLs = make(map[int64]string, len(issues))
139+
for _, issue := range issues {
140+
if issue.Ref != "" {
141+
issueRefEndNames[issue.ID] = git.RefEndName(issue.Ref)
142+
issueRefURLs[issue.ID] = git.RefURL(repoLink, util.PathEscapeSegments(issue.Ref))
143+
}
144+
}
145+
return issueRefEndNames, issueRefURLs
146+
}

services/issue/issue_test.go

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2020 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package issue
6+
7+
import (
8+
"testing"
9+
10+
"code.gitea.io/gitea/models"
11+
12+
"github.com/stretchr/testify/assert"
13+
)
14+
15+
func TestGetRefEndNamesAndURLs(t *testing.T) {
16+
issues := []*models.Issue{
17+
{ID: 1, Ref: "refs/heads/branch1"},
18+
{ID: 2, Ref: "refs/tags/tag1"},
19+
{ID: 3, Ref: "c0ffee"},
20+
}
21+
repoLink := "/foo/bar"
22+
23+
endNames, urls := GetRefEndNamesAndURLs(issues, repoLink)
24+
assert.EqualValues(t, map[int64]string{1: "branch1", 2: "tag1", 3: "c0ffee"}, endNames)
25+
assert.EqualValues(t, map[int64]string{
26+
1: repoLink + "/src/branch/branch1",
27+
2: repoLink + "/src/tag/tag1",
28+
3: repoLink + "/src/commit/c0ffee",
29+
}, urls)
30+
}

templates/repo/issue/branch_selector_field.tmpl

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<input id="ref_selector" name="ref" type="hidden" value="{{.Issue.Ref}}">
33
<div class="ui {{if .ReadOnly}}disabled{{end}} floating filter select-branch dropdown" data-no-results="{{.i18n.Tr "repo.pulls.no_results"}}">
44
<div class="ui basic small button">
5-
<span class="text branch-name">{{if .Issue.Ref}}{{.Issue.Ref}}{{else}}{{.i18n.Tr "repo.issues.no_ref"}}{{end}}</span>
5+
<span class="text branch-name">{{if .Issue.Ref}}{{$.RefEndName}}{{else}}{{.i18n.Tr "repo.issues.no_ref"}}{{end}}</span>
66
<i class="dropdown icon"></i>
77
</div>
88
<div class="menu">
@@ -28,12 +28,12 @@
2828
</div>
2929
<div id="branch-list" class="scrolling menu reference-list-menu">
3030
{{range .Branches}}
31-
<div class="item" data-id="{{.}}" data-id-selector="#ref_selector">{{.}}</div>
31+
<div class="item" data-id="refs/heads/{{.}}" data-name="{{.}}" data-id-selector="#ref_selector">{{.}}</div>
3232
{{end}}
3333
</div>
3434
<div id="tag-list" class="scrolling menu reference-list-menu" style="display: none">
3535
{{range .Tags}}
36-
<div class="item" data-id="{{.}}" data-id-selector="#ref_selector">{{.}}</div>
36+
<div class="item" data-id="refs/tags/{{.}}" data-name="tags/{{.}}" data-id-selector="#ref_selector">{{.}}</div>
3737
{{end}}
3838
</div>
3939
</div>

templates/repo/issue/list.tmpl

+2-2
Original file line numberDiff line numberDiff line change
@@ -247,8 +247,8 @@
247247
</a>
248248
{{end}}
249249
{{if .Ref}}
250-
<a class="ref" href="{{$.RepoLink}}/src/branch/{{.Ref | PathEscapeSegments}}">
251-
{{svg "octicon-git-branch" 16}} {{.Ref}}
250+
<a class="ref" href="{{index $.IssueRefURLs .ID}}">
251+
{{svg "octicon-git-branch" 16}} {{index $.IssueRefEndNames .ID}}
252252
</a>
253253
{{end}}
254254
{{$tasks := .GetTasks}}

templates/repo/issue/milestone_issues.tmpl

+2-2
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,8 @@
218218
{{end}}
219219

220220
{{if .Ref}}
221-
<a class="ref" href="{{$.RepoLink}}/src/branch/{{.Ref}}">
222-
{{svg "octicon-git-branch" 16}} {{.Ref}}
221+
<a class="ref" href="{{index $.IssueRefURLs .ID}}">
222+
{{svg "octicon-git-branch" 16}} {{index $.IssueRefEndNames .ID}}
223223
</a>
224224
{{end}}
225225
{{$tasks := .GetTasks}}

templates/user/dashboard/issues.tmpl

+2-2
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,8 @@
149149
</a>
150150
{{end}}
151151
{{if .Ref}}
152-
<a class="ref" href="{{AppSubUrl}}/{{.Repo.Owner.Name}}/{{.Repo.Name}}/src/branch/{{.Ref}}">
153-
{{svg "octicon-git-branch" 16}} {{.Ref}}
152+
<a class="ref" href="{{AppSubUrl}}/{{.Repo.Owner.Name}}/{{.Repo.Name}}{{index $.IssueRefURLs .ID}}">
153+
{{svg "octicon-git-branch" 16}} {{index $.IssueRefEndNames .ID}}
154154
</a>
155155
{{end}}
156156
{{range .Assignees}}

web_src/js/index.js

+3-4
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,9 @@ function initEditForm() {
114114
function initBranchSelector() {
115115
const $selectBranch = $('.ui.select-branch');
116116
const $branchMenu = $selectBranch.find('.reference-list-menu');
117-
$branchMenu.find('.item:not(.no-select)').on('click', function () {
118-
const selectedValue = $(this).data('id');
119-
$($(this).data('id-selector')).val(selectedValue);
120-
$selectBranch.find('.ui .branch-name').text(selectedValue);
117+
$branchMenu.find('.item:not(.no-select)').click(function () {
118+
$($(this).data('id-selector')).val($(this).data('id'));
119+
$selectBranch.find('.ui .branch-name').text($(this).data('name'));
121120
});
122121
$selectBranch.find('.reference.column').on('click', function () {
123122
$selectBranch.find('.scrolling.reference-list-menu').css('display', 'none');

0 commit comments

Comments
 (0)