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  }