You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cherry_pick.go 6.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. // Copyright 2021 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package repo
  4. import (
  5. "bytes"
  6. "errors"
  7. "strings"
  8. "code.gitea.io/gitea/models"
  9. "code.gitea.io/gitea/models/unit"
  10. "code.gitea.io/gitea/modules/base"
  11. "code.gitea.io/gitea/modules/context"
  12. "code.gitea.io/gitea/modules/git"
  13. "code.gitea.io/gitea/modules/setting"
  14. "code.gitea.io/gitea/modules/util"
  15. "code.gitea.io/gitea/modules/web"
  16. "code.gitea.io/gitea/services/forms"
  17. "code.gitea.io/gitea/services/repository/files"
  18. )
  19. var tplCherryPick base.TplName = "repo/editor/cherry_pick"
  20. // CherryPick handles cherrypick GETs
  21. func CherryPick(ctx *context.Context) {
  22. ctx.Data["SHA"] = ctx.Params(":sha")
  23. cherryPickCommit, err := ctx.Repo.GitRepo.GetCommit(ctx.Params(":sha"))
  24. if err != nil {
  25. if git.IsErrNotExist(err) {
  26. ctx.NotFound("Missing Commit", err)
  27. return
  28. }
  29. ctx.ServerError("GetCommit", err)
  30. return
  31. }
  32. if ctx.FormString("cherry-pick-type") == "revert" {
  33. ctx.Data["CherryPickType"] = "revert"
  34. ctx.Data["commit_summary"] = "revert " + ctx.Params(":sha")
  35. ctx.Data["commit_message"] = "revert " + cherryPickCommit.Message()
  36. } else {
  37. ctx.Data["CherryPickType"] = "cherry-pick"
  38. splits := strings.SplitN(cherryPickCommit.Message(), "\n", 2)
  39. ctx.Data["commit_summary"] = splits[0]
  40. ctx.Data["commit_message"] = splits[1]
  41. }
  42. canCommit := renderCommitRights(ctx)
  43. ctx.Data["TreePath"] = ""
  44. if canCommit {
  45. ctx.Data["commit_choice"] = frmCommitChoiceDirect
  46. } else {
  47. ctx.Data["commit_choice"] = frmCommitChoiceNewBranch
  48. }
  49. ctx.Data["new_branch_name"] = GetUniquePatchBranchName(ctx)
  50. ctx.Data["last_commit"] = ctx.Repo.CommitID
  51. ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",")
  52. ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
  53. ctx.HTML(200, tplCherryPick)
  54. }
  55. // CherryPickPost handles cherrypick POSTs
  56. func CherryPickPost(ctx *context.Context) {
  57. form := web.GetForm(ctx).(*forms.CherryPickForm)
  58. sha := ctx.Params(":sha")
  59. ctx.Data["SHA"] = sha
  60. if form.Revert {
  61. ctx.Data["CherryPickType"] = "revert"
  62. } else {
  63. ctx.Data["CherryPickType"] = "cherry-pick"
  64. }
  65. canCommit := renderCommitRights(ctx)
  66. branchName := ctx.Repo.BranchName
  67. if form.CommitChoice == frmCommitChoiceNewBranch {
  68. branchName = form.NewBranchName
  69. }
  70. ctx.Data["commit_summary"] = form.CommitSummary
  71. ctx.Data["commit_message"] = form.CommitMessage
  72. ctx.Data["commit_choice"] = form.CommitChoice
  73. ctx.Data["new_branch_name"] = form.NewBranchName
  74. ctx.Data["last_commit"] = ctx.Repo.CommitID
  75. ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",")
  76. ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
  77. if ctx.HasError() {
  78. ctx.HTML(200, tplCherryPick)
  79. return
  80. }
  81. // Cannot commit to a an existing branch if user doesn't have rights
  82. if branchName == ctx.Repo.BranchName && !canCommit {
  83. ctx.Data["Err_NewBranchName"] = true
  84. ctx.Data["commit_choice"] = frmCommitChoiceNewBranch
  85. ctx.RenderWithErr(ctx.Tr("repo.editor.cannot_commit_to_protected_branch", branchName), tplCherryPick, &form)
  86. return
  87. }
  88. message := strings.TrimSpace(form.CommitSummary)
  89. if message == "" {
  90. if form.Revert {
  91. message = ctx.Tr("repo.commit.revert-header", sha)
  92. } else {
  93. message = ctx.Tr("repo.commit.cherry-pick-header", sha)
  94. }
  95. }
  96. form.CommitMessage = strings.TrimSpace(form.CommitMessage)
  97. if len(form.CommitMessage) > 0 {
  98. message += "\n\n" + form.CommitMessage
  99. }
  100. opts := &files.ApplyDiffPatchOptions{
  101. LastCommitID: form.LastCommit,
  102. OldBranch: ctx.Repo.BranchName,
  103. NewBranch: branchName,
  104. Message: message,
  105. }
  106. // First lets try the simple plain read-tree -m approach
  107. opts.Content = sha
  108. if _, err := files.CherryPick(ctx, ctx.Repo.Repository, ctx.Doer, form.Revert, opts); err != nil {
  109. if models.IsErrBranchAlreadyExists(err) {
  110. // User has specified a branch that already exists
  111. branchErr := err.(models.ErrBranchAlreadyExists)
  112. ctx.Data["Err_NewBranchName"] = true
  113. ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplCherryPick, &form)
  114. return
  115. } else if models.IsErrCommitIDDoesNotMatch(err) {
  116. ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+form.LastCommit+"..."+ctx.Repo.CommitID), tplPatchFile, &form)
  117. return
  118. }
  119. // Drop through to the apply technique
  120. buf := &bytes.Buffer{}
  121. if form.Revert {
  122. if err := git.GetReverseRawDiff(ctx, ctx.Repo.Repository.RepoPath(), sha, buf); err != nil {
  123. if git.IsErrNotExist(err) {
  124. ctx.NotFound("GetRawDiff", errors.New("commit "+ctx.Params(":sha")+" does not exist."))
  125. return
  126. }
  127. ctx.ServerError("GetRawDiff", err)
  128. return
  129. }
  130. } else {
  131. if err := git.GetRawDiff(ctx.Repo.GitRepo, sha, git.RawDiffType("patch"), buf); err != nil {
  132. if git.IsErrNotExist(err) {
  133. ctx.NotFound("GetRawDiff", errors.New("commit "+ctx.Params(":sha")+" does not exist."))
  134. return
  135. }
  136. ctx.ServerError("GetRawDiff", err)
  137. return
  138. }
  139. }
  140. opts.Content = buf.String()
  141. ctx.Data["FileContent"] = opts.Content
  142. if _, err := files.ApplyDiffPatch(ctx, ctx.Repo.Repository, ctx.Doer, opts); err != nil {
  143. if models.IsErrBranchAlreadyExists(err) {
  144. // User has specified a branch that already exists
  145. branchErr := err.(models.ErrBranchAlreadyExists)
  146. ctx.Data["Err_NewBranchName"] = true
  147. ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplCherryPick, &form)
  148. return
  149. } else if models.IsErrCommitIDDoesNotMatch(err) {
  150. ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+form.LastCommit+"..."+ctx.Repo.CommitID), tplPatchFile, &form)
  151. return
  152. } else {
  153. ctx.RenderWithErr(ctx.Tr("repo.editor.fail_to_apply_patch", err), tplPatchFile, &form)
  154. return
  155. }
  156. }
  157. }
  158. if form.CommitChoice == frmCommitChoiceNewBranch && ctx.Repo.Repository.UnitEnabled(ctx, unit.TypePullRequests) {
  159. ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ctx.Repo.BranchName) + "..." + util.PathEscapeSegments(form.NewBranchName))
  160. } else {
  161. ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + util.PathEscapeSegments(branchName))
  162. }
  163. }