Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Copyright 2018 The Gitea Authors. All rights reserved.
  3. // SPDX-License-Identifier: MIT
  4. package repo
  5. import (
  6. "errors"
  7. "fmt"
  8. "net/http"
  9. "net/url"
  10. "strings"
  11. "code.gitea.io/gitea/models"
  12. git_model "code.gitea.io/gitea/models/git"
  13. repo_model "code.gitea.io/gitea/models/repo"
  14. "code.gitea.io/gitea/models/unit"
  15. "code.gitea.io/gitea/modules/base"
  16. "code.gitea.io/gitea/modules/context"
  17. "code.gitea.io/gitea/modules/git"
  18. "code.gitea.io/gitea/modules/log"
  19. repo_module "code.gitea.io/gitea/modules/repository"
  20. "code.gitea.io/gitea/modules/setting"
  21. "code.gitea.io/gitea/modules/util"
  22. "code.gitea.io/gitea/modules/web"
  23. "code.gitea.io/gitea/routers/utils"
  24. "code.gitea.io/gitea/services/forms"
  25. release_service "code.gitea.io/gitea/services/release"
  26. repo_service "code.gitea.io/gitea/services/repository"
  27. )
  28. const (
  29. tplBranch base.TplName = "repo/branch/list"
  30. )
  31. // Branches render repository branch page
  32. func Branches(ctx *context.Context) {
  33. ctx.Data["Title"] = "Branches"
  34. ctx.Data["IsRepoToolbarBranches"] = true
  35. ctx.Data["AllowsPulls"] = ctx.Repo.Repository.AllowsPulls()
  36. ctx.Data["IsWriter"] = ctx.Repo.CanWrite(unit.TypeCode)
  37. ctx.Data["IsMirror"] = ctx.Repo.Repository.IsMirror
  38. ctx.Data["CanPull"] = ctx.Repo.CanWrite(unit.TypeCode) ||
  39. (ctx.IsSigned && repo_model.HasForkedRepo(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID))
  40. ctx.Data["PageIsViewCode"] = true
  41. ctx.Data["PageIsBranches"] = true
  42. page := ctx.FormInt("page")
  43. if page <= 1 {
  44. page = 1
  45. }
  46. pageSize := setting.Git.BranchesRangeSize
  47. kw := ctx.FormString("q")
  48. defaultBranch, branches, branchesCount, err := repo_service.LoadBranches(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, util.OptionalBoolNone, kw, page, pageSize)
  49. if err != nil {
  50. ctx.ServerError("LoadBranches", err)
  51. return
  52. }
  53. commitIDs := []string{defaultBranch.DBBranch.CommitID}
  54. for _, branch := range branches {
  55. commitIDs = append(commitIDs, branch.DBBranch.CommitID)
  56. }
  57. commitStatuses, err := git_model.GetLatestCommitStatusForRepoCommitIDs(ctx, ctx.Repo.Repository.ID, commitIDs)
  58. if err != nil {
  59. ctx.ServerError("LoadBranches", err)
  60. return
  61. }
  62. commitStatus := make(map[string]*git_model.CommitStatus)
  63. for commitID, cs := range commitStatuses {
  64. commitStatus[commitID] = git_model.CalcCommitStatus(cs)
  65. }
  66. ctx.Data["Keyword"] = kw
  67. ctx.Data["Branches"] = branches
  68. ctx.Data["CommitStatus"] = commitStatus
  69. ctx.Data["CommitStatuses"] = commitStatuses
  70. ctx.Data["DefaultBranchBranch"] = defaultBranch
  71. pager := context.NewPagination(int(branchesCount), pageSize, page, 5)
  72. pager.SetDefaultParams(ctx)
  73. ctx.Data["Page"] = pager
  74. ctx.HTML(http.StatusOK, tplBranch)
  75. }
  76. // DeleteBranchPost responses for delete merged branch
  77. func DeleteBranchPost(ctx *context.Context) {
  78. defer redirect(ctx)
  79. branchName := ctx.FormString("name")
  80. if err := repo_service.DeleteBranch(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, branchName); err != nil {
  81. switch {
  82. case git.IsErrBranchNotExist(err):
  83. log.Debug("DeleteBranch: Can't delete non existing branch '%s'", branchName)
  84. ctx.Flash.Error(ctx.Tr("repo.branch.deletion_failed", branchName))
  85. case errors.Is(err, repo_service.ErrBranchIsDefault):
  86. log.Debug("DeleteBranch: Can't delete default branch '%s'", branchName)
  87. ctx.Flash.Error(ctx.Tr("repo.branch.default_deletion_failed", branchName))
  88. case errors.Is(err, git_model.ErrBranchIsProtected):
  89. log.Debug("DeleteBranch: Can't delete protected branch '%s'", branchName)
  90. ctx.Flash.Error(ctx.Tr("repo.branch.protected_deletion_failed", branchName))
  91. default:
  92. log.Error("DeleteBranch: %v", err)
  93. ctx.Flash.Error(ctx.Tr("repo.branch.deletion_failed", branchName))
  94. }
  95. return
  96. }
  97. ctx.Flash.Success(ctx.Tr("repo.branch.deletion_success", branchName))
  98. }
  99. // RestoreBranchPost responses for delete merged branch
  100. func RestoreBranchPost(ctx *context.Context) {
  101. defer redirect(ctx)
  102. branchID := ctx.FormInt64("branch_id")
  103. branchName := ctx.FormString("name")
  104. deletedBranch, err := git_model.GetDeletedBranchByID(ctx, ctx.Repo.Repository.ID, branchID)
  105. if err != nil {
  106. log.Error("GetDeletedBranchByID: %v", err)
  107. ctx.Flash.Error(ctx.Tr("repo.branch.restore_failed", branchName))
  108. return
  109. } else if deletedBranch == nil {
  110. log.Debug("RestoreBranch: Can't restore branch[%d] '%s', as it does not exist", branchID, branchName)
  111. ctx.Flash.Error(ctx.Tr("repo.branch.restore_failed", branchName))
  112. return
  113. }
  114. if err := git.Push(ctx, ctx.Repo.Repository.RepoPath(), git.PushOptions{
  115. Remote: ctx.Repo.Repository.RepoPath(),
  116. Branch: fmt.Sprintf("%s:%s%s", deletedBranch.CommitID, git.BranchPrefix, deletedBranch.Name),
  117. Env: repo_module.PushingEnvironment(ctx.Doer, ctx.Repo.Repository),
  118. }); err != nil {
  119. if strings.Contains(err.Error(), "already exists") {
  120. log.Debug("RestoreBranch: Can't restore branch '%s', since one with same name already exist", deletedBranch.Name)
  121. ctx.Flash.Error(ctx.Tr("repo.branch.already_exists", deletedBranch.Name))
  122. return
  123. }
  124. log.Error("RestoreBranch: CreateBranch: %v", err)
  125. ctx.Flash.Error(ctx.Tr("repo.branch.restore_failed", deletedBranch.Name))
  126. return
  127. }
  128. // Don't return error below this
  129. if err := repo_service.PushUpdate(
  130. &repo_module.PushUpdateOptions{
  131. RefFullName: git.RefNameFromBranch(deletedBranch.Name),
  132. OldCommitID: git.EmptySHA,
  133. NewCommitID: deletedBranch.CommitID,
  134. PusherID: ctx.Doer.ID,
  135. PusherName: ctx.Doer.Name,
  136. RepoUserName: ctx.Repo.Owner.Name,
  137. RepoName: ctx.Repo.Repository.Name,
  138. }); err != nil {
  139. log.Error("RestoreBranch: Update: %v", err)
  140. }
  141. ctx.Flash.Success(ctx.Tr("repo.branch.restore_success", deletedBranch.Name))
  142. }
  143. func redirect(ctx *context.Context) {
  144. ctx.JSONRedirect(ctx.Repo.RepoLink + "/branches?page=" + url.QueryEscape(ctx.FormString("page")))
  145. }
  146. // CreateBranch creates new branch in repository
  147. func CreateBranch(ctx *context.Context) {
  148. form := web.GetForm(ctx).(*forms.NewBranchForm)
  149. if !ctx.Repo.CanCreateBranch() {
  150. ctx.NotFound("CreateBranch", nil)
  151. return
  152. }
  153. if ctx.HasError() {
  154. ctx.Flash.Error(ctx.GetErrMsg())
  155. ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
  156. return
  157. }
  158. var err error
  159. if form.CreateTag {
  160. target := ctx.Repo.CommitID
  161. if ctx.Repo.IsViewBranch {
  162. target = ctx.Repo.BranchName
  163. }
  164. err = release_service.CreateNewTag(ctx, ctx.Doer, ctx.Repo.Repository, target, form.NewBranchName, "")
  165. } else if ctx.Repo.IsViewBranch {
  166. err = repo_service.CreateNewBranch(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.Repo.BranchName, form.NewBranchName)
  167. } else {
  168. err = repo_service.CreateNewBranchFromCommit(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.Repo.CommitID, form.NewBranchName)
  169. }
  170. if err != nil {
  171. if models.IsErrProtectedTagName(err) {
  172. ctx.Flash.Error(ctx.Tr("repo.release.tag_name_protected"))
  173. ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
  174. return
  175. }
  176. if models.IsErrTagAlreadyExists(err) {
  177. e := err.(models.ErrTagAlreadyExists)
  178. ctx.Flash.Error(ctx.Tr("repo.branch.tag_collision", e.TagName))
  179. ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
  180. return
  181. }
  182. if git_model.IsErrBranchAlreadyExists(err) || git.IsErrPushOutOfDate(err) {
  183. ctx.Flash.Error(ctx.Tr("repo.branch.branch_already_exists", form.NewBranchName))
  184. ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
  185. return
  186. }
  187. if git_model.IsErrBranchNameConflict(err) {
  188. e := err.(git_model.ErrBranchNameConflict)
  189. ctx.Flash.Error(ctx.Tr("repo.branch.branch_name_conflict", form.NewBranchName, e.BranchName))
  190. ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
  191. return
  192. }
  193. if git.IsErrPushRejected(err) {
  194. e := err.(*git.ErrPushRejected)
  195. if len(e.Message) == 0 {
  196. ctx.Flash.Error(ctx.Tr("repo.editor.push_rejected_no_message"))
  197. } else {
  198. flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{
  199. "Message": ctx.Tr("repo.editor.push_rejected"),
  200. "Summary": ctx.Tr("repo.editor.push_rejected_summary"),
  201. "Details": utils.SanitizeFlashErrorString(e.Message),
  202. })
  203. if err != nil {
  204. ctx.ServerError("UpdatePullRequest.HTMLString", err)
  205. return
  206. }
  207. ctx.Flash.Error(flashError)
  208. }
  209. ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
  210. return
  211. }
  212. ctx.ServerError("CreateNewBranch", err)
  213. return
  214. }
  215. if form.CreateTag {
  216. ctx.Flash.Success(ctx.Tr("repo.tag.create_success", form.NewBranchName))
  217. ctx.Redirect(ctx.Repo.RepoLink + "/src/tag/" + util.PathEscapeSegments(form.NewBranchName))
  218. return
  219. }
  220. ctx.Flash.Success(ctx.Tr("repo.branch.create_success", form.NewBranchName))
  221. ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + util.PathEscapeSegments(form.NewBranchName) + "/" + util.PathEscapeSegments(form.CurrentPath))
  222. }