2018-01-05 13:56:52 +03:00
// Copyright 2018 The Gitea Authors.
// Copyright 2014 The Gogs Authors.
// All rights reserved.
2014-03-24 14:25:15 +04:00
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package repo
import (
2021-06-07 17:52:59 +03:00
"errors"
2016-10-12 16:28:51 +03:00
"fmt"
2021-11-16 21:18:25 +03:00
"html"
2019-12-16 09:20:25 +03:00
"net/http"
2021-11-16 21:18:25 +03:00
"net/url"
"strconv"
2015-08-08 17:43:14 +03:00
"strings"
2020-01-17 09:03:40 +03:00
"time"
2015-08-08 17:43:14 +03:00
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/models"
2021-09-24 14:32:56 +03:00
"code.gitea.io/gitea/models/db"
2021-12-10 04:27:50 +03:00
repo_model "code.gitea.io/gitea/models/repo"
2021-11-09 22:57:58 +03:00
"code.gitea.io/gitea/models/unit"
2021-11-24 12:49:20 +03:00
user_model "code.gitea.io/gitea/models/user"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
2019-03-27 12:33:00 +03:00
"code.gitea.io/gitea/modules/git"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/log"
2017-01-28 18:59:58 +03:00
"code.gitea.io/gitea/modules/notification"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/setting"
2020-06-07 03:45:12 +03:00
"code.gitea.io/gitea/modules/structs"
2020-10-05 08:49:33 +03:00
"code.gitea.io/gitea/modules/upload"
2019-01-21 14:45:32 +03:00
"code.gitea.io/gitea/modules/util"
2021-01-26 18:36:53 +03:00
"code.gitea.io/gitea/modules/web"
2021-01-30 11:55:53 +03:00
"code.gitea.io/gitea/modules/web/middleware"
2020-02-22 16:08:48 +03:00
"code.gitea.io/gitea/routers/utils"
2021-04-06 22:44:05 +03:00
"code.gitea.io/gitea/services/forms"
2019-09-06 05:20:09 +03:00
"code.gitea.io/gitea/services/gitdiff"
2019-09-27 03:22:36 +03:00
pull_service "code.gitea.io/gitea/services/pull"
2019-10-26 09:54:11 +03:00
repo_service "code.gitea.io/gitea/services/repository"
2014-03-24 14:25:15 +04:00
)
2014-06-23 07:11:12 +04:00
const (
2016-11-24 10:04:31 +03:00
tplFork base . TplName = "repo/pulls/fork"
2019-06-07 23:29:29 +03:00
tplCompareDiff base . TplName = "repo/diff/compare"
2016-11-24 10:04:31 +03:00
tplPullCommits base . TplName = "repo/pulls/commits"
tplPullFiles base . TplName = "repo/pulls/files"
2016-02-18 01:21:31 +03:00
2016-11-24 10:04:31 +03:00
pullRequestTemplateKey = "PullRequestTemplate"
2016-02-18 01:21:31 +03:00
)
2022-01-20 20:46:10 +03:00
var pullRequestTemplateCandidates = [ ] string {
"PULL_REQUEST_TEMPLATE.md" ,
"pull_request_template.md" ,
".gitea/PULL_REQUEST_TEMPLATE.md" ,
".gitea/pull_request_template.md" ,
".github/PULL_REQUEST_TEMPLATE.md" ,
".github/pull_request_template.md" ,
}
2014-06-23 07:11:12 +04:00
2021-12-10 04:27:50 +03:00
func getRepository ( ctx * context . Context , repoID int64 ) * repo_model . Repository {
repo , err := repo_model . GetRepositoryByID ( repoID )
2015-08-08 12:10:34 +03:00
if err != nil {
2021-12-10 04:27:50 +03:00
if repo_model . IsErrRepoNotExist ( err ) {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "GetRepositoryByID" , nil )
2015-08-08 12:10:34 +03:00
} else {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetRepositoryByID" , err )
2015-08-08 12:10:34 +03:00
}
return nil
}
2015-09-01 18:57:02 +03:00
2022-03-22 10:03:22 +03:00
perm , err := models . GetUserRepoPermission ( repo , ctx . Doer )
2018-11-28 14:26:14 +03:00
if err != nil {
ctx . ServerError ( "GetUserRepoPermission" , err )
return nil
}
2021-11-09 22:57:58 +03:00
if ! perm . CanRead ( unit . TypeCode ) {
2019-11-11 18:15:29 +03:00
log . Trace ( "Permission Denied: User %-v cannot read %-v of repo %-v\n" +
"User in repo has Permissions: %-+v" ,
2022-03-22 10:03:22 +03:00
ctx . Doer ,
2021-11-09 22:57:58 +03:00
unit . TypeCode ,
2019-11-11 18:15:29 +03:00
ctx . Repo ,
perm )
ctx . NotFound ( "getRepository" , nil )
return nil
}
return repo
}
2021-12-10 04:27:50 +03:00
func getForkRepository ( ctx * context . Context ) * repo_model . Repository {
2019-11-11 18:15:29 +03:00
forkRepo := getRepository ( ctx , ctx . ParamsInt64 ( ":repoid" ) )
if ctx . Written ( ) {
return nil
}
if forkRepo . IsEmpty {
log . Trace ( "Empty repository %-v" , forkRepo )
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "getForkRepository" , nil )
2015-09-01 18:57:02 +03:00
return nil
}
2021-12-10 04:27:50 +03:00
if err := forkRepo . GetOwner ( db . DefaultContext ) ; err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetOwner" , err )
2015-08-08 12:10:34 +03:00
return nil
}
2020-06-07 03:45:12 +03:00
ctx . Data [ "repo_name" ] = forkRepo . Name
ctx . Data [ "description" ] = forkRepo . Description
ctx . Data [ "IsPrivate" ] = forkRepo . IsPrivate || forkRepo . Owner . Visibility == structs . VisibleTypePrivate
2022-03-22 10:03:22 +03:00
canForkToUser := forkRepo . OwnerID != ctx . Doer . ID && ! repo_model . HasForkedRepo ( ctx . Doer . ID , forkRepo . ID )
2020-06-07 03:45:12 +03:00
2021-11-16 21:18:25 +03:00
ctx . Data [ "ForkRepo" ] = forkRepo
2015-08-08 12:10:34 +03:00
2022-03-22 10:03:22 +03:00
ownedOrgs , err := models . GetOrgsCanCreateRepoByUserID ( ctx . Doer . ID )
2021-11-22 18:21:55 +03:00
if err != nil {
2021-11-25 08:03:03 +03:00
ctx . ServerError ( "GetOrgsCanCreateRepoByUserID" , err )
2015-08-08 12:10:34 +03:00
return nil
}
2021-11-22 18:21:55 +03:00
var orgs [ ] * models . Organization
for _ , org := range ownedOrgs {
2021-12-12 18:48:20 +03:00
if forkRepo . OwnerID != org . ID && ! repo_model . HasForkedRepo ( org . ID , forkRepo . ID ) {
2017-10-15 18:06:07 +03:00
orgs = append ( orgs , org )
}
}
2017-11-06 07:12:55 +03:00
2022-01-20 20:46:10 +03:00
traverseParentRepo := forkRepo
2017-11-06 07:12:55 +03:00
for {
2022-03-22 10:03:22 +03:00
if ctx . Doer . ID == traverseParentRepo . OwnerID {
2017-11-06 07:12:55 +03:00
canForkToUser = false
} else {
for i , org := range orgs {
if org . ID == traverseParentRepo . OwnerID {
orgs = append ( orgs [ : i ] , orgs [ i + 1 : ] ... )
break
}
}
}
if ! traverseParentRepo . IsFork {
break
}
2021-12-10 04:27:50 +03:00
traverseParentRepo , err = repo_model . GetRepositoryByID ( traverseParentRepo . ForkID )
2017-11-06 07:12:55 +03:00
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetRepositoryByID" , err )
2017-11-06 07:12:55 +03:00
return nil
}
}
ctx . Data [ "CanForkToUser" ] = canForkToUser
2017-10-15 18:06:07 +03:00
ctx . Data [ "Orgs" ] = orgs
if canForkToUser {
2022-03-22 10:03:22 +03:00
ctx . Data [ "ContextUser" ] = ctx . Doer
2017-10-15 18:06:07 +03:00
} else if len ( orgs ) > 0 {
ctx . Data [ "ContextUser" ] = orgs [ 0 ]
}
2015-08-08 12:10:34 +03:00
return forkRepo
}
2016-11-24 10:04:31 +03:00
// Fork render repository fork page
2016-03-11 19:56:52 +03:00
func Fork ( ctx * context . Context ) {
2015-08-08 12:10:34 +03:00
ctx . Data [ "Title" ] = ctx . Tr ( "new_fork" )
getForkRepository ( ctx )
if ctx . Written ( ) {
return
}
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , tplFork )
2015-08-08 12:10:34 +03:00
}
2016-11-24 10:04:31 +03:00
// ForkPost response for forking a repository
2021-01-26 18:36:53 +03:00
func ForkPost ( ctx * context . Context ) {
2021-04-06 22:44:05 +03:00
form := web . GetForm ( ctx ) . ( * forms . CreateRepoForm )
2015-08-08 12:10:34 +03:00
ctx . Data [ "Title" ] = ctx . Tr ( "new_fork" )
2017-10-15 18:06:07 +03:00
ctxUser := checkContextUser ( ctx , form . UID )
2015-08-08 12:10:34 +03:00
if ctx . Written ( ) {
return
}
2017-10-15 18:06:07 +03:00
forkRepo := getForkRepository ( ctx )
2015-08-08 12:10:34 +03:00
if ctx . Written ( ) {
return
}
2017-10-15 18:06:07 +03:00
2015-08-08 12:10:34 +03:00
ctx . Data [ "ContextUser" ] = ctxUser
if ctx . HasError ( ) {
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , tplFork )
2015-08-08 12:10:34 +03:00
return
}
2017-11-06 07:12:55 +03:00
var err error
2022-01-20 20:46:10 +03:00
traverseParentRepo := forkRepo
2017-11-06 07:12:55 +03:00
for {
if ctxUser . ID == traverseParentRepo . OwnerID {
ctx . RenderWithErr ( ctx . Tr ( "repo.settings.new_owner_has_same_repo" ) , tplFork , & form )
return
}
2021-12-12 18:48:20 +03:00
repo := repo_model . GetForkedRepo ( ctxUser . ID , traverseParentRepo . ID )
2021-11-22 18:21:55 +03:00
if repo != nil {
2021-11-16 21:18:25 +03:00
ctx . Redirect ( ctxUser . HomeLink ( ) + "/" + url . PathEscape ( repo . Name ) )
2017-11-06 07:12:55 +03:00
return
}
if ! traverseParentRepo . IsFork {
break
}
2021-12-10 04:27:50 +03:00
traverseParentRepo , err = repo_model . GetRepositoryByID ( traverseParentRepo . ForkID )
2017-11-06 07:12:55 +03:00
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetRepositoryByID" , err )
2017-11-06 07:12:55 +03:00
return
}
2017-07-26 10:17:38 +03:00
}
2021-11-25 08:03:03 +03:00
// Check if user is allowed to create repo's on the organization.
2015-08-08 12:10:34 +03:00
if ctxUser . IsOrganization ( ) {
2022-03-22 10:03:22 +03:00
isAllowedToFork , err := models . OrgFromUser ( ctxUser ) . CanCreateOrgRepo ( ctx . Doer . ID )
2017-12-21 10:43:26 +03:00
if err != nil {
2021-11-25 08:03:03 +03:00
ctx . ServerError ( "CanCreateOrgRepo" , err )
2017-12-21 10:43:26 +03:00
return
2021-11-25 08:03:03 +03:00
} else if ! isAllowedToFork {
2021-04-05 18:30:52 +03:00
ctx . Error ( http . StatusForbidden )
2015-08-08 12:10:34 +03:00
return
}
}
2022-03-22 10:03:22 +03:00
repo , err := repo_service . ForkRepository ( ctx . Doer , ctxUser , repo_service . ForkRepoOptions {
2021-08-28 11:37:14 +03:00
BaseRepo : forkRepo ,
Name : form . RepoName ,
Description : form . Description ,
} )
2015-08-08 12:10:34 +03:00
if err != nil {
2015-08-31 10:24:28 +03:00
ctx . Data [ "Err_RepoName" ] = true
2015-08-08 12:10:34 +03:00
switch {
2021-12-12 18:48:20 +03:00
case repo_model . IsErrRepoAlreadyExist ( err ) :
2016-11-24 10:04:31 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.settings.new_owner_has_same_repo" ) , tplFork , & form )
2021-11-24 12:49:20 +03:00
case db . IsErrNameReserved ( err ) :
ctx . RenderWithErr ( ctx . Tr ( "repo.form.name_reserved" , err . ( db . ErrNameReserved ) . Name ) , tplFork , & form )
case db . IsErrNamePatternNotAllowed ( err ) :
ctx . RenderWithErr ( ctx . Tr ( "repo.form.name_pattern_not_allowed" , err . ( db . ErrNamePatternNotAllowed ) . Pattern ) , tplFork , & form )
2015-08-08 12:10:34 +03:00
default :
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "ForkPost" , err )
2015-08-08 12:10:34 +03:00
}
return
}
2015-08-08 17:43:14 +03:00
log . Trace ( "Repository forked[%d]: %s/%s" , forkRepo . ID , ctxUser . Name , repo . Name )
2021-11-16 21:18:25 +03:00
ctx . Redirect ( ctxUser . HomeLink ( ) + "/" + url . PathEscape ( repo . Name ) )
2015-08-08 12:10:34 +03:00
}
2016-03-11 19:56:52 +03:00
func checkPullInfo ( ctx * context . Context ) * models . Issue {
2015-10-19 02:30:39 +03:00
issue , err := models . GetIssueByIndex ( ctx . Repo . Repository . ID , ctx . ParamsInt64 ( ":index" ) )
2015-09-02 11:08:05 +03:00
if err != nil {
if models . IsErrIssueNotExist ( err ) {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "GetIssueByIndex" , err )
2015-09-02 11:08:05 +03:00
} else {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetIssueByIndex" , err )
2015-09-02 11:08:05 +03:00
}
return nil
}
2018-12-13 18:55:43 +03:00
if err = issue . LoadPoster ( ) ; err != nil {
ctx . ServerError ( "LoadPoster" , err )
return nil
}
2019-10-23 20:54:13 +03:00
if err := issue . LoadRepo ( ) ; err != nil {
ctx . ServerError ( "LoadRepo" , err )
return nil
}
2016-10-12 16:28:51 +03:00
ctx . Data [ "Title" ] = fmt . Sprintf ( "#%d - %s" , issue . Index , issue . Title )
2015-10-19 02:30:39 +03:00
ctx . Data [ "Issue" ] = issue
2015-09-02 11:08:05 +03:00
2015-10-19 02:30:39 +03:00
if ! issue . IsPull {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "ViewPullCommits" , nil )
2015-09-02 11:08:05 +03:00
return nil
}
2018-12-13 18:55:43 +03:00
if err = issue . LoadPullRequest ( ) ; err != nil {
ctx . ServerError ( "LoadPullRequest" , err )
return nil
}
2020-03-03 01:31:55 +03:00
if err = issue . PullRequest . LoadHeadRepo ( ) ; err != nil {
ctx . ServerError ( "LoadHeadRepo" , err )
2015-10-24 10:36:47 +03:00
return nil
2015-09-02 11:08:05 +03:00
}
if ctx . IsSigned {
// Update issue-user.
2022-03-22 10:03:22 +03:00
if err = issue . ReadBy ( ctx . Doer . ID ) ; err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "ReadBy" , err )
2015-09-02 11:08:05 +03:00
return nil
}
}
2015-10-19 02:30:39 +03:00
return issue
2015-09-02 11:08:05 +03:00
}
2017-10-04 20:35:01 +03:00
func setMergeTarget ( ctx * context . Context , pull * models . PullRequest ) {
2019-10-18 14:13:31 +03:00
if ctx . Repo . Owner . Name == pull . MustHeadUserName ( ) {
2017-10-04 20:35:01 +03:00
ctx . Data [ "HeadTarget" ] = pull . HeadBranch
} else if pull . HeadRepo == nil {
2019-10-18 14:13:31 +03:00
ctx . Data [ "HeadTarget" ] = pull . MustHeadUserName ( ) + ":" + pull . HeadBranch
2017-10-04 20:35:01 +03:00
} else {
2019-10-18 14:13:31 +03:00
ctx . Data [ "HeadTarget" ] = pull . MustHeadUserName ( ) + "/" + pull . HeadRepo . Name + ":" + pull . HeadBranch
2017-10-04 20:35:01 +03:00
}
ctx . Data [ "BaseTarget" ] = pull . BaseBranch
2021-02-18 05:45:49 +03:00
ctx . Data [ "HeadBranchHTMLURL" ] = pull . GetHeadBranchHTMLURL ( )
ctx . Data [ "BaseBranchHTMLURL" ] = pull . GetBaseBranchHTMLURL ( )
2017-10-04 20:35:01 +03:00
}
2016-11-24 10:04:31 +03:00
// PrepareMergedViewPullInfo show meta information for a merged pull request view page
2019-06-07 23:29:29 +03:00
func PrepareMergedViewPullInfo ( ctx * context . Context , issue * models . Issue ) * git . CompareInfo {
2016-08-16 20:19:09 +03:00
pull := issue . PullRequest
2015-09-02 16:26:56 +03:00
2017-10-04 20:35:01 +03:00
setMergeTarget ( ctx , pull )
ctx . Data [ "HasMerged" ] = true
2021-12-23 11:32:29 +03:00
var baseCommit string
// Some migrated PR won't have any Base SHA and lose history, try to get one
if pull . MergeBase == "" {
var commitSHA , parentCommit string
// If there is a head or a patch file, and it is readable, grab info
2021-12-23 16:44:00 +03:00
commitSHA , err := ctx . Repo . GitRepo . GetRefCommitID ( pull . GetGitRefName ( ) )
2021-12-23 11:32:29 +03:00
if err != nil {
// Head File does not exist, try the patch
commitSHA , err = ctx . Repo . GitRepo . ReadPatchCommit ( pull . Index )
if err == nil {
// Recreate pull head in files for next time
2021-12-23 16:44:00 +03:00
if err := ctx . Repo . GitRepo . SetReference ( pull . GetGitRefName ( ) , commitSHA ) ; err != nil {
2021-12-23 11:32:29 +03:00
log . Error ( "Could not write head file" , err )
}
} else {
// There is no history available
log . Trace ( "No history file available for PR %d" , pull . Index )
}
}
if commitSHA != "" {
// Get immediate parent of the first commit in the patch, grab history back
2022-02-06 22:01:47 +03:00
parentCommit , err = git . NewCommand ( ctx , "rev-list" , "-1" , "--skip=1" , commitSHA ) . RunInDir ( ctx . Repo . GitRepo . Path )
2021-12-23 11:32:29 +03:00
if err == nil {
parentCommit = strings . TrimSpace ( parentCommit )
}
// Special case on Git < 2.25 that doesn't fail on immediate empty history
if err != nil || parentCommit == "" {
log . Info ( "No known parent commit for PR %d, error: %v" , pull . Index , err )
// bring at least partial history if it can work
parentCommit = commitSHA
}
}
baseCommit = parentCommit
} else {
// Keep an empty history or original commit
baseCommit = pull . MergeBase
}
2019-06-12 02:32:08 +03:00
compareInfo , err := ctx . Repo . GitRepo . GetCompareInfo ( ctx . Repo . Repository . RepoPath ( ) ,
2022-01-18 10:45:43 +03:00
baseCommit , pull . GetGitRefName ( ) , false , false )
2015-09-02 16:26:56 +03:00
if err != nil {
2020-10-20 15:52:54 +03:00
if strings . Contains ( err . Error ( ) , "fatal: Not a valid object name" ) || strings . Contains ( err . Error ( ) , "unknown revision or path not in the working tree" ) {
2018-08-01 06:00:35 +03:00
ctx . Data [ "IsPullRequestBroken" ] = true
2020-03-03 01:31:55 +03:00
ctx . Data [ "BaseTarget" ] = pull . BaseBranch
2018-01-19 09:18:51 +03:00
ctx . Data [ "NumCommits" ] = 0
ctx . Data [ "NumFiles" ] = 0
return nil
}
2019-06-07 23:29:29 +03:00
ctx . ServerError ( "GetCompareInfo" , err )
2018-01-19 09:18:51 +03:00
return nil
2015-09-02 16:26:56 +03:00
}
2021-08-09 21:08:51 +03:00
ctx . Data [ "NumCommits" ] = len ( compareInfo . Commits )
2019-06-12 02:32:08 +03:00
ctx . Data [ "NumFiles" ] = compareInfo . NumFiles
2020-12-18 15:37:55 +03:00
2021-08-09 21:08:51 +03:00
if len ( compareInfo . Commits ) != 0 {
sha := compareInfo . Commits [ 0 ] . ID . String ( )
2021-12-15 08:39:34 +03:00
commitStatuses , _ , err := models . GetLatestCommitStatus ( ctx . Repo . Repository . ID , sha , db . ListOptions { } )
2020-12-18 15:37:55 +03:00
if err != nil {
ctx . ServerError ( "GetLatestCommitStatus" , err )
return nil
}
if len ( commitStatuses ) != 0 {
ctx . Data [ "LatestCommitStatuses" ] = commitStatuses
ctx . Data [ "LatestCommitStatus" ] = models . CalcCommitStatus ( commitStatuses )
}
}
2019-06-12 02:32:08 +03:00
return compareInfo
2015-09-02 16:26:56 +03:00
}
2016-11-24 10:04:31 +03:00
// PrepareViewPullInfo show meta information for a pull request preview page
2019-06-07 23:29:29 +03:00
func PrepareViewPullInfo ( ctx * context . Context , issue * models . Issue ) * git . CompareInfo {
2022-02-11 11:02:53 +03:00
ctx . Data [ "PullRequestWorkInProgressPrefixes" ] = setting . Repository . PullRequest . WorkInProgressPrefixes
2015-09-02 11:08:05 +03:00
repo := ctx . Repo . Repository
2016-08-16 20:19:09 +03:00
pull := issue . PullRequest
2015-09-02 11:08:05 +03:00
2020-03-03 01:31:55 +03:00
if err := pull . LoadHeadRepo ( ) ; err != nil {
ctx . ServerError ( "LoadHeadRepo" , err )
2015-10-24 10:36:47 +03:00
return nil
}
2020-03-03 01:31:55 +03:00
if err := pull . LoadBaseRepo ( ) ; err != nil {
ctx . ServerError ( "LoadBaseRepo" , err )
2020-01-07 20:06:14 +03:00
return nil
}
2017-10-04 20:35:01 +03:00
setMergeTarget ( ctx , pull )
2020-01-07 20:06:14 +03:00
if err := pull . LoadProtectedBranch ( ) ; err != nil {
2020-01-17 09:03:40 +03:00
ctx . ServerError ( "LoadProtectedBranch" , err )
2019-09-18 08:39:45 +03:00
return nil
}
ctx . Data [ "EnableStatusCheck" ] = pull . ProtectedBranch != nil && pull . ProtectedBranch . EnableStatusCheck
2022-01-20 02:26:57 +03:00
var baseGitRepo * git . Repository
if pull . BaseRepoID == ctx . Repo . Repository . ID && ctx . Repo . GitRepo != nil {
baseGitRepo = ctx . Repo . GitRepo
} else {
baseGitRepo , err := git . OpenRepositoryCtx ( ctx , pull . BaseRepo . RepoPath ( ) )
if err != nil {
ctx . ServerError ( "OpenRepository" , err )
return nil
}
defer baseGitRepo . Close ( )
2020-01-07 20:06:14 +03:00
}
2020-03-05 21:51:21 +03:00
if ! baseGitRepo . IsBranchExist ( pull . BaseBranch ) {
ctx . Data [ "IsPullRequestBroken" ] = true
ctx . Data [ "BaseTarget" ] = pull . BaseBranch
ctx . Data [ "HeadTarget" ] = pull . HeadBranch
2020-03-31 16:42:44 +03:00
sha , err := baseGitRepo . GetRefCommitID ( pull . GetGitRefName ( ) )
if err != nil {
ctx . ServerError ( fmt . Sprintf ( "GetRefCommitID(%s)" , pull . GetGitRefName ( ) ) , err )
return nil
}
2021-12-15 08:39:34 +03:00
commitStatuses , _ , err := models . GetLatestCommitStatus ( repo . ID , sha , db . ListOptions { } )
2020-03-31 16:42:44 +03:00
if err != nil {
ctx . ServerError ( "GetLatestCommitStatus" , err )
return nil
}
if len ( commitStatuses ) > 0 {
ctx . Data [ "LatestCommitStatuses" ] = commitStatuses
ctx . Data [ "LatestCommitStatus" ] = models . CalcCommitStatus ( commitStatuses )
}
compareInfo , err := baseGitRepo . GetCompareInfo ( pull . BaseRepo . RepoPath ( ) ,
2022-01-18 10:45:43 +03:00
pull . MergeBase , pull . GetGitRefName ( ) , false , false )
2020-03-31 16:42:44 +03:00
if err != nil {
if strings . Contains ( err . Error ( ) , "fatal: Not a valid object name" ) {
ctx . Data [ "IsPullRequestBroken" ] = true
ctx . Data [ "BaseTarget" ] = pull . BaseBranch
ctx . Data [ "NumCommits" ] = 0
ctx . Data [ "NumFiles" ] = 0
return nil
}
ctx . ServerError ( "GetCompareInfo" , err )
return nil
}
2021-08-09 21:08:51 +03:00
ctx . Data [ "NumCommits" ] = len ( compareInfo . Commits )
2020-03-31 16:42:44 +03:00
ctx . Data [ "NumFiles" ] = compareInfo . NumFiles
return compareInfo
2020-03-05 21:51:21 +03:00
}
2019-06-30 10:57:59 +03:00
var headBranchExist bool
2020-01-07 20:06:14 +03:00
var headBranchSha string
2019-06-30 10:57:59 +03:00
// HeadRepo may be missing
2015-10-05 03:54:06 +03:00
if pull . HeadRepo != nil {
2022-01-20 02:26:57 +03:00
headGitRepo , err := git . OpenRepositoryCtx ( ctx , pull . HeadRepo . RepoPath ( ) )
2015-10-05 03:54:06 +03:00
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "OpenRepository" , err )
2015-10-05 03:54:06 +03:00
return nil
}
2019-11-13 10:01:19 +03:00
defer headGitRepo . Close ( )
2019-06-30 10:57:59 +03:00
2021-07-28 12:42:56 +03:00
if pull . Flow == models . PullRequestFlowGithub {
headBranchExist = headGitRepo . IsBranchExist ( pull . HeadBranch )
} else {
2021-11-30 23:06:32 +03:00
headBranchExist = git . IsReferenceExist ( ctx , baseGitRepo . Path , pull . GetGitRefName ( ) )
2021-07-28 12:42:56 +03:00
}
2019-06-30 10:57:59 +03:00
if headBranchExist {
2021-07-28 12:42:56 +03:00
if pull . Flow != models . PullRequestFlowGithub {
headBranchSha , err = baseGitRepo . GetRefCommitID ( pull . GetGitRefName ( ) )
} else {
headBranchSha , err = headGitRepo . GetBranchCommitID ( pull . HeadBranch )
}
2019-06-30 10:57:59 +03:00
if err != nil {
ctx . ServerError ( "GetBranchCommitID" , err )
return nil
}
2020-01-07 20:06:14 +03:00
}
}
2019-06-30 10:57:59 +03:00
2020-01-25 05:48:22 +03:00
if headBranchExist {
2022-01-20 02:26:57 +03:00
var err error
2022-03-22 10:03:22 +03:00
ctx . Data [ "UpdateAllowed" ] , ctx . Data [ "UpdateByRebaseAllowed" ] , err = pull_service . IsUserAllowedToUpdate ( pull , ctx . Doer )
2020-01-25 05:48:22 +03:00
if err != nil {
ctx . ServerError ( "IsUserAllowedToUpdate" , err )
return nil
}
2022-01-20 02:26:57 +03:00
ctx . Data [ "GetCommitMessages" ] = pull_service . GetSquashMergeCommitMessages ( ctx , pull )
2020-01-25 05:48:22 +03:00
}
2020-01-07 20:06:14 +03:00
sha , err := baseGitRepo . GetRefCommitID ( pull . GetGitRefName ( ) )
if err != nil {
2020-06-08 21:07:41 +03:00
if git . IsErrNotExist ( err ) {
ctx . Data [ "IsPullRequestBroken" ] = true
if pull . IsSameRepo ( ) {
ctx . Data [ "HeadTarget" ] = pull . HeadBranch
} else if pull . HeadRepo == nil {
ctx . Data [ "HeadTarget" ] = "<deleted>:" + pull . HeadBranch
} else {
ctx . Data [ "HeadTarget" ] = pull . HeadRepo . OwnerName + ":" + pull . HeadBranch
}
ctx . Data [ "BaseTarget" ] = pull . BaseBranch
ctx . Data [ "NumCommits" ] = 0
ctx . Data [ "NumFiles" ] = 0
return nil
}
2020-01-07 20:06:14 +03:00
ctx . ServerError ( fmt . Sprintf ( "GetRefCommitID(%s)" , pull . GetGitRefName ( ) ) , err )
return nil
}
2021-12-15 08:39:34 +03:00
commitStatuses , _ , err := models . GetLatestCommitStatus ( repo . ID , sha , db . ListOptions { } )
2020-01-07 20:06:14 +03:00
if err != nil {
ctx . ServerError ( "GetLatestCommitStatus" , err )
return nil
}
if len ( commitStatuses ) > 0 {
ctx . Data [ "LatestCommitStatuses" ] = commitStatuses
ctx . Data [ "LatestCommitStatus" ] = models . CalcCommitStatus ( commitStatuses )
}
2019-09-18 08:39:45 +03:00
2020-01-07 20:06:14 +03:00
if pull . ProtectedBranch != nil && pull . ProtectedBranch . EnableStatusCheck {
ctx . Data [ "is_context_required" ] = func ( context string ) bool {
for _ , c := range pull . ProtectedBranch . StatusCheckContexts {
if c == context {
return true
2019-09-18 08:39:45 +03:00
}
}
2020-01-07 20:06:14 +03:00
return false
2019-06-30 10:57:59 +03:00
}
2020-04-15 23:35:18 +03:00
ctx . Data [ "RequiredStatusCheckState" ] = pull_service . MergeRequiredContextsCommitStatus ( commitStatuses , pull . ProtectedBranch . StatusCheckContexts )
2015-09-02 11:08:05 +03:00
}
2020-01-07 20:06:14 +03:00
ctx . Data [ "HeadBranchMovedOn" ] = headBranchSha != sha
ctx . Data [ "HeadBranchCommitID" ] = headBranchSha
ctx . Data [ "PullHeadCommitID" ] = sha
if pull . HeadRepo == nil || ! headBranchExist || headBranchSha != sha {
2018-08-01 06:00:35 +03:00
ctx . Data [ "IsPullRequestBroken" ] = true
2020-03-03 01:31:55 +03:00
if pull . IsSameRepo ( ) {
ctx . Data [ "HeadTarget" ] = pull . HeadBranch
2020-06-08 21:07:41 +03:00
} else if pull . HeadRepo == nil {
ctx . Data [ "HeadTarget" ] = "<deleted>:" + pull . HeadBranch
2020-03-03 01:31:55 +03:00
} else {
2020-06-08 21:07:41 +03:00
ctx . Data [ "HeadTarget" ] = pull . HeadRepo . OwnerName + ":" + pull . HeadBranch
2020-03-03 01:31:55 +03:00
}
2015-09-02 16:26:56 +03:00
}
2020-01-07 20:06:14 +03:00
compareInfo , err := baseGitRepo . GetCompareInfo ( pull . BaseRepo . RepoPath ( ) ,
2022-01-18 10:45:43 +03:00
git . BranchPrefix + pull . BaseBranch , pull . GetGitRefName ( ) , false , false )
2015-09-02 11:08:05 +03:00
if err != nil {
2016-07-23 13:35:16 +03:00
if strings . Contains ( err . Error ( ) , "fatal: Not a valid object name" ) {
2018-08-01 06:00:35 +03:00
ctx . Data [ "IsPullRequestBroken" ] = true
2020-03-03 01:31:55 +03:00
ctx . Data [ "BaseTarget" ] = pull . BaseBranch
2016-07-23 13:35:16 +03:00
ctx . Data [ "NumCommits" ] = 0
ctx . Data [ "NumFiles" ] = 0
return nil
}
2019-06-07 23:29:29 +03:00
ctx . ServerError ( "GetCompareInfo" , err )
2015-09-02 11:08:05 +03:00
return nil
}
2018-08-13 22:04:39 +03:00
2021-07-29 05:32:48 +03:00
if compareInfo . HeadCommitID == compareInfo . MergeBase {
ctx . Data [ "IsNothingToCompare" ] = true
}
2018-08-13 22:04:39 +03:00
if pull . IsWorkInProgress ( ) {
ctx . Data [ "IsPullWorkInProgress" ] = true
ctx . Data [ "WorkInProgressPrefix" ] = pull . GetWorkInProgressPrefix ( )
}
2019-02-05 14:54:49 +03:00
if pull . IsFilesConflicted ( ) {
ctx . Data [ "IsPullFilesConflicted" ] = true
ctx . Data [ "ConflictedFiles" ] = pull . ConflictedFiles
}
2021-08-09 21:08:51 +03:00
ctx . Data [ "NumCommits" ] = len ( compareInfo . Commits )
2019-06-12 02:32:08 +03:00
ctx . Data [ "NumFiles" ] = compareInfo . NumFiles
return compareInfo
2015-09-02 11:08:05 +03:00
}
2016-11-24 10:04:31 +03:00
// ViewPullCommits show commits for a pull request
2016-03-11 19:56:52 +03:00
func ViewPullCommits ( ctx * context . Context ) {
2016-08-14 13:32:24 +03:00
ctx . Data [ "PageIsPullList" ] = true
2015-09-02 11:08:05 +03:00
ctx . Data [ "PageIsPullCommits" ] = true
2016-08-16 20:19:09 +03:00
issue := checkPullInfo ( ctx )
2015-09-02 11:08:05 +03:00
if ctx . Written ( ) {
return
}
2016-08-16 20:19:09 +03:00
pull := issue . PullRequest
2015-09-02 11:08:05 +03:00
2019-12-18 21:37:44 +03:00
var prInfo * git . CompareInfo
2015-09-02 16:26:56 +03:00
if pull . HasMerged {
2019-12-18 21:37:44 +03:00
prInfo = PrepareMergedViewPullInfo ( ctx , issue )
2015-09-02 16:26:56 +03:00
} else {
2019-12-18 21:37:44 +03:00
prInfo = PrepareViewPullInfo ( ctx , issue )
2015-09-02 11:08:05 +03:00
}
2019-12-18 21:37:44 +03:00
if ctx . Written ( ) {
return
} else if prInfo == nil {
ctx . NotFound ( "ViewPullCommits" , nil )
return
}
ctx . Data [ "Username" ] = ctx . Repo . Owner . Name
ctx . Data [ "Reponame" ] = ctx . Repo . Repository . Name
2021-08-09 21:08:51 +03:00
commits := models . ConvertFromGitCommit ( prInfo . Commits , ctx . Repo . Repository )
2015-09-02 16:26:56 +03:00
ctx . Data [ "Commits" ] = commits
2021-08-09 21:08:51 +03:00
ctx . Data [ "CommitCount" ] = len ( commits )
2015-09-02 16:26:56 +03:00
2019-12-16 09:20:25 +03:00
getBranchData ( ctx , issue )
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , tplPullCommits )
2015-09-02 11:08:05 +03:00
}
2016-11-24 10:04:31 +03:00
// ViewPullFiles render pull request changed files list page
2016-03-11 19:56:52 +03:00
func ViewPullFiles ( ctx * context . Context ) {
2016-08-14 13:32:24 +03:00
ctx . Data [ "PageIsPullList" ] = true
2015-09-02 11:08:05 +03:00
ctx . Data [ "PageIsPullFiles" ] = true
2016-08-16 20:19:09 +03:00
issue := checkPullInfo ( ctx )
2015-09-02 11:08:05 +03:00
if ctx . Written ( ) {
return
}
2016-08-16 20:19:09 +03:00
pull := issue . PullRequest
2015-09-02 11:08:05 +03:00
2015-09-02 16:26:56 +03:00
var (
startCommitID string
endCommitID string
2021-08-31 07:16:23 +03:00
gitRepo = ctx . Repo . GitRepo
2015-09-02 16:26:56 +03:00
)
2019-12-18 21:37:44 +03:00
var prInfo * git . CompareInfo
2015-09-02 16:26:56 +03:00
if pull . HasMerged {
2019-12-18 21:37:44 +03:00
prInfo = PrepareMergedViewPullInfo ( ctx , issue )
2015-09-02 16:26:56 +03:00
} else {
2019-12-18 21:37:44 +03:00
prInfo = PrepareViewPullInfo ( ctx , issue )
}
2015-09-02 11:08:05 +03:00
2019-12-18 21:37:44 +03:00
if ctx . Written ( ) {
return
} else if prInfo == nil {
ctx . NotFound ( "ViewPullFiles" , nil )
return
}
2015-09-02 11:08:05 +03:00
2019-12-18 21:37:44 +03:00
headCommitID , err := gitRepo . GetRefCommitID ( pull . GetGitRefName ( ) )
if err != nil {
ctx . ServerError ( "GetRefCommitID" , err )
return
}
2015-09-02 16:26:56 +03:00
2019-12-18 21:37:44 +03:00
startCommitID = prInfo . MergeBase
endCommitID = headCommitID
2017-06-21 01:25:38 +03:00
2019-12-18 21:37:44 +03:00
ctx . Data [ "Username" ] = ctx . Repo . Owner . Name
ctx . Data [ "Reponame" ] = ctx . Repo . Repository . Name
2019-11-15 05:52:59 +03:00
ctx . Data [ "AfterCommitID" ] = endCommitID
2015-09-02 11:08:05 +03:00
2021-11-21 19:51:08 +03:00
fileOnly := ctx . FormBool ( "file-only" )
maxLines , maxFiles := setting . Git . MaxGitDiffLines , setting . Git . MaxGitDiffFiles
files := ctx . FormStrings ( "files" )
if fileOnly && ( len ( files ) == 2 || len ( files ) == 1 ) {
maxLines , maxFiles = - 1 , - 1
}
diff , err := gitdiff . GetDiff ( gitRepo ,
& gitdiff . DiffOptions {
BeforeCommitID : startCommitID ,
AfterCommitID : endCommitID ,
SkipTo : ctx . FormString ( "skip-to" ) ,
MaxLines : maxLines ,
MaxLineCharacters : setting . Git . MaxGitDiffLineCharacters ,
MaxFiles : maxFiles ,
WhitespaceBehavior : gitdiff . GetWhitespaceFlag ( ctx . Data [ "WhitespaceBehavior" ] . ( string ) ) ,
} , ctx . FormStrings ( "files" ) ... )
2015-09-02 11:08:05 +03:00
if err != nil {
2018-08-14 20:49:33 +03:00
ctx . ServerError ( "GetDiffRangeWithWhitespaceBehavior" , err )
2015-09-02 11:08:05 +03:00
return
}
2018-08-06 07:43:22 +03:00
2022-03-22 10:03:22 +03:00
if err = diff . LoadComments ( ctx , issue , ctx . Doer ) ; err != nil {
2018-08-06 07:43:22 +03:00
ctx . ServerError ( "LoadComments" , err )
return
}
2020-10-13 21:50:57 +03:00
if err = pull . LoadProtectedBranch ( ) ; err != nil {
ctx . ServerError ( "LoadProtectedBranch" , err )
return
}
if pull . ProtectedBranch != nil {
glob := pull . ProtectedBranch . GetProtectedFilePatterns ( )
if len ( glob ) != 0 {
for _ , file := range diff . Files {
file . IsProtected = pull . ProtectedBranch . IsProtectedFile ( glob , file . Name )
}
}
}
2015-09-02 11:08:05 +03:00
ctx . Data [ "Diff" ] = diff
2020-05-26 08:58:07 +03:00
ctx . Data [ "DiffNotAvailable" ] = diff . NumFiles == 0
2015-09-02 11:08:05 +03:00
2019-09-16 12:03:22 +03:00
baseCommit , err := ctx . Repo . GitRepo . GetCommit ( startCommitID )
if err != nil {
ctx . ServerError ( "GetCommit" , err )
return
}
2015-09-02 16:26:56 +03:00
commit , err := gitRepo . GetCommit ( endCommitID )
2015-09-02 11:08:05 +03:00
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetCommit" , err )
2015-09-02 11:08:05 +03:00
return
}
2022-03-22 10:03:22 +03:00
if ctx . IsSigned && ctx . Doer != nil {
if ctx . Data [ "CanMarkConversation" ] , err = models . CanMarkConversation ( issue , ctx . Doer ) ; err != nil {
2020-04-18 16:50:25 +03:00
ctx . ServerError ( "CanMarkConversation" , err )
return
}
}
2021-11-16 21:18:25 +03:00
setCompareContext ( ctx , baseCommit , commit , ctx . Repo . Owner . Name , ctx . Repo . Repository . Name )
2019-09-16 12:03:22 +03:00
2016-01-31 19:19:02 +03:00
ctx . Data [ "RequireHighlightJS" ] = true
2018-08-06 07:43:22 +03:00
ctx . Data [ "RequireTribute" ] = true
2021-12-10 04:27:50 +03:00
if ctx . Data [ "Assignees" ] , err = models . GetRepoAssignees ( ctx . Repo . Repository ) ; err != nil {
2018-08-06 07:43:22 +03:00
ctx . ServerError ( "GetAssignees" , err )
return
}
2020-12-21 18:39:28 +03:00
handleTeamMentions ( ctx )
if ctx . Written ( ) {
return
}
2022-03-22 10:03:22 +03:00
ctx . Data [ "CurrentReview" ] , err = models . GetCurrentReview ( ctx . Doer , issue )
2018-08-06 07:43:22 +03:00
if err != nil && ! models . IsErrReviewNotExist ( err ) {
ctx . ServerError ( "GetCurrentReview" , err )
return
}
2019-12-16 09:20:25 +03:00
getBranchData ( ctx , issue )
2022-03-22 10:03:22 +03:00
ctx . Data [ "IsIssuePoster" ] = ctx . IsSigned && issue . IsPoster ( ctx . Doer . ID )
2020-04-04 08:39:48 +03:00
ctx . Data [ "HasIssuesOrPullsWritePermission" ] = ctx . Repo . CanWriteIssuesOrPulls ( issue . IsPull )
2021-06-15 04:12:33 +03:00
ctx . Data [ "IsAttachmentEnabled" ] = setting . Attachment . Enabled
upload . AddUploadContext ( ctx , "comment" )
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , tplPullFiles )
2015-09-02 11:08:05 +03:00
}
2015-08-31 10:24:28 +03:00
2020-08-04 23:55:22 +03:00
// UpdatePullRequest merge PR's baseBranch into headBranch
2020-01-17 09:03:40 +03:00
func UpdatePullRequest ( ctx * context . Context ) {
issue := checkPullInfo ( ctx )
if ctx . Written ( ) {
return
}
if issue . IsClosed {
ctx . NotFound ( "MergePullRequest" , nil )
return
}
if issue . PullRequest . HasMerged {
ctx . NotFound ( "MergePullRequest" , nil )
return
}
2021-08-31 17:03:45 +03:00
rebase := ctx . FormString ( "style" ) == "rebase"
2020-01-17 09:03:40 +03:00
if err := issue . PullRequest . LoadBaseRepo ( ) ; err != nil {
2021-01-14 23:27:22 +03:00
ctx . ServerError ( "LoadBaseRepo" , err )
2020-01-17 09:03:40 +03:00
return
}
if err := issue . PullRequest . LoadHeadRepo ( ) ; err != nil {
2021-01-14 23:27:22 +03:00
ctx . ServerError ( "LoadHeadRepo" , err )
2020-01-17 09:03:40 +03:00
return
}
2022-03-22 10:03:22 +03:00
allowedUpdateByMerge , allowedUpdateByRebase , err := pull_service . IsUserAllowedToUpdate ( issue . PullRequest , ctx . Doer )
2020-01-17 09:03:40 +03:00
if err != nil {
ctx . ServerError ( "IsUserAllowedToMerge" , err )
return
}
// ToDo: add check if maintainers are allowed to change branch ... (need migration & co)
2021-08-31 17:03:45 +03:00
if ( ! allowedUpdateByMerge && ! rebase ) || ( rebase && ! allowedUpdateByRebase ) {
2020-01-17 09:03:40 +03:00
ctx . Flash . Error ( ctx . Tr ( "repo.pulls.update_not_allowed" ) )
2021-11-16 21:18:25 +03:00
ctx . Redirect ( issue . Link ( ) )
2020-01-17 09:03:40 +03:00
return
}
// default merge commit message
message := fmt . Sprintf ( "Merge branch '%s' into %s" , issue . PullRequest . BaseBranch , issue . PullRequest . HeadBranch )
2022-03-22 10:03:22 +03:00
if err = pull_service . Update ( ctx , issue . PullRequest , ctx . Doer , message , rebase ) ; err != nil {
2020-01-17 09:03:40 +03:00
if models . IsErrMergeConflicts ( err ) {
conflictError := err . ( models . ErrMergeConflicts )
2021-12-15 09:59:57 +03:00
flashError , err := ctx . RenderToString ( tplAlertDetails , map [ string ] interface { } {
2020-10-21 02:50:10 +03:00
"Message" : ctx . Tr ( "repo.pulls.merge_conflict" ) ,
"Summary" : ctx . Tr ( "repo.pulls.merge_conflict_summary" ) ,
"Details" : utils . SanitizeFlashErrorString ( conflictError . StdErr ) + "<br>" + utils . SanitizeFlashErrorString ( conflictError . StdOut ) ,
} )
if err != nil {
ctx . ServerError ( "UpdatePullRequest.HTMLString" , err )
return
}
ctx . Flash . Error ( flashError )
2021-11-16 21:18:25 +03:00
ctx . Redirect ( issue . Link ( ) )
2020-01-17 09:03:40 +03:00
return
2021-09-05 12:30:40 +03:00
} else if models . IsErrRebaseConflicts ( err ) {
conflictError := err . ( models . ErrRebaseConflicts )
2021-12-15 09:59:57 +03:00
flashError , err := ctx . RenderToString ( tplAlertDetails , map [ string ] interface { } {
2021-09-05 12:30:40 +03:00
"Message" : ctx . Tr ( "repo.pulls.rebase_conflict" , utils . SanitizeFlashErrorString ( conflictError . CommitSHA ) ) ,
"Summary" : ctx . Tr ( "repo.pulls.rebase_conflict_summary" ) ,
"Details" : utils . SanitizeFlashErrorString ( conflictError . StdErr ) + "<br>" + utils . SanitizeFlashErrorString ( conflictError . StdOut ) ,
} )
if err != nil {
ctx . ServerError ( "UpdatePullRequest.HTMLString" , err )
return
}
ctx . Flash . Error ( flashError )
2021-11-16 21:18:25 +03:00
ctx . Redirect ( issue . Link ( ) )
2021-09-05 12:30:40 +03:00
return
2020-01-17 09:03:40 +03:00
}
ctx . Flash . Error ( err . Error ( ) )
2021-11-16 21:18:25 +03:00
ctx . Redirect ( issue . Link ( ) )
2020-08-03 23:50:29 +03:00
return
2020-01-17 09:03:40 +03:00
}
time . Sleep ( 1 * time . Second )
ctx . Flash . Success ( ctx . Tr ( "repo.pulls.update_branch_success" ) )
2021-11-16 21:18:25 +03:00
ctx . Redirect ( issue . Link ( ) )
2020-01-17 09:03:40 +03:00
}
2016-11-24 10:04:31 +03:00
// MergePullRequest response for merging pull request
2021-01-26 18:36:53 +03:00
func MergePullRequest ( ctx * context . Context ) {
2021-04-06 22:44:05 +03:00
form := web . GetForm ( ctx ) . ( * forms . MergePullRequestForm )
2015-10-19 02:30:39 +03:00
issue := checkPullInfo ( ctx )
2015-09-02 16:26:56 +03:00
if ctx . Written ( ) {
return
}
2015-10-19 02:30:39 +03:00
if issue . IsClosed {
2020-01-27 13:26:53 +03:00
if issue . IsPull {
ctx . Flash . Error ( ctx . Tr ( "repo.pulls.is_closed" ) )
2021-11-16 21:18:25 +03:00
ctx . Redirect ( issue . Link ( ) )
2020-01-27 13:26:53 +03:00
return
}
ctx . Flash . Error ( ctx . Tr ( "repo.issues.closed_title" ) )
2021-11-16 21:18:25 +03:00
ctx . Redirect ( issue . Link ( ) )
2015-09-02 16:26:56 +03:00
return
}
2018-12-13 18:55:43 +03:00
pr := issue . PullRequest
2015-09-02 16:26:56 +03:00
2022-03-22 10:03:22 +03:00
allowedMerge , err := pull_service . IsUserAllowedToMerge ( pr , ctx . Repo . Permission , ctx . Doer )
2020-01-11 10:29:34 +03:00
if err != nil {
ctx . ServerError ( "IsUserAllowedToMerge" , err )
return
}
if ! allowedMerge {
2020-01-27 13:26:53 +03:00
ctx . Flash . Error ( ctx . Tr ( "repo.pulls.update_not_allowed" ) )
2021-11-16 21:18:25 +03:00
ctx . Redirect ( issue . Link ( ) )
2020-01-11 10:29:34 +03:00
return
}
2021-03-04 06:41:23 +03:00
if pr . HasMerged {
ctx . Flash . Error ( ctx . Tr ( "repo.pulls.has_merged" ) )
2021-11-16 21:18:25 +03:00
ctx . Redirect ( issue . Link ( ) )
2020-01-27 13:26:53 +03:00
return
}
2021-03-04 06:41:23 +03:00
// handle manually-merged mark
2021-12-10 04:27:50 +03:00
if repo_model . MergeStyle ( form . Do ) == repo_model . MergeStyleManuallyMerged {
2022-03-22 10:03:22 +03:00
if err = pull_service . MergedManually ( pr , ctx . Doer , ctx . Repo . GitRepo , form . MergeCommitID ) ; err != nil {
2021-03-04 06:41:23 +03:00
if models . IsErrInvalidMergeStyle ( err ) {
ctx . Flash . Error ( ctx . Tr ( "repo.pulls.invalid_merge_option" ) )
2021-11-16 21:18:25 +03:00
ctx . Redirect ( issue . Link ( ) )
2021-03-04 06:41:23 +03:00
return
} else if strings . Contains ( err . Error ( ) , "Wrong commit ID" ) {
ctx . Flash . Error ( ctx . Tr ( "repo.pulls.wrong_commit_id" ) )
2021-11-16 21:18:25 +03:00
ctx . Redirect ( issue . Link ( ) )
2021-03-04 06:41:23 +03:00
return
}
ctx . ServerError ( "MergedManually" , err )
return
}
2021-11-16 21:18:25 +03:00
ctx . Redirect ( issue . Link ( ) )
2021-03-04 06:41:23 +03:00
return
}
if ! pr . CanAutoMerge ( ) {
ctx . Flash . Error ( ctx . Tr ( "repo.pulls.no_merge_not_ready" ) )
2021-11-16 21:18:25 +03:00
ctx . Redirect ( issue . Link ( ) )
2015-09-02 16:26:56 +03:00
return
}
2018-08-13 22:04:39 +03:00
if pr . IsWorkInProgress ( ) {
ctx . Flash . Error ( ctx . Tr ( "repo.pulls.no_merge_wip" ) )
2021-11-16 21:18:25 +03:00
ctx . Redirect ( issue . Link ( ) )
2018-08-13 22:04:39 +03:00
return
}
2022-01-20 02:26:57 +03:00
if err := pull_service . CheckPRReadyToMerge ( ctx , pr , false ) ; err != nil {
2020-01-11 10:29:34 +03:00
if ! models . IsErrNotAllowedToMerge ( err ) {
ctx . ServerError ( "Merge PR status" , err )
return
}
2022-03-22 10:03:22 +03:00
if isRepoAdmin , err := models . IsUserRepoAdmin ( pr . BaseRepo , ctx . Doer ) ; err != nil {
2020-01-11 10:29:34 +03:00
ctx . ServerError ( "IsUserRepoAdmin" , err )
return
} else if ! isRepoAdmin {
ctx . Flash . Error ( ctx . Tr ( "repo.pulls.no_merge_not_ready" ) )
2021-11-16 21:18:25 +03:00
ctx . Redirect ( issue . Link ( ) )
2020-01-11 10:29:34 +03:00
return
}
2019-09-18 08:39:45 +03:00
}
2018-01-05 21:56:50 +03:00
if ctx . HasError ( ) {
ctx . Flash . Error ( ctx . Data [ "ErrorMsg" ] . ( string ) )
2021-11-16 21:18:25 +03:00
ctx . Redirect ( issue . Link ( ) )
2018-01-05 21:56:50 +03:00
return
}
message := strings . TrimSpace ( form . MergeTitleField )
if len ( message ) == 0 {
2021-12-10 04:27:50 +03:00
if repo_model . MergeStyle ( form . Do ) == repo_model . MergeStyleMerge {
2018-01-05 21:56:50 +03:00
message = pr . GetDefaultMergeMessage ( )
}
2021-12-10 04:27:50 +03:00
if repo_model . MergeStyle ( form . Do ) == repo_model . MergeStyleRebaseMerge {
2018-12-27 13:27:08 +03:00
message = pr . GetDefaultMergeMessage ( )
}
2021-12-10 04:27:50 +03:00
if repo_model . MergeStyle ( form . Do ) == repo_model . MergeStyleSquash {
2018-01-05 21:56:50 +03:00
message = pr . GetDefaultSquashMessage ( )
}
}
form . MergeMessageField = strings . TrimSpace ( form . MergeMessageField )
if len ( form . MergeMessageField ) > 0 {
message += "\n\n" + form . MergeMessageField
}
2015-10-19 02:30:39 +03:00
pr . Issue = issue
pr . Issue . Repo = ctx . Repo . Repository
2018-07-18 00:23:58 +03:00
noDeps , err := models . IssueNoDependenciesLeft ( issue )
if err != nil {
return
}
if ! noDeps {
ctx . Flash . Error ( ctx . Tr ( "repo.issues.dependency.pr_close_blocked" ) )
2021-11-16 21:18:25 +03:00
ctx . Redirect ( issue . Link ( ) )
2018-07-18 00:23:58 +03:00
return
}
2022-03-22 10:03:22 +03:00
if err = pull_service . Merge ( ctx , pr , ctx . Doer , ctx . Repo . GitRepo , repo_model . MergeStyle ( form . Do ) , form . HeadCommitID , message ) ; err != nil {
2018-01-05 21:56:50 +03:00
if models . IsErrInvalidMergeStyle ( err ) {
ctx . Flash . Error ( ctx . Tr ( "repo.pulls.invalid_merge_option" ) )
2021-11-16 21:18:25 +03:00
ctx . Redirect ( issue . Link ( ) )
2018-01-05 21:56:50 +03:00
return
2019-11-10 11:42:51 +03:00
} else if models . IsErrMergeConflicts ( err ) {
conflictError := err . ( models . ErrMergeConflicts )
2021-12-15 09:59:57 +03:00
flashError , err := ctx . RenderToString ( tplAlertDetails , map [ string ] interface { } {
2020-10-21 02:50:10 +03:00
"Message" : ctx . Tr ( "repo.editor.merge_conflict" ) ,
"Summary" : ctx . Tr ( "repo.editor.merge_conflict_summary" ) ,
"Details" : utils . SanitizeFlashErrorString ( conflictError . StdErr ) + "<br>" + utils . SanitizeFlashErrorString ( conflictError . StdOut ) ,
} )
if err != nil {
ctx . ServerError ( "MergePullRequest.HTMLString" , err )
return
}
ctx . Flash . Error ( flashError )
2021-11-16 21:18:25 +03:00
ctx . Redirect ( issue . Link ( ) )
2019-11-10 11:42:51 +03:00
return
} else if models . IsErrRebaseConflicts ( err ) {
conflictError := err . ( models . ErrRebaseConflicts )
2021-12-15 09:59:57 +03:00
flashError , err := ctx . RenderToString ( tplAlertDetails , map [ string ] interface { } {
2020-10-21 22:59:12 +03:00
"Message" : ctx . Tr ( "repo.pulls.rebase_conflict" , utils . SanitizeFlashErrorString ( conflictError . CommitSHA ) ) ,
"Summary" : ctx . Tr ( "repo.pulls.rebase_conflict_summary" ) ,
2020-10-21 02:50:10 +03:00
"Details" : utils . SanitizeFlashErrorString ( conflictError . StdErr ) + "<br>" + utils . SanitizeFlashErrorString ( conflictError . StdOut ) ,
} )
if err != nil {
ctx . ServerError ( "MergePullRequest.HTMLString" , err )
return
}
ctx . Flash . Error ( flashError )
2021-11-16 21:18:25 +03:00
ctx . Redirect ( issue . Link ( ) )
2019-11-10 11:42:51 +03:00
return
} else if models . IsErrMergeUnrelatedHistories ( err ) {
log . Debug ( "MergeUnrelatedHistories error: %v" , err )
ctx . Flash . Error ( ctx . Tr ( "repo.pulls.unrelated_histories" ) )
2021-11-16 21:18:25 +03:00
ctx . Redirect ( issue . Link ( ) )
2019-11-10 11:42:51 +03:00
return
2020-03-28 07:13:18 +03:00
} else if git . IsErrPushOutOfDate ( err ) {
2019-11-10 11:42:51 +03:00
log . Debug ( "MergePushOutOfDate error: %v" , err )
ctx . Flash . Error ( ctx . Tr ( "repo.pulls.merge_out_of_date" ) )
2021-11-16 21:18:25 +03:00
ctx . Redirect ( issue . Link ( ) )
2019-11-10 11:42:51 +03:00
return
2021-12-20 03:32:54 +03:00
} else if models . IsErrSHADoesNotMatch ( err ) {
log . Debug ( "MergeHeadOutOfDate error: %v" , err )
ctx . Flash . Error ( ctx . Tr ( "repo.pulls.head_out_of_date" ) )
ctx . Redirect ( issue . Link ( ) )
return
2020-03-28 07:13:18 +03:00
} else if git . IsErrPushRejected ( err ) {
2020-02-22 16:08:48 +03:00
log . Debug ( "MergePushRejected error: %v" , err )
2020-03-28 07:13:18 +03:00
pushrejErr := err . ( * git . ErrPushRejected )
2020-02-22 16:08:48 +03:00
message := pushrejErr . Message
if len ( message ) == 0 {
ctx . Flash . Error ( ctx . Tr ( "repo.pulls.push_rejected_no_message" ) )
} else {
2021-12-15 09:59:57 +03:00
flashError , err := ctx . RenderToString ( tplAlertDetails , map [ string ] interface { } {
2020-10-21 02:50:10 +03:00
"Message" : ctx . Tr ( "repo.pulls.push_rejected" ) ,
"Summary" : ctx . Tr ( "repo.pulls.push_rejected_summary" ) ,
"Details" : utils . SanitizeFlashErrorString ( pushrejErr . Message ) ,
} )
if err != nil {
ctx . ServerError ( "MergePullRequest.HTMLString" , err )
return
}
ctx . Flash . Error ( flashError )
2020-02-22 16:08:48 +03:00
}
2021-11-16 21:18:25 +03:00
ctx . Redirect ( issue . Link ( ) )
2020-02-22 16:08:48 +03:00
return
2018-01-05 21:56:50 +03:00
}
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "Merge" , err )
2015-09-02 16:26:56 +03:00
return
}
2022-03-22 10:03:22 +03:00
if err := stopTimerIfAvailable ( ctx . Doer , issue ) ; err != nil {
2019-02-05 14:38:11 +03:00
ctx . ServerError ( "CreateOrStopIssueStopwatch" , err )
return
}
2015-09-02 16:26:56 +03:00
log . Trace ( "Pull request merged: %d" , pr . ID )
2021-07-13 02:26:25 +03:00
if form . DeleteBranchAfterMerge {
2022-01-03 22:45:58 +03:00
// Don't cleanup when other pr use this branch as head branch
exist , err := models . HasUnmergedPullRequestsByHeadInfo ( pr . HeadRepoID , pr . HeadBranch )
if err != nil {
ctx . ServerError ( "HasUnmergedPullRequestsByHeadInfo" , err )
return
}
if exist {
ctx . Redirect ( issue . Link ( ) )
return
}
2021-07-13 02:26:25 +03:00
var headRepo * git . Repository
if ctx . Repo != nil && ctx . Repo . Repository != nil && pr . HeadRepoID == ctx . Repo . Repository . ID && ctx . Repo . GitRepo != nil {
headRepo = ctx . Repo . GitRepo
} else {
2022-01-20 02:26:57 +03:00
headRepo , err = git . OpenRepositoryCtx ( ctx , pr . HeadRepo . RepoPath ( ) )
2021-07-13 02:26:25 +03:00
if err != nil {
ctx . ServerError ( fmt . Sprintf ( "OpenRepository[%s]" , pr . HeadRepo . RepoPath ( ) ) , err )
return
}
defer headRepo . Close ( )
}
deleteBranch ( ctx , pr , headRepo )
}
2021-11-16 21:18:25 +03:00
ctx . Redirect ( issue . Link ( ) )
2015-09-02 16:26:56 +03:00
}
2021-11-24 12:49:20 +03:00
func stopTimerIfAvailable ( user * user_model . User , issue * models . Issue ) error {
2019-02-05 14:38:11 +03:00
if models . StopwatchExists ( user . ID , issue . ID ) {
if err := models . CreateOrStopIssueStopwatch ( user , issue ) ; err != nil {
return err
}
}
return nil
}
2016-11-24 10:04:31 +03:00
// CompareAndPullRequestPost response for creating pull request
2021-01-26 18:36:53 +03:00
func CompareAndPullRequestPost ( ctx * context . Context ) {
2021-04-06 22:44:05 +03:00
form := web . GetForm ( ctx ) . ( * forms . CreateIssueForm )
2015-09-02 02:07:02 +03:00
ctx . Data [ "Title" ] = ctx . Tr ( "repo.pulls.compare_changes" )
ctx . Data [ "PageIsComparePull" ] = true
ctx . Data [ "IsDiffCompare" ] = true
2021-07-25 05:59:27 +03:00
ctx . Data [ "IsRepoToolbarCommits" ] = true
ctx . Data [ "RequireTribute" ] = true
2016-08-16 00:04:44 +03:00
ctx . Data [ "RequireHighlightJS" ] = true
2018-08-13 22:04:39 +03:00
ctx . Data [ "PullRequestWorkInProgressPrefixes" ] = setting . Repository . PullRequest . WorkInProgressPrefixes
2020-10-05 08:49:33 +03:00
ctx . Data [ "IsAttachmentEnabled" ] = setting . Attachment . Enabled
upload . AddUploadContext ( ctx , "comment" )
2021-11-09 22:57:58 +03:00
ctx . Data [ "HasIssuesOrPullsWritePermission" ] = ctx . Repo . CanWrite ( unit . TypePullRequests )
2015-09-02 02:07:02 +03:00
var (
repo = ctx . Repo . Repository
attachments [ ] string
)
2021-09-27 15:19:34 +03:00
ci := ParseCompareInfo ( ctx )
2021-08-31 10:43:31 +03:00
defer func ( ) {
2021-09-30 22:31:02 +03:00
if ci != nil && ci . HeadGitRepo != nil {
2021-09-27 15:19:34 +03:00
ci . HeadGitRepo . Close ( )
2021-08-31 10:43:31 +03:00
}
} ( )
2015-09-02 02:07:02 +03:00
if ctx . Written ( ) {
return
}
2021-01-26 18:36:53 +03:00
labelIDs , assigneeIDs , milestoneID , _ := ValidateRepoMetas ( ctx , * form , true )
2015-09-02 02:07:02 +03:00
if ctx . Written ( ) {
return
}
2020-08-18 07:23:45 +03:00
if setting . Attachment . Enabled {
2016-08-11 15:48:08 +03:00
attachments = form . Files
2015-09-02 02:07:02 +03:00
}
if ctx . HasError ( ) {
2021-01-30 11:55:53 +03:00
middleware . AssignForm ( form , ctx . Data )
2016-08-16 00:04:44 +03:00
// This stage is already stop creating new pull request, so it does not matter if it has
// something to compare or not.
2021-09-27 15:19:34 +03:00
PrepareCompareDiff ( ctx , ci ,
2021-02-13 07:35:43 +03:00
gitdiff . GetWhitespaceFlag ( ctx . Data [ "WhitespaceBehavior" ] . ( string ) ) )
2016-08-16 00:04:44 +03:00
if ctx . Written ( ) {
return
}
2021-07-25 05:59:27 +03:00
if len ( form . Title ) > 255 {
var trailer string
form . Title , trailer = util . SplitStringAtByteN ( form . Title , 255 )
form . Content = trailer + "\n\n" + form . Content
}
middleware . AssignForm ( form , ctx . Data )
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , tplCompareDiff )
2015-09-02 02:07:02 +03:00
return
}
2019-01-21 14:45:32 +03:00
if util . IsEmptyString ( form . Title ) {
2021-09-27 15:19:34 +03:00
PrepareCompareDiff ( ctx , ci ,
2021-02-13 07:35:43 +03:00
gitdiff . GetWhitespaceFlag ( ctx . Data [ "WhitespaceBehavior" ] . ( string ) ) )
2019-01-21 14:45:32 +03:00
if ctx . Written ( ) {
return
}
2019-06-07 23:29:29 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.issues.new.title_empty" ) , tplCompareDiff , form )
2019-01-21 14:45:32 +03:00
return
}
2016-02-24 15:56:54 +03:00
pullIssue := & models . Issue {
2015-09-02 02:07:02 +03:00
RepoID : repo . ID ,
2021-11-16 21:18:25 +03:00
Repo : repo ,
2016-08-14 13:32:24 +03:00
Title : form . Title ,
2022-03-22 10:03:22 +03:00
PosterID : ctx . Doer . ID ,
Poster : ctx . Doer ,
2015-09-02 02:07:02 +03:00
MilestoneID : milestoneID ,
IsPull : true ,
Content : form . Content ,
}
2016-02-24 15:56:54 +03:00
pullRequest := & models . PullRequest {
2021-09-27 15:19:34 +03:00
HeadRepoID : ci . HeadRepo . ID ,
2019-10-18 14:13:31 +03:00
BaseRepoID : repo . ID ,
2021-09-27 15:19:34 +03:00
HeadBranch : ci . HeadBranch ,
BaseBranch : ci . BaseBranch ,
HeadRepo : ci . HeadRepo ,
2019-10-18 14:13:31 +03:00
BaseRepo : repo ,
2021-09-27 15:19:34 +03:00
MergeBase : ci . CompareInfo . MergeBase ,
2019-10-18 14:13:31 +03:00
Type : models . PullRequestGitea ,
2016-02-24 15:56:54 +03:00
}
2016-08-16 00:04:44 +03:00
// FIXME: check error in the case two people send pull request at almost same time, give nice error prompt
// instead of 500.
2018-05-09 19:29:04 +03:00
2022-01-20 02:26:57 +03:00
if err := pull_service . NewPullRequest ( ctx , repo , pullIssue , labelIDs , attachments , pullRequest , assigneeIDs ) ; err != nil {
2018-05-09 19:29:04 +03:00
if models . IsErrUserDoesNotHaveAccessToRepo ( err ) {
2021-04-05 18:30:52 +03:00
ctx . Error ( http . StatusBadRequest , "UserDoesNotHaveAccessToRepo" , err . Error ( ) )
2018-05-09 19:29:04 +03:00
return
2020-06-08 21:07:41 +03:00
} else if git . IsErrPushRejected ( err ) {
pushrejErr := err . ( * git . ErrPushRejected )
message := pushrejErr . Message
if len ( message ) == 0 {
ctx . Flash . Error ( ctx . Tr ( "repo.pulls.push_rejected_no_message" ) )
} else {
2021-12-15 09:59:57 +03:00
flashError , err := ctx . RenderToString ( tplAlertDetails , map [ string ] interface { } {
2020-10-21 02:50:10 +03:00
"Message" : ctx . Tr ( "repo.pulls.push_rejected" ) ,
"Summary" : ctx . Tr ( "repo.pulls.push_rejected_summary" ) ,
"Details" : utils . SanitizeFlashErrorString ( pushrejErr . Message ) ,
} )
if err != nil {
ctx . ServerError ( "CompareAndPullRequest.HTMLString" , err )
return
}
ctx . Flash . Error ( flashError )
2020-06-08 21:07:41 +03:00
}
2021-11-16 21:18:25 +03:00
ctx . Redirect ( pullIssue . Link ( ) )
2020-06-08 21:07:41 +03:00
return
2018-05-09 19:29:04 +03:00
}
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "NewPullRequest" , err )
2015-09-02 02:07:02 +03:00
return
2015-12-10 19:18:56 +03:00
}
2016-02-24 15:56:54 +03:00
log . Trace ( "Pull request created: %d/%d" , repo . ID , pullIssue . ID )
2021-11-16 21:18:25 +03:00
ctx . Redirect ( pullIssue . Link ( ) )
2014-03-24 14:25:15 +04:00
}
2015-10-24 10:36:47 +03:00
2017-06-21 04:00:03 +03:00
// CleanUpPullRequest responses for delete merged branch when PR has been merged
func CleanUpPullRequest ( ctx * context . Context ) {
issue := checkPullInfo ( ctx )
if ctx . Written ( ) {
return
}
2018-12-13 18:55:43 +03:00
pr := issue . PullRequest
2017-06-21 04:00:03 +03:00
2019-04-20 23:50:34 +03:00
// Don't cleanup unmerged and unclosed PRs
if ! pr . HasMerged && ! issue . IsClosed {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "CleanUpPullRequest" , nil )
2017-06-21 04:00:03 +03:00
return
}
2022-01-03 22:45:58 +03:00
// Don't cleanup when there are other PR's that use this branch as head branch.
exist , err := models . HasUnmergedPullRequestsByHeadInfo ( pr . HeadRepoID , pr . HeadBranch )
if err != nil {
ctx . ServerError ( "HasUnmergedPullRequestsByHeadInfo" , err )
return
}
if exist {
ctx . NotFound ( "CleanUpPullRequest" , nil )
return
}
2020-03-03 01:31:55 +03:00
if err := pr . LoadHeadRepo ( ) ; err != nil {
ctx . ServerError ( "LoadHeadRepo" , err )
2017-06-21 04:00:03 +03:00
return
} else if pr . HeadRepo == nil {
// Forked repository has already been deleted
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "CleanUpPullRequest" , nil )
2017-06-21 04:00:03 +03:00
return
2020-03-03 01:31:55 +03:00
} else if err = pr . LoadBaseRepo ( ) ; err != nil {
ctx . ServerError ( "LoadBaseRepo" , err )
2017-06-21 04:00:03 +03:00
return
2021-12-10 04:27:50 +03:00
} else if err = pr . HeadRepo . GetOwner ( db . DefaultContext ) ; err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "HeadRepo.GetOwner" , err )
2017-06-21 04:00:03 +03:00
return
}
2022-03-22 10:03:22 +03:00
perm , err := models . GetUserRepoPermission ( pr . HeadRepo , ctx . Doer )
2018-11-28 14:26:14 +03:00
if err != nil {
ctx . ServerError ( "GetUserRepoPermission" , err )
return
}
2021-11-09 22:57:58 +03:00
if ! perm . CanWrite ( unit . TypeCode ) {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "CleanUpPullRequest" , nil )
2017-06-21 04:00:03 +03:00
return
}
fullBranchName := pr . HeadRepo . Owner . Name + "/" + pr . HeadBranch
2021-07-13 02:26:25 +03:00
var gitBaseRepo * git . Repository
// Assume that the base repo is the current context (almost certainly)
if ctx . Repo != nil && ctx . Repo . Repository != nil && ctx . Repo . Repository . ID == pr . BaseRepoID && ctx . Repo . GitRepo != nil {
gitBaseRepo = ctx . Repo . GitRepo
} else {
// If not just open it
2022-01-20 02:26:57 +03:00
gitBaseRepo , err = git . OpenRepositoryCtx ( ctx , pr . BaseRepo . RepoPath ( ) )
2021-07-13 02:26:25 +03:00
if err != nil {
ctx . ServerError ( fmt . Sprintf ( "OpenRepository[%s]" , pr . BaseRepo . RepoPath ( ) ) , err )
return
}
defer gitBaseRepo . Close ( )
2017-06-21 04:00:03 +03:00
}
2021-07-13 02:26:25 +03:00
// Now assume that the head repo is the same as the base repo (reasonable chance)
gitRepo := gitBaseRepo
// But if not: is it the same as the context?
if pr . BaseRepoID != pr . HeadRepoID && ctx . Repo != nil && ctx . Repo . Repository != nil && ctx . Repo . Repository . ID == pr . HeadRepoID && ctx . Repo . GitRepo != nil {
gitRepo = ctx . Repo . GitRepo
} else if pr . BaseRepoID != pr . HeadRepoID {
// Otherwise just load it up
2022-01-20 02:26:57 +03:00
gitRepo , err = git . OpenRepositoryCtx ( ctx , pr . HeadRepo . RepoPath ( ) )
2021-07-13 02:26:25 +03:00
if err != nil {
ctx . ServerError ( fmt . Sprintf ( "OpenRepository[%s]" , pr . HeadRepo . RepoPath ( ) ) , err )
return
}
defer gitRepo . Close ( )
2017-06-21 04:00:03 +03:00
}
defer func ( ) {
2021-04-05 18:30:52 +03:00
ctx . JSON ( http . StatusOK , map [ string ] interface { } {
2021-11-16 21:18:25 +03:00
"redirect" : issue . Link ( ) ,
2017-06-21 04:00:03 +03:00
} )
} ( )
// Check if branch has no new commits
2018-01-30 15:29:39 +03:00
headCommitID , err := gitBaseRepo . GetRefCommitID ( pr . GetGitRefName ( ) )
if err != nil {
2019-04-02 10:48:31 +03:00
log . Error ( "GetRefCommitID: %v" , err )
2018-01-30 15:29:39 +03:00
ctx . Flash . Error ( ctx . Tr ( "repo.branch.deletion_failed" , fullBranchName ) )
return
}
branchCommitID , err := gitRepo . GetBranchCommitID ( pr . HeadBranch )
if err != nil {
2019-04-02 10:48:31 +03:00
log . Error ( "GetBranchCommitID: %v" , err )
2018-01-30 15:29:39 +03:00
ctx . Flash . Error ( ctx . Tr ( "repo.branch.deletion_failed" , fullBranchName ) )
return
}
if headCommitID != branchCommitID {
ctx . Flash . Error ( ctx . Tr ( "repo.branch.delete_branch_has_new_commits" , fullBranchName ) )
return
2017-06-21 04:00:03 +03:00
}
2021-07-13 02:26:25 +03:00
deleteBranch ( ctx , pr , gitRepo )
}
func deleteBranch ( ctx * context . Context , pr * models . PullRequest , gitRepo * git . Repository ) {
fullBranchName := pr . HeadRepo . Owner . Name + "/" + pr . HeadBranch
2022-03-22 10:03:22 +03:00
if err := repo_service . DeleteBranch ( ctx . Doer , pr . HeadRepo , gitRepo , pr . HeadBranch ) ; err != nil {
2021-06-07 17:52:59 +03:00
switch {
case git . IsErrBranchNotExist ( err ) :
ctx . Flash . Error ( ctx . Tr ( "repo.branch.deletion_failed" , fullBranchName ) )
case errors . Is ( err , repo_service . ErrBranchIsDefault ) :
ctx . Flash . Error ( ctx . Tr ( "repo.branch.deletion_failed" , fullBranchName ) )
case errors . Is ( err , repo_service . ErrBranchIsProtected ) :
ctx . Flash . Error ( ctx . Tr ( "repo.branch.deletion_failed" , fullBranchName ) )
default :
log . Error ( "DeleteBranch: %v" , err )
ctx . Flash . Error ( ctx . Tr ( "repo.branch.deletion_failed" , fullBranchName ) )
}
2017-06-21 04:00:03 +03:00
return
}
2022-03-22 10:03:22 +03:00
if err := models . AddDeletePRBranchComment ( ctx . Doer , pr . BaseRepo , pr . IssueID , pr . HeadBranch ) ; err != nil {
2017-06-21 04:00:03 +03:00
// Do not fail here as branch has already been deleted
2019-04-02 10:48:31 +03:00
log . Error ( "DeleteBranch: %v" , err )
2017-06-21 04:00:03 +03:00
}
ctx . Flash . Success ( ctx . Tr ( "repo.branch.deletion_success" , fullBranchName ) )
}
2018-01-05 13:56:52 +03:00
// DownloadPullDiff render a pull's raw diff
func DownloadPullDiff ( ctx * context . Context ) {
2019-12-14 01:21:06 +03:00
DownloadPullDiffOrPatch ( ctx , false )
2018-01-05 13:56:52 +03:00
}
2018-01-07 16:10:20 +03:00
// DownloadPullPatch render a pull's raw patch
func DownloadPullPatch ( ctx * context . Context ) {
2019-12-14 01:21:06 +03:00
DownloadPullDiffOrPatch ( ctx , true )
}
// DownloadPullDiffOrPatch render a pull's raw diff or patch
func DownloadPullDiffOrPatch ( ctx * context . Context , patch bool ) {
2021-10-05 17:41:48 +03:00
pr , err := models . GetPullRequestByIndex ( ctx . Repo . Repository . ID , ctx . ParamsInt64 ( ":index" ) )
2018-01-07 16:10:20 +03:00
if err != nil {
2021-10-05 17:41:48 +03:00
if models . IsErrPullRequestNotExist ( err ) {
ctx . NotFound ( "GetPullRequestByIndex" , err )
2018-01-07 16:10:20 +03:00
} else {
2021-10-05 17:41:48 +03:00
ctx . ServerError ( "GetPullRequestByIndex" , err )
2018-01-07 16:10:20 +03:00
}
return
}
2021-09-28 00:09:49 +03:00
binary := ctx . FormBool ( "binary" )
2018-01-07 16:10:20 +03:00
2022-01-20 02:26:57 +03:00
if err := pull_service . DownloadDiffOrPatch ( ctx , pr , ctx , patch , binary ) ; err != nil {
2019-12-14 01:21:06 +03:00
ctx . ServerError ( "DownloadDiffOrPatch" , err )
2018-01-07 16:10:20 +03:00
return
}
}
2019-12-16 09:20:25 +03:00
// UpdatePullRequestTarget change pull request's target branch
func UpdatePullRequestTarget ( ctx * context . Context ) {
issue := GetActionIssue ( ctx )
pr := issue . PullRequest
if ctx . Written ( ) {
return
}
if ! issue . IsPull {
ctx . Error ( http . StatusNotFound )
return
}
2022-03-22 10:03:22 +03:00
if ! ctx . IsSigned || ( ! issue . IsPoster ( ctx . Doer . ID ) && ! ctx . Repo . CanWriteIssuesOrPulls ( issue . IsPull ) ) {
2019-12-16 09:20:25 +03:00
ctx . Error ( http . StatusForbidden )
return
}
2021-07-29 04:42:15 +03:00
targetBranch := ctx . FormTrim ( "target_branch" )
2019-12-16 09:20:25 +03:00
if len ( targetBranch ) == 0 {
ctx . Error ( http . StatusNoContent )
return
}
2022-03-22 10:03:22 +03:00
if err := pull_service . ChangeTargetBranch ( ctx , pr , ctx . Doer , targetBranch ) ; err != nil {
2019-12-16 09:20:25 +03:00
if models . IsErrPullRequestAlreadyExists ( err ) {
err := err . ( models . ErrPullRequestAlreadyExists )
RepoRelPath := ctx . Repo . Owner . Name + "/" + ctx . Repo . Repository . Name
2021-11-16 21:18:25 +03:00
errorMessage := ctx . Tr ( "repo.pulls.has_pull_request" , html . EscapeString ( ctx . Repo . RepoLink + "/pulls/" + strconv . FormatInt ( err . IssueID , 10 ) ) , html . EscapeString ( RepoRelPath ) , err . IssueID ) // FIXME: Creates url insidde locale string
2019-12-16 09:20:25 +03:00
ctx . Flash . Error ( errorMessage )
ctx . JSON ( http . StatusConflict , map [ string ] interface { } {
"error" : err . Error ( ) ,
"user_error" : errorMessage ,
} )
} else if models . IsErrIssueIsClosed ( err ) {
errorMessage := ctx . Tr ( "repo.pulls.is_closed" )
ctx . Flash . Error ( errorMessage )
ctx . JSON ( http . StatusConflict , map [ string ] interface { } {
"error" : err . Error ( ) ,
"user_error" : errorMessage ,
} )
} else if models . IsErrPullRequestHasMerged ( err ) {
errorMessage := ctx . Tr ( "repo.pulls.has_merged" )
ctx . Flash . Error ( errorMessage )
ctx . JSON ( http . StatusConflict , map [ string ] interface { } {
"error" : err . Error ( ) ,
"user_error" : errorMessage ,
} )
} else if models . IsErrBranchesEqual ( err ) {
errorMessage := ctx . Tr ( "repo.pulls.nothing_to_compare" )
ctx . Flash . Error ( errorMessage )
ctx . JSON ( http . StatusBadRequest , map [ string ] interface { } {
"error" : err . Error ( ) ,
"user_error" : errorMessage ,
} )
} else {
ctx . ServerError ( "UpdatePullRequestTarget" , err )
}
return
}
2022-03-22 10:03:22 +03:00
notification . NotifyPullRequestChangeTargetBranch ( ctx . Doer , pr , targetBranch )
2019-12-16 09:20:25 +03:00
ctx . JSON ( http . StatusOK , map [ string ] interface { } {
"base_branch" : pr . BaseBranch ,
} )
}