diff --git a/modules/context/context_response.go b/modules/context/context_response.go index bb3ccf69ce421..9dc6d1fc0ec5f 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 729e3b4672a7c..c44882d886171 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 54e424bb832ab..8eaa17cb04182 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 25fb155435760..dc88c422b5ea1 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 e88f1139f8b7e..5b32591b89140 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 d7ef2fb82f229..0b519614453a7 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 0000000000000..2497910a838d7 --- /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 0000000000000..c19aa55c56197 --- /dev/null +++ b/templates/repo/commit_load_branches_and_tags.tmpl @@ -0,0 +1,18 @@ +
{{RenderCommitBody $.Context .Commit.Message $.RepoLink $.Repository.ComposeMetas}}{{end}} - {{if .BranchName}} - {{svg "octicon-git-branch" 16 "gt-mr-2"}}{{.BranchName}} - {{end}} - {{if .TagName}} - {{svg "octicon-tag" 16 "gt-mr-2"}}{{.TagName}} - {{end}} + {{template "repo/commit_load_branches_and_tags" .}}