diff --git a/modules/context/context_response.go b/modules/context/context_response.go
index bb3ccf69ce..9dc6d1fc0e 100644
--- a/modules/context/context_response.go
+++ b/modules/context/context_response.go
@@ -166,6 +166,7 @@ func (ctx *Context) serverErrorInternal(logMsg string, logErr error) {
 // NotFoundOrServerError use error check function to determine if the error
 // is about not found. It responds with 404 status code for not found error,
 // or error context description for logging purpose of 500 server error.
+// TODO: remove the "errCheck" and use util.ErrNotFound to check
 func (ctx *Context) NotFoundOrServerError(logMsg string, errCheck func(error) bool, logErr error) {
 	if errCheck(logErr) {
 		ctx.notFoundInternal(logMsg, logErr)
diff --git a/modules/git/commit.go b/modules/git/commit.go
index 729e3b4672..c44882d886 100644
--- a/modules/git/commit.go
+++ b/modules/git/commit.go
@@ -20,7 +20,6 @@ import (
 
 // Commit represents a git commit.
 type Commit struct {
-	Branch string // Branch this commit belongs to
 	Tree
 	ID            SHA1 // The ID of this commit object
 	Author        *Signature
@@ -432,31 +431,6 @@ func (c *Commit) GetBranchName() (string, error) {
 	return strings.SplitN(strings.TrimSpace(data), "~", 2)[0], nil
 }
 
-// LoadBranchName load branch name for commit
-func (c *Commit) LoadBranchName() (err error) {
-	if len(c.Branch) != 0 {
-		return nil
-	}
-
-	c.Branch, err = c.GetBranchName()
-	return err
-}
-
-// GetTagName gets the current tag name for given commit
-func (c *Commit) GetTagName() (string, error) {
-	data, _, err := NewCommand(c.repo.Ctx, "describe", "--exact-match", "--tags", "--always").AddDynamicArguments(c.ID.String()).RunStdString(&RunOpts{Dir: c.repo.Path})
-	if err != nil {
-		// handle special case where there is no tag for this commit
-		if strings.Contains(err.Error(), "no tag exactly matches") {
-			return "", nil
-		}
-
-		return "", err
-	}
-
-	return strings.TrimSpace(data), nil
-}
-
 // CommitFileStatus represents status of files in a commit.
 type CommitFileStatus struct {
 	Added    []string
diff --git a/modules/git/repo_ref.go b/modules/git/repo_ref.go
index 54e424bb83..8eaa17cb04 100644
--- a/modules/git/repo_ref.go
+++ b/modules/git/repo_ref.go
@@ -3,7 +3,61 @@
 
 package git
 
+import (
+	"context"
+	"strings"
+
+	"code.gitea.io/gitea/modules/util"
+)
+
 // GetRefs returns all references of the repository.
 func (repo *Repository) GetRefs() ([]*Reference, error) {
 	return repo.GetRefsFiltered("")
 }
+
+// ListOccurrences lists all refs of the given refType the given commit appears in sorted by creation date DESC
+// refType should only be a literal "branch" or "tag" and nothing else
+func (repo *Repository) ListOccurrences(ctx context.Context, refType, commitSHA string) ([]string, error) {
+	cmd := NewCommand(ctx)
+	if refType == "branch" {
+		cmd.AddArguments("branch")
+	} else if refType == "tag" {
+		cmd.AddArguments("tag")
+	} else {
+		return nil, util.NewInvalidArgumentErrorf(`can only use "branch" or "tag" for refType, but got %q`, refType)
+	}
+	stdout, _, err := cmd.AddArguments("--no-color", "--sort=-creatordate", "--contains").AddDynamicArguments(commitSHA).RunStdString(&RunOpts{Dir: repo.Path})
+	if err != nil {
+		return nil, err
+	}
+
+	refs := strings.Split(strings.TrimSpace(stdout), "\n")
+	if refType == "branch" {
+		return parseBranches(refs), nil
+	}
+	return parseTags(refs), nil
+}
+
+func parseBranches(refs []string) []string {
+	results := make([]string, 0, len(refs))
+	for _, ref := range refs {
+		if strings.HasPrefix(ref, "* ") { // current branch (main branch)
+			results = append(results, ref[len("* "):])
+		} else if strings.HasPrefix(ref, "  ") { // all other branches
+			results = append(results, ref[len("  "):])
+		} else if ref != "" {
+			results = append(results, ref)
+		}
+	}
+	return results
+}
+
+func parseTags(refs []string) []string {
+	results := make([]string, 0, len(refs))
+	for _, ref := range refs {
+		if ref != "" {
+			results = append(results, ref)
+		}
+	}
+	return results
+}
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 25fb155435..dc88c422b5 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -1170,6 +1170,9 @@ commit_graph.select = Select branches
 commit_graph.hide_pr_refs = Hide Pull Requests
 commit_graph.monochrome = Mono
 commit_graph.color = Color
+commit.contained_in = This commit is contained in:
+commit.contained_in_default_branch = This commit is part of the default branch
+commit.load_referencing_branches_and_tags = Load branches and tags referencing this commit
 blame = Blame
 download_file = Download file
 normal_view = Normal View
diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go
index e88f1139f8..5b32591b89 100644
--- a/routers/web/repo/commit.go
+++ b/routers/web/repo/commit.go
@@ -23,6 +23,7 @@ import (
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/services/gitdiff"
+	git_service "code.gitea.io/gitea/services/repository"
 )
 
 const (
@@ -255,6 +256,15 @@ func FileHistory(ctx *context.Context) {
 	ctx.HTML(http.StatusOK, tplCommits)
 }
 
+func LoadBranchesAndTags(ctx *context.Context) {
+	response, err := git_service.LoadBranchesAndTags(ctx, ctx.Repo, ctx.Params("sha"))
+	if err == nil {
+		ctx.JSON(http.StatusOK, response)
+		return
+	}
+	ctx.NotFoundOrServerError(fmt.Sprintf("could not load branches and tags the commit %s belongs to", ctx.Params("sha")), git.IsErrNotExist, err)
+}
+
 // Diff show different from current commit to previous commit
 func Diff(ctx *context.Context) {
 	ctx.Data["PageIsDiff"] = true
@@ -374,11 +384,6 @@ func Diff(ctx *context.Context) {
 		return
 	}
 
-	ctx.Data["TagName"], err = commit.GetTagName()
-	if err != nil {
-		ctx.ServerError("commit.GetTagName", err)
-		return
-	}
 	ctx.HTML(http.StatusOK, tplCommitPage)
 }
 
diff --git a/routers/web/web.go b/routers/web/web.go
index d7ef2fb82f..0b51961445 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -1337,6 +1337,7 @@ func registerRoutes(m *web.Route) {
 		m.Group("", func() {
 			m.Get("/graph", repo.Graph)
 			m.Get("/commit/{sha:([a-f0-9]{7,40})$}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff)
+			m.Get("/commit/{sha:([a-f0-9]{7,40})$}/load-branches-and-tags", repo.LoadBranchesAndTags)
 			m.Get("/cherry-pick/{sha:([a-f0-9]{7,40})$}", repo.SetEditorconfigIfExists, repo.CherryPick)
 		}, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader)
 
diff --git a/services/repository/commit.go b/services/repository/commit.go
new file mode 100644
index 0000000000..2497910a83
--- /dev/null
+++ b/services/repository/commit.go
@@ -0,0 +1,55 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package repository
+
+import (
+	"context"
+	"fmt"
+
+	gitea_ctx "code.gitea.io/gitea/modules/context"
+	"code.gitea.io/gitea/modules/util"
+)
+
+type ContainedLinks struct { // TODO: better name?
+	Branches      []*namedLink `json:"branches"`
+	Tags          []*namedLink `json:"tags"`
+	DefaultBranch string       `json:"default_branch"`
+}
+
+type namedLink struct { // TODO: better name?
+	Name    string `json:"name"`
+	WebLink string `json:"web_link"`
+}
+
+// LoadBranchesAndTags creates a new repository branch
+func LoadBranchesAndTags(ctx context.Context, baseRepo *gitea_ctx.Repository, commitSHA string) (*ContainedLinks, error) {
+	containedTags, err := baseRepo.GitRepo.ListOccurrences(ctx, "tag", commitSHA)
+	if err != nil {
+		return nil, fmt.Errorf("encountered a problem while querying %s: %w", "tags", err)
+	}
+	containedBranches, err := baseRepo.GitRepo.ListOccurrences(ctx, "branch", commitSHA)
+	if err != nil {
+		return nil, fmt.Errorf("encountered a problem while querying %s: %w", "branches", err)
+	}
+
+	result := &ContainedLinks{
+		DefaultBranch: baseRepo.Repository.DefaultBranch,
+		Branches:      make([]*namedLink, 0, len(containedBranches)),
+		Tags:          make([]*namedLink, 0, len(containedTags)),
+	}
+	for _, tag := range containedTags {
+		// TODO: Use a common method to get the link to a branch/tag instead of hard-coding it here
+		result.Tags = append(result.Tags, &namedLink{
+			Name:    tag,
+			WebLink: fmt.Sprintf("%s/src/tag/%s", baseRepo.RepoLink, util.PathEscapeSegments(tag)),
+		})
+	}
+	for _, branch := range containedBranches {
+		result.Branches = append(result.Branches, &namedLink{
+			Name:    branch,
+			WebLink: fmt.Sprintf("%s/src/branch/%s", baseRepo.RepoLink, util.PathEscapeSegments(branch)),
+		})
+	}
+	return result, nil
+}
diff --git a/templates/repo/commit_load_branches_and_tags.tmpl b/templates/repo/commit_load_branches_and_tags.tmpl
new file mode 100644
index 0000000000..c19aa55c56
--- /dev/null
+++ b/templates/repo/commit_load_branches_and_tags.tmpl
@@ -0,0 +1,18 @@
+<div class="branch-and-tag-area" data-text-default-branch-tooltip="{{.locale.Tr "repo.commit.contained_in_default_branch"}}">
+	<button class="ui button ellipsis-button load-branches-and-tags gt-mt-3" aria-expanded="false"
+		data-fetch-url="{{.RepoLink}}/commit/{{.CommitID}}/load-branches-and-tags"
+		data-tooltip-content="{{.locale.Tr "repo.commit.load_referencing_branches_and_tags"}}"
+	>...</button>
+	<div class="branch-and-tag-detail gt-hidden">
+		<div class="divider"></div>
+		<div>{{.locale.Tr "repo.commit.contained_in"}}</div>
+		<div class="gt-df gt-mt-3">
+			<div class="gt-p-2">{{svg "octicon-git-branch"}}</div>
+			<div class="branch-area flex-text-block gt-f1"></div>
+		</div>
+		<div class="gt-df gt-mt-3">
+			<div class="gt-p-2">{{svg "octicon-tag"}}</div>
+			<div class="tag-area flex-text-block gt-f1"></div>
+		</div>
+	</div>
+</div>
diff --git a/templates/repo/commit_page.tmpl b/templates/repo/commit_page.tmpl
index e4aad30fa7..bd33a30443 100644
--- a/templates/repo/commit_page.tmpl
+++ b/templates/repo/commit_page.tmpl
@@ -137,12 +137,7 @@
 			{{if IsMultilineCommitMessage .Commit.Message}}
 				<pre class="commit-body gt-mt-0">{{RenderCommitBody $.Context .Commit.Message $.RepoLink $.Repository.ComposeMetas}}</pre>
 			{{end}}
-			{{if .BranchName}}
-				<span class="text grey gt-mr-3">{{svg "octicon-git-branch" 16 "gt-mr-2"}}{{.BranchName}}</span>
-			{{end}}
-			{{if .TagName}}
-				<span class="text grey gt-mr-3">{{svg "octicon-tag" 16 "gt-mr-2"}}{{.TagName}}</span>
-			{{end}}
+			{{template "repo/commit_load_branches_and_tags" .}}
 		</div>
 		<div class="ui attached segment gt-df gt-ac gt-sb gt-py-2 commit-header-row gt-fw {{$class}}">
 				<div class="gt-df gt-ac author">
diff --git a/templates/repo/commits_list.tmpl b/templates/repo/commits_list.tmpl
index 6768bcb513..ef9d065456 100644
--- a/templates/repo/commits_list.tmpl
+++ b/templates/repo/commits_list.tmpl
@@ -70,7 +70,7 @@
 							{{end}}
 							</span>
 							{{if IsMultilineCommitMessage .Message}}
-							<button class="ui button ellipsis-button" aria-expanded="false">...</button>
+							<button class="ui button js-toggle-commit-body ellipsis-button" aria-expanded="false">...</button>
 							{{end}}
 							{{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses "root" $}}
 							{{if IsMultilineCommitMessage .Message}}
diff --git a/templates/repo/commits_list_small.tmpl b/templates/repo/commits_list_small.tmpl
index 6bbc19529f..57c9fd17ef 100644
--- a/templates/repo/commits_list_small.tmpl
+++ b/templates/repo/commits_list_small.tmpl
@@ -40,7 +40,7 @@
 
 		<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>
 		{{if IsMultilineCommitMessage .Message}}
-			<button class="ui button ellipsis-button" aria-expanded="false">...</button>
+			<button class="ui button js-toggle-commit-body ellipsis-button" aria-expanded="false">...</button>
 		{{end}}
 		{{if IsMultilineCommitMessage .Message}}
 			<pre class="commit-body gt-hidden">{{RenderCommitBody $.root.Context .Message ($.comment.Issue.PullRequest.BaseRepo.Link|Escape) $.comment.Issue.PullRequest.BaseRepo.ComposeMetas}}</pre>
diff --git a/templates/repo/view_list.tmpl b/templates/repo/view_list.tmpl
index 13b4d3d3d3..3eabf9f181 100644
--- a/templates/repo/view_list.tmpl
+++ b/templates/repo/view_list.tmpl
@@ -28,7 +28,7 @@
 					{{$commitLink:= printf "%s/commit/%s" .RepoLink (PathEscape .LatestCommit.ID.String)}}
 					<span class="grey commit-summary" title="{{.LatestCommit.Summary}}"><span class="message-wrapper">{{RenderCommitMessageLinkSubject $.Context .LatestCommit.Message $.RepoLink $commitLink $.Repository.ComposeMetas}}</span>
 						{{if IsMultilineCommitMessage .LatestCommit.Message}}
-							<button class="ui button ellipsis-button" aria-expanded="false">...</button>
+							<button class="ui button js-toggle-commit-body ellipsis-button" aria-expanded="false">...</button>
 							<pre class="commit-body gt-hidden">{{RenderCommitBody $.Context .LatestCommit.Message $.RepoLink $.Repository.ComposeMetas}}</pre>
 						{{end}}
 					</span>
diff --git a/web_src/js/features/repo-commit.js b/web_src/js/features/repo-commit.js
index 7e4e40806b..7240bf398a 100644
--- a/web_src/js/features/repo-commit.js
+++ b/web_src/js/features/repo-commit.js
@@ -5,7 +5,7 @@ import {toggleElem} from '../utils/dom.js';
 const {csrfToken} = window.config;
 
 export function initRepoEllipsisButton() {
-  $('.ellipsis-button').on('click', function (e) {
+  $('.js-toggle-commit-body').on('click', function (e) {
     e.preventDefault();
     const expanded = $(this).attr('aria-expanded') === 'true';
     toggleElem($(this).parent().find('.commit-body'));
diff --git a/web_src/js/features/repo-diff-commit.js b/web_src/js/features/repo-diff-commit.js
new file mode 100644
index 0000000000..968f318e63
--- /dev/null
+++ b/web_src/js/features/repo-diff-commit.js
@@ -0,0 +1,52 @@
+import {hideElem, showElem, toggleElem} from '../utils/dom.js';
+
+async function loadBranchesAndTags(area, loadingButton) {
+  loadingButton.classList.add('disabled');
+  try {
+    const res = await fetch(loadingButton.getAttribute('data-fetch-url'));
+    const data = await res.json();
+    hideElem(loadingButton);
+    addTags(area, data.tags);
+    addBranches(area, data.branches, data.default_branch);
+    showElem(area.querySelectorAll('.branch-and-tag-detail'));
+  } finally {
+    loadingButton.classList.remove('disabled');
+  }
+}
+
+function addTags(area, tags) {
+  const tagArea = area.querySelector('.tag-area');
+  toggleElem(tagArea, tags.length > 0);
+  for (const tag of tags) {
+    addLink(tagArea, tag.web_link, tag.name);
+  }
+}
+
+function addBranches(area, branches, defaultBranch) {
+  const defaultBranchTooltip = area.getAttribute('data-text-default-branch-tooltip');
+  const branchArea = area.querySelector('.branch-area');
+  toggleElem(branchArea, branches.length > 0);
+  for (const branch of branches) {
+    const tooltip = defaultBranch === branch.name ? defaultBranchTooltip : null;
+    addLink(branchArea, branch.web_link, branch.name, tooltip);
+  }
+}
+
+function addLink(parent, href, text, tooltip) {
+  const link = document.createElement('a');
+  link.classList.add('muted', 'gt-px-2');
+  link.href = href;
+  link.textContent = text;
+  if (tooltip) {
+    link.classList.add('gt-border-secondary', 'gt-rounded');
+    link.setAttribute('data-tooltip-content', tooltip);
+  }
+  parent.append(link);
+}
+
+export function initRepoDiffCommitBranchesAndTags() {
+  for (const area of document.querySelectorAll('.branch-and-tag-area')) {
+    const btn = area.querySelector('.load-branches-and-tags');
+    btn.addEventListener('click', () => loadBranchesAndTags(area, btn));
+  }
+}
diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js
index f23ff45470..5991df6322 100644
--- a/web_src/js/features/repo-legacy.js
+++ b/web_src/js/features/repo-legacy.js
@@ -459,7 +459,7 @@ async function onEditContent(event) {
 }
 
 export function initRepository() {
-  if ($('.repository').length === 0) {
+  if ($('.page-content.repository').length === 0) {
     return;
   }
 
diff --git a/web_src/js/index.js b/web_src/js/index.js
index 0c786f96fb..8bd219bbe1 100644
--- a/web_src/js/index.js
+++ b/web_src/js/index.js
@@ -83,6 +83,7 @@ import {initGiteaFomantic} from './modules/fomantic.js';
 import {onDomReady} from './utils/dom.js';
 import {initRepoIssueList} from './features/repo-issue-list.js';
 import {initCommonIssueListQuickGoto} from './features/common-issue-list.js';
+import {initRepoDiffCommitBranchesAndTags} from './features/repo-diff-commit.js';
 
 // Init Gitea's Fomantic settings
 initGiteaFomantic();
@@ -141,6 +142,7 @@ onDomReady(() => {
   initRepoCodeView();
   initRepoCommentForm();
   initRepoEllipsisButton();
+  initRepoDiffCommitBranchesAndTags();
   initRepoCommitLastCommitLoader();
   initRepoEditor();
   initRepoGraphGit();