Skip to content

Commit bd6ef71

Browse files
delvhsilverwindwxiaoguang
authored
Show branches and tags that contain a commit (#25180)
Now, you can see for a commit which existing branches and tags contain it. You first have to click on the `load branches and tags` button, they are not preloaded by default. All branches and tags are ordered descending by creation date. You can even see without much hassle if the given commit is already part of the default branch. Closes #25152 ## Screenshots ### Initial ![image](https://github.com/go-gitea/gitea/assets/51889757/84db2c0b-aaef-4f69-ab92-0b812793d2ad) ### Loaded ![image](https://github.com/go-gitea/gitea/assets/51889757/a9b84e66-8e44-4c55-b017-c37f4a45f41b) --------- Co-authored-by: silverwind <[email protected]> Co-authored-by: wxiaoguang <[email protected]>
1 parent bd7b5e6 commit bd6ef71

16 files changed

+202
-42
lines changed

modules/context/context_response.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ func (ctx *Context) serverErrorInternal(logMsg string, logErr error) {
166166
// NotFoundOrServerError use error check function to determine if the error
167167
// is about not found. It responds with 404 status code for not found error,
168168
// or error context description for logging purpose of 500 server error.
169+
// TODO: remove the "errCheck" and use util.ErrNotFound to check
169170
func (ctx *Context) NotFoundOrServerError(logMsg string, errCheck func(error) bool, logErr error) {
170171
if errCheck(logErr) {
171172
ctx.notFoundInternal(logMsg, logErr)

modules/git/commit.go

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import (
2020

2121
// Commit represents a git commit.
2222
type Commit struct {
23-
Branch string // Branch this commit belongs to
2423
Tree
2524
ID SHA1 // The ID of this commit object
2625
Author *Signature
@@ -432,31 +431,6 @@ func (c *Commit) GetBranchName() (string, error) {
432431
return strings.SplitN(strings.TrimSpace(data), "~", 2)[0], nil
433432
}
434433

435-
// LoadBranchName load branch name for commit
436-
func (c *Commit) LoadBranchName() (err error) {
437-
if len(c.Branch) != 0 {
438-
return nil
439-
}
440-
441-
c.Branch, err = c.GetBranchName()
442-
return err
443-
}
444-
445-
// GetTagName gets the current tag name for given commit
446-
func (c *Commit) GetTagName() (string, error) {
447-
data, _, err := NewCommand(c.repo.Ctx, "describe", "--exact-match", "--tags", "--always").AddDynamicArguments(c.ID.String()).RunStdString(&RunOpts{Dir: c.repo.Path})
448-
if err != nil {
449-
// handle special case where there is no tag for this commit
450-
if strings.Contains(err.Error(), "no tag exactly matches") {
451-
return "", nil
452-
}
453-
454-
return "", err
455-
}
456-
457-
return strings.TrimSpace(data), nil
458-
}
459-
460434
// CommitFileStatus represents status of files in a commit.
461435
type CommitFileStatus struct {
462436
Added []string

modules/git/repo_ref.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,61 @@
33

44
package git
55

6+
import (
7+
"context"
8+
"strings"
9+
10+
"code.gitea.io/gitea/modules/util"
11+
)
12+
613
// GetRefs returns all references of the repository.
714
func (repo *Repository) GetRefs() ([]*Reference, error) {
815
return repo.GetRefsFiltered("")
916
}
17+
18+
// ListOccurrences lists all refs of the given refType the given commit appears in sorted by creation date DESC
19+
// refType should only be a literal "branch" or "tag" and nothing else
20+
func (repo *Repository) ListOccurrences(ctx context.Context, refType, commitSHA string) ([]string, error) {
21+
cmd := NewCommand(ctx)
22+
if refType == "branch" {
23+
cmd.AddArguments("branch")
24+
} else if refType == "tag" {
25+
cmd.AddArguments("tag")
26+
} else {
27+
return nil, util.NewInvalidArgumentErrorf(`can only use "branch" or "tag" for refType, but got %q`, refType)
28+
}
29+
stdout, _, err := cmd.AddArguments("--no-color", "--sort=-creatordate", "--contains").AddDynamicArguments(commitSHA).RunStdString(&RunOpts{Dir: repo.Path})
30+
if err != nil {
31+
return nil, err
32+
}
33+
34+
refs := strings.Split(strings.TrimSpace(stdout), "\n")
35+
if refType == "branch" {
36+
return parseBranches(refs), nil
37+
}
38+
return parseTags(refs), nil
39+
}
40+
41+
func parseBranches(refs []string) []string {
42+
results := make([]string, 0, len(refs))
43+
for _, ref := range refs {
44+
if strings.HasPrefix(ref, "* ") { // current branch (main branch)
45+
results = append(results, ref[len("* "):])
46+
} else if strings.HasPrefix(ref, " ") { // all other branches
47+
results = append(results, ref[len(" "):])
48+
} else if ref != "" {
49+
results = append(results, ref)
50+
}
51+
}
52+
return results
53+
}
54+
55+
func parseTags(refs []string) []string {
56+
results := make([]string, 0, len(refs))
57+
for _, ref := range refs {
58+
if ref != "" {
59+
results = append(results, ref)
60+
}
61+
}
62+
return results
63+
}

options/locale/locale_en-US.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1170,6 +1170,9 @@ commit_graph.select = Select branches
11701170
commit_graph.hide_pr_refs = Hide Pull Requests
11711171
commit_graph.monochrome = Mono
11721172
commit_graph.color = Color
1173+
commit.contained_in = This commit is contained in:
1174+
commit.contained_in_default_branch = This commit is part of the default branch
1175+
commit.load_referencing_branches_and_tags = Load branches and tags referencing this commit
11731176
blame = Blame
11741177
download_file = Download file
11751178
normal_view = Normal View

routers/web/repo/commit.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"code.gitea.io/gitea/modules/log"
2424
"code.gitea.io/gitea/modules/setting"
2525
"code.gitea.io/gitea/services/gitdiff"
26+
git_service "code.gitea.io/gitea/services/repository"
2627
)
2728

2829
const (
@@ -255,6 +256,15 @@ func FileHistory(ctx *context.Context) {
255256
ctx.HTML(http.StatusOK, tplCommits)
256257
}
257258

259+
func LoadBranchesAndTags(ctx *context.Context) {
260+
response, err := git_service.LoadBranchesAndTags(ctx, ctx.Repo, ctx.Params("sha"))
261+
if err == nil {
262+
ctx.JSON(http.StatusOK, response)
263+
return
264+
}
265+
ctx.NotFoundOrServerError(fmt.Sprintf("could not load branches and tags the commit %s belongs to", ctx.Params("sha")), git.IsErrNotExist, err)
266+
}
267+
258268
// Diff show different from current commit to previous commit
259269
func Diff(ctx *context.Context) {
260270
ctx.Data["PageIsDiff"] = true
@@ -374,11 +384,6 @@ func Diff(ctx *context.Context) {
374384
return
375385
}
376386

377-
ctx.Data["TagName"], err = commit.GetTagName()
378-
if err != nil {
379-
ctx.ServerError("commit.GetTagName", err)
380-
return
381-
}
382387
ctx.HTML(http.StatusOK, tplCommitPage)
383388
}
384389

routers/web/web.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1337,6 +1337,7 @@ func registerRoutes(m *web.Route) {
13371337
m.Group("", func() {
13381338
m.Get("/graph", repo.Graph)
13391339
m.Get("/commit/{sha:([a-f0-9]{7,40})$}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff)
1340+
m.Get("/commit/{sha:([a-f0-9]{7,40})$}/load-branches-and-tags", repo.LoadBranchesAndTags)
13401341
m.Get("/cherry-pick/{sha:([a-f0-9]{7,40})$}", repo.SetEditorconfigIfExists, repo.CherryPick)
13411342
}, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader)
13421343

services/repository/commit.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright 2023 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package repository
5+
6+
import (
7+
"context"
8+
"fmt"
9+
10+
gitea_ctx "code.gitea.io/gitea/modules/context"
11+
"code.gitea.io/gitea/modules/util"
12+
)
13+
14+
type ContainedLinks struct { // TODO: better name?
15+
Branches []*namedLink `json:"branches"`
16+
Tags []*namedLink `json:"tags"`
17+
DefaultBranch string `json:"default_branch"`
18+
}
19+
20+
type namedLink struct { // TODO: better name?
21+
Name string `json:"name"`
22+
WebLink string `json:"web_link"`
23+
}
24+
25+
// LoadBranchesAndTags creates a new repository branch
26+
func LoadBranchesAndTags(ctx context.Context, baseRepo *gitea_ctx.Repository, commitSHA string) (*ContainedLinks, error) {
27+
containedTags, err := baseRepo.GitRepo.ListOccurrences(ctx, "tag", commitSHA)
28+
if err != nil {
29+
return nil, fmt.Errorf("encountered a problem while querying %s: %w", "tags", err)
30+
}
31+
containedBranches, err := baseRepo.GitRepo.ListOccurrences(ctx, "branch", commitSHA)
32+
if err != nil {
33+
return nil, fmt.Errorf("encountered a problem while querying %s: %w", "branches", err)
34+
}
35+
36+
result := &ContainedLinks{
37+
DefaultBranch: baseRepo.Repository.DefaultBranch,
38+
Branches: make([]*namedLink, 0, len(containedBranches)),
39+
Tags: make([]*namedLink, 0, len(containedTags)),
40+
}
41+
for _, tag := range containedTags {
42+
// TODO: Use a common method to get the link to a branch/tag instead of hard-coding it here
43+
result.Tags = append(result.Tags, &namedLink{
44+
Name: tag,
45+
WebLink: fmt.Sprintf("%s/src/tag/%s", baseRepo.RepoLink, util.PathEscapeSegments(tag)),
46+
})
47+
}
48+
for _, branch := range containedBranches {
49+
result.Branches = append(result.Branches, &namedLink{
50+
Name: branch,
51+
WebLink: fmt.Sprintf("%s/src/branch/%s", baseRepo.RepoLink, util.PathEscapeSegments(branch)),
52+
})
53+
}
54+
return result, nil
55+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<div class="branch-and-tag-area" data-text-default-branch-tooltip="{{.locale.Tr "repo.commit.contained_in_default_branch"}}">
2+
<button class="ui button ellipsis-button load-branches-and-tags gt-mt-3" aria-expanded="false"
3+
data-fetch-url="{{.RepoLink}}/commit/{{.CommitID}}/load-branches-and-tags"
4+
data-tooltip-content="{{.locale.Tr "repo.commit.load_referencing_branches_and_tags"}}"
5+
>...</button>
6+
<div class="branch-and-tag-detail gt-hidden">
7+
<div class="divider"></div>
8+
<div>{{.locale.Tr "repo.commit.contained_in"}}</div>
9+
<div class="gt-df gt-mt-3">
10+
<div class="gt-p-2">{{svg "octicon-git-branch"}}</div>
11+
<div class="branch-area flex-text-block gt-f1"></div>
12+
</div>
13+
<div class="gt-df gt-mt-3">
14+
<div class="gt-p-2">{{svg "octicon-tag"}}</div>
15+
<div class="tag-area flex-text-block gt-f1"></div>
16+
</div>
17+
</div>
18+
</div>

templates/repo/commit_page.tmpl

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,12 +137,7 @@
137137
{{if IsMultilineCommitMessage .Commit.Message}}
138138
<pre class="commit-body gt-mt-0">{{RenderCommitBody $.Context .Commit.Message $.RepoLink $.Repository.ComposeMetas}}</pre>
139139
{{end}}
140-
{{if .BranchName}}
141-
<span class="text grey gt-mr-3">{{svg "octicon-git-branch" 16 "gt-mr-2"}}{{.BranchName}}</span>
142-
{{end}}
143-
{{if .TagName}}
144-
<span class="text grey gt-mr-3">{{svg "octicon-tag" 16 "gt-mr-2"}}{{.TagName}}</span>
145-
{{end}}
140+
{{template "repo/commit_load_branches_and_tags" .}}
146141
</div>
147142
<div class="ui attached segment gt-df gt-ac gt-sb gt-py-2 commit-header-row gt-fw {{$class}}">
148143
<div class="gt-df gt-ac author">

templates/repo/commits_list.tmpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
{{end}}
7171
</span>
7272
{{if IsMultilineCommitMessage .Message}}
73-
<button class="ui button ellipsis-button" aria-expanded="false">...</button>
73+
<button class="ui button js-toggle-commit-body ellipsis-button" aria-expanded="false">...</button>
7474
{{end}}
7575
{{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses "root" $}}
7676
{{if IsMultilineCommitMessage .Message}}

templates/repo/commits_list_small.tmpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040

4141
<span class="gt-mono commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{RenderCommitMessageLinkSubject $.root.Context .Message ($.comment.Issue.PullRequest.BaseRepo.Link|Escape) $commitLink $.comment.Issue.PullRequest.BaseRepo.ComposeMetas}}</span>
4242
{{if IsMultilineCommitMessage .Message}}
43-
<button class="ui button ellipsis-button" aria-expanded="false">...</button>
43+
<button class="ui button js-toggle-commit-body ellipsis-button" aria-expanded="false">...</button>
4444
{{end}}
4545
{{if IsMultilineCommitMessage .Message}}
4646
<pre class="commit-body gt-hidden">{{RenderCommitBody $.root.Context .Message ($.comment.Issue.PullRequest.BaseRepo.Link|Escape) $.comment.Issue.PullRequest.BaseRepo.ComposeMetas}}</pre>

templates/repo/view_list.tmpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
{{$commitLink:= printf "%s/commit/%s" .RepoLink (PathEscape .LatestCommit.ID.String)}}
2929
<span class="grey commit-summary" title="{{.LatestCommit.Summary}}"><span class="message-wrapper">{{RenderCommitMessageLinkSubject $.Context .LatestCommit.Message $.RepoLink $commitLink $.Repository.ComposeMetas}}</span>
3030
{{if IsMultilineCommitMessage .LatestCommit.Message}}
31-
<button class="ui button ellipsis-button" aria-expanded="false">...</button>
31+
<button class="ui button js-toggle-commit-body ellipsis-button" aria-expanded="false">...</button>
3232
<pre class="commit-body gt-hidden">{{RenderCommitBody $.Context .LatestCommit.Message $.RepoLink $.Repository.ComposeMetas}}</pre>
3333
{{end}}
3434
</span>

web_src/js/features/repo-commit.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {toggleElem} from '../utils/dom.js';
55
const {csrfToken} = window.config;
66

77
export function initRepoEllipsisButton() {
8-
$('.ellipsis-button').on('click', function (e) {
8+
$('.js-toggle-commit-body').on('click', function (e) {
99
e.preventDefault();
1010
const expanded = $(this).attr('aria-expanded') === 'true';
1111
toggleElem($(this).parent().find('.commit-body'));
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import {hideElem, showElem, toggleElem} from '../utils/dom.js';
2+
3+
async function loadBranchesAndTags(area, loadingButton) {
4+
loadingButton.classList.add('disabled');
5+
try {
6+
const res = await fetch(loadingButton.getAttribute('data-fetch-url'));
7+
const data = await res.json();
8+
hideElem(loadingButton);
9+
addTags(area, data.tags);
10+
addBranches(area, data.branches, data.default_branch);
11+
showElem(area.querySelectorAll('.branch-and-tag-detail'));
12+
} finally {
13+
loadingButton.classList.remove('disabled');
14+
}
15+
}
16+
17+
function addTags(area, tags) {
18+
const tagArea = area.querySelector('.tag-area');
19+
toggleElem(tagArea, tags.length > 0);
20+
for (const tag of tags) {
21+
addLink(tagArea, tag.web_link, tag.name);
22+
}
23+
}
24+
25+
function addBranches(area, branches, defaultBranch) {
26+
const defaultBranchTooltip = area.getAttribute('data-text-default-branch-tooltip');
27+
const branchArea = area.querySelector('.branch-area');
28+
toggleElem(branchArea, branches.length > 0);
29+
for (const branch of branches) {
30+
const tooltip = defaultBranch === branch.name ? defaultBranchTooltip : null;
31+
addLink(branchArea, branch.web_link, branch.name, tooltip);
32+
}
33+
}
34+
35+
function addLink(parent, href, text, tooltip) {
36+
const link = document.createElement('a');
37+
link.classList.add('muted', 'gt-px-2');
38+
link.href = href;
39+
link.textContent = text;
40+
if (tooltip) {
41+
link.classList.add('gt-border-secondary', 'gt-rounded');
42+
link.setAttribute('data-tooltip-content', tooltip);
43+
}
44+
parent.append(link);
45+
}
46+
47+
export function initRepoDiffCommitBranchesAndTags() {
48+
for (const area of document.querySelectorAll('.branch-and-tag-area')) {
49+
const btn = area.querySelector('.load-branches-and-tags');
50+
btn.addEventListener('click', () => loadBranchesAndTags(area, btn));
51+
}
52+
}

web_src/js/features/repo-legacy.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ async function onEditContent(event) {
459459
}
460460

461461
export function initRepository() {
462-
if ($('.repository').length === 0) {
462+
if ($('.page-content.repository').length === 0) {
463463
return;
464464
}
465465

web_src/js/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ import {initGiteaFomantic} from './modules/fomantic.js';
8383
import {onDomReady} from './utils/dom.js';
8484
import {initRepoIssueList} from './features/repo-issue-list.js';
8585
import {initCommonIssueListQuickGoto} from './features/common-issue-list.js';
86+
import {initRepoDiffCommitBranchesAndTags} from './features/repo-diff-commit.js';
8687

8788
// Init Gitea's Fomantic settings
8889
initGiteaFomantic();
@@ -141,6 +142,7 @@ onDomReady(() => {
141142
initRepoCodeView();
142143
initRepoCommentForm();
143144
initRepoEllipsisButton();
145+
initRepoDiffCommitBranchesAndTags();
144146
initRepoCommitLastCommitLoader();
145147
initRepoEditor();
146148
initRepoGraphGit();

0 commit comments

Comments
 (0)