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