2022-02-09 23:28:55 +03:00
// Copyright 2021 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2022-02-09 23:28:55 +03:00
package repo
import (
"bytes"
"errors"
"strings"
"code.gitea.io/gitea/models"
2023-06-29 13:03:20 +03:00
git_model "code.gitea.io/gitea/models/git"
2022-02-09 23:28:55 +03:00
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
2024-02-27 10:12:22 +03:00
"code.gitea.io/gitea/services/context"
2022-02-09 23:28:55 +03:00
"code.gitea.io/gitea/services/forms"
"code.gitea.io/gitea/services/repository/files"
)
var tplCherryPick base . TplName = "repo/editor/cherry_pick"
// CherryPick handles cherrypick GETs
func CherryPick ( ctx * context . Context ) {
2024-06-19 01:32:45 +03:00
ctx . Data [ "SHA" ] = ctx . PathParam ( ":sha" )
cherryPickCommit , err := ctx . Repo . GitRepo . GetCommit ( ctx . PathParam ( ":sha" ) )
2022-02-09 23:28:55 +03:00
if err != nil {
if git . IsErrNotExist ( err ) {
ctx . NotFound ( "Missing Commit" , err )
return
}
ctx . ServerError ( "GetCommit" , err )
return
}
if ctx . FormString ( "cherry-pick-type" ) == "revert" {
ctx . Data [ "CherryPickType" ] = "revert"
2024-06-19 01:32:45 +03:00
ctx . Data [ "commit_summary" ] = "revert " + ctx . PathParam ( ":sha" )
2022-02-09 23:28:55 +03:00
ctx . Data [ "commit_message" ] = "revert " + cherryPickCommit . Message ( )
} else {
ctx . Data [ "CherryPickType" ] = "cherry-pick"
splits := strings . SplitN ( cherryPickCommit . Message ( ) , "\n" , 2 )
ctx . Data [ "commit_summary" ] = splits [ 0 ]
ctx . Data [ "commit_message" ] = splits [ 1 ]
}
canCommit := renderCommitRights ( ctx )
2022-02-14 12:18:55 +03:00
ctx . Data [ "TreePath" ] = ""
2022-02-09 23:28:55 +03:00
if canCommit {
ctx . Data [ "commit_choice" ] = frmCommitChoiceDirect
} else {
ctx . Data [ "commit_choice" ] = frmCommitChoiceNewBranch
}
ctx . Data [ "new_branch_name" ] = GetUniquePatchBranchName ( ctx )
ctx . Data [ "last_commit" ] = ctx . Repo . CommitID
ctx . Data [ "LineWrapExtensions" ] = strings . Join ( setting . Repository . Editor . LineWrapExtensions , "," )
ctx . Data [ "BranchLink" ] = ctx . Repo . RepoLink + "/src/" + ctx . Repo . BranchNameSubURL ( )
ctx . HTML ( 200 , tplCherryPick )
}
// CherryPickPost handles cherrypick POSTs
func CherryPickPost ( ctx * context . Context ) {
form := web . GetForm ( ctx ) . ( * forms . CherryPickForm )
2024-06-19 01:32:45 +03:00
sha := ctx . PathParam ( ":sha" )
2022-02-09 23:28:55 +03:00
ctx . Data [ "SHA" ] = sha
if form . Revert {
ctx . Data [ "CherryPickType" ] = "revert"
} else {
ctx . Data [ "CherryPickType" ] = "cherry-pick"
}
canCommit := renderCommitRights ( ctx )
branchName := ctx . Repo . BranchName
if form . CommitChoice == frmCommitChoiceNewBranch {
branchName = form . NewBranchName
}
ctx . Data [ "commit_summary" ] = form . CommitSummary
ctx . Data [ "commit_message" ] = form . CommitMessage
ctx . Data [ "commit_choice" ] = form . CommitChoice
ctx . Data [ "new_branch_name" ] = form . NewBranchName
ctx . Data [ "last_commit" ] = ctx . Repo . CommitID
ctx . Data [ "LineWrapExtensions" ] = strings . Join ( setting . Repository . Editor . LineWrapExtensions , "," )
ctx . Data [ "BranchLink" ] = ctx . Repo . RepoLink + "/src/" + ctx . Repo . BranchNameSubURL ( )
if ctx . HasError ( ) {
ctx . HTML ( 200 , tplCherryPick )
return
}
// Cannot commit to a an existing branch if user doesn't have rights
if branchName == ctx . Repo . BranchName && ! canCommit {
ctx . Data [ "Err_NewBranchName" ] = true
ctx . Data [ "commit_choice" ] = frmCommitChoiceNewBranch
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.cannot_commit_to_protected_branch" , branchName ) , tplCherryPick , & form )
return
}
message := strings . TrimSpace ( form . CommitSummary )
if message == "" {
if form . Revert {
2024-02-15 00:48:45 +03:00
message = ctx . Locale . TrString ( "repo.commit.revert-header" , sha )
2022-02-09 23:28:55 +03:00
} else {
2024-02-15 00:48:45 +03:00
message = ctx . Locale . TrString ( "repo.commit.cherry-pick-header" , sha )
2022-02-09 23:28:55 +03:00
}
}
form . CommitMessage = strings . TrimSpace ( form . CommitMessage )
if len ( form . CommitMessage ) > 0 {
message += "\n\n" + form . CommitMessage
}
opts := & files . ApplyDiffPatchOptions {
LastCommitID : form . LastCommit ,
OldBranch : ctx . Repo . BranchName ,
NewBranch : branchName ,
Message : message ,
}
// First lets try the simple plain read-tree -m approach
opts . Content = sha
2022-03-22 10:03:22 +03:00
if _ , err := files . CherryPick ( ctx , ctx . Repo . Repository , ctx . Doer , form . Revert , opts ) ; err != nil {
2023-06-29 13:03:20 +03:00
if git_model . IsErrBranchAlreadyExists ( err ) {
2022-02-09 23:28:55 +03:00
// User has specified a branch that already exists
2023-06-29 13:03:20 +03:00
branchErr := err . ( git_model . ErrBranchAlreadyExists )
2022-02-09 23:28:55 +03:00
ctx . Data [ "Err_NewBranchName" ] = true
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.branch_already_exists" , branchErr . BranchName ) , tplCherryPick , & form )
return
} else if models . IsErrCommitIDDoesNotMatch ( err ) {
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.file_changed_while_editing" , ctx . Repo . RepoLink + "/compare/" + form . LastCommit + "..." + ctx . Repo . CommitID ) , tplPatchFile , & form )
return
}
// Drop through to the apply technique
buf := & bytes . Buffer { }
if form . Revert {
if err := git . GetReverseRawDiff ( ctx , ctx . Repo . Repository . RepoPath ( ) , sha , buf ) ; err != nil {
if git . IsErrNotExist ( err ) {
2024-06-19 01:32:45 +03:00
ctx . NotFound ( "GetRawDiff" , errors . New ( "commit " + ctx . PathParam ( ":sha" ) + " does not exist." ) )
2022-02-09 23:28:55 +03:00
return
}
ctx . ServerError ( "GetRawDiff" , err )
return
}
} else {
2022-04-25 21:45:18 +03:00
if err := git . GetRawDiff ( ctx . Repo . GitRepo , sha , git . RawDiffType ( "patch" ) , buf ) ; err != nil {
2022-02-09 23:28:55 +03:00
if git . IsErrNotExist ( err ) {
2024-06-19 01:32:45 +03:00
ctx . NotFound ( "GetRawDiff" , errors . New ( "commit " + ctx . PathParam ( ":sha" ) + " does not exist." ) )
2022-02-09 23:28:55 +03:00
return
}
ctx . ServerError ( "GetRawDiff" , err )
return
}
}
opts . Content = buf . String ( )
ctx . Data [ "FileContent" ] = opts . Content
2022-03-22 10:03:22 +03:00
if _ , err := files . ApplyDiffPatch ( ctx , ctx . Repo . Repository , ctx . Doer , opts ) ; err != nil {
2023-06-29 13:03:20 +03:00
if git_model . IsErrBranchAlreadyExists ( err ) {
2022-02-09 23:28:55 +03:00
// User has specified a branch that already exists
2023-06-29 13:03:20 +03:00
branchErr := err . ( git_model . ErrBranchAlreadyExists )
2022-02-09 23:28:55 +03:00
ctx . Data [ "Err_NewBranchName" ] = true
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.branch_already_exists" , branchErr . BranchName ) , tplCherryPick , & form )
return
} else if models . IsErrCommitIDDoesNotMatch ( err ) {
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.file_changed_while_editing" , ctx . Repo . RepoLink + "/compare/" + form . LastCommit + "..." + ctx . Repo . CommitID ) , tplPatchFile , & form )
return
}
2023-10-24 05:54:59 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.fail_to_apply_patch" , err ) , tplPatchFile , & form )
return
2022-02-09 23:28:55 +03:00
}
}
2022-12-10 05:46:31 +03:00
if form . CommitChoice == frmCommitChoiceNewBranch && ctx . Repo . Repository . UnitEnabled ( ctx , unit . TypePullRequests ) {
2022-02-09 23:28:55 +03:00
ctx . Redirect ( ctx . Repo . RepoLink + "/compare/" + util . PathEscapeSegments ( ctx . Repo . BranchName ) + "..." + util . PathEscapeSegments ( form . NewBranchName ) )
} else {
ctx . Redirect ( ctx . Repo . RepoLink + "/src/branch/" + util . PathEscapeSegments ( branchName ) )
}
}