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 (
2015-09-02 16:26:56 +03:00
"container/list"
2019-07-06 20:03:13 +03:00
"crypto/subtle"
2016-10-12 16:28:51 +03:00
"fmt"
2019-12-16 09:20:25 +03:00
"net/http"
2015-09-02 02:07:02 +03:00
"path"
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"
"code.gitea.io/gitea/modules/auth"
"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"
2019-12-25 08:59:41 +03:00
"code.gitea.io/gitea/modules/repofiles"
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"
2019-01-21 14:45:32 +03:00
"code.gitea.io/gitea/modules/util"
2020-02-22 16:08:48 +03:00
"code.gitea.io/gitea/routers/utils"
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"
2017-01-28 18:59:58 +03:00
2019-08-23 19:40:30 +03:00
"github.com/unknwon/com"
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
)
var (
2016-11-24 10:04:31 +03:00
pullRequestTemplateCandidates = [ ] string {
2017-01-05 03:48:23 +03:00
"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" ,
2016-02-18 01:21:31 +03:00
}
2014-06-23 07:11:12 +04:00
)
2019-11-11 18:15:29 +03:00
func getRepository ( ctx * context . Context , repoID int64 ) * models . Repository {
repo , err := models . GetRepositoryByID ( repoID )
2015-08-08 12:10:34 +03:00
if err != nil {
if models . 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
2019-11-11 18:15:29 +03:00
perm , err := models . GetUserRepoPermission ( repo , ctx . User )
2018-11-28 14:26:14 +03:00
if err != nil {
ctx . ServerError ( "GetUserRepoPermission" , err )
return nil
}
2019-11-11 18:15:29 +03:00
if ! perm . CanRead ( models . UnitTypeCode ) {
log . Trace ( "Permission Denied: User %-v cannot read %-v of repo %-v\n" +
"User in repo has Permissions: %-+v" ,
ctx . User ,
models . UnitTypeCode ,
ctx . Repo ,
perm )
ctx . NotFound ( "getRepository" , nil )
return nil
}
return repo
}
func getForkRepository ( ctx * context . Context ) * models . Repository {
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
}
2019-11-11 18:15:29 +03:00
if err := forkRepo . GetOwner ( ) ; 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
canForkToUser := forkRepo . OwnerID != ctx . User . ID && ! ctx . User . HasForkedRepo ( forkRepo . ID )
2015-08-08 12:10:34 +03:00
ctx . Data [ "ForkFrom" ] = forkRepo . Owner . Name + "/" + forkRepo . Name
2016-12-01 13:51:50 +03:00
ctx . Data [ "ForkFromOwnerID" ] = forkRepo . Owner . ID
2015-08-08 12:10:34 +03:00
2017-10-15 18:06:07 +03:00
if err := ctx . User . GetOwnedOrganizations ( ) ; err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetOwnedOrganizations" , err )
2015-08-08 12:10:34 +03:00
return nil
}
2017-10-15 18:06:07 +03:00
var orgs [ ] * models . User
for _ , org := range ctx . User . OwnedOrgs {
if forkRepo . OwnerID != org . ID && ! org . HasForkedRepo ( forkRepo . ID ) {
orgs = append ( orgs , org )
}
}
2017-11-06 07:12:55 +03:00
var traverseParentRepo = forkRepo
2019-11-11 18:15:29 +03:00
var err error
2017-11-06 07:12:55 +03:00
for {
if ctx . User . ID == traverseParentRepo . OwnerID {
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
}
traverseParentRepo , err = models . GetRepositoryByID ( traverseParentRepo . ForkID )
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 {
ctx . Data [ "ContextUser" ] = ctx . User
} 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
}
2016-11-24 10:04:31 +03:00
ctx . HTML ( 200 , tplFork )
2015-08-08 12:10:34 +03:00
}
2016-11-24 10:04:31 +03:00
// ForkPost response for forking a repository
2016-03-11 19:56:52 +03:00
func ForkPost ( ctx * context . Context , form auth . 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 ( ) {
2016-11-24 10:04:31 +03:00
ctx . HTML ( 200 , tplFork )
2015-08-08 12:10:34 +03:00
return
}
2017-11-06 07:12:55 +03:00
var err error
var traverseParentRepo = forkRepo
for {
if ctxUser . ID == traverseParentRepo . OwnerID {
ctx . RenderWithErr ( ctx . Tr ( "repo.settings.new_owner_has_same_repo" ) , tplFork , & form )
return
}
repo , has := models . HasForkedRepo ( ctxUser . ID , traverseParentRepo . ID )
if has {
ctx . Redirect ( setting . AppSubURL + "/" + ctxUser . Name + "/" + repo . Name )
return
}
if ! traverseParentRepo . IsFork {
break
}
traverseParentRepo , err = models . GetRepositoryByID ( traverseParentRepo . ForkID )
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
}
2015-08-08 12:10:34 +03:00
// Check ownership of organization.
if ctxUser . IsOrganization ( ) {
2017-12-21 10:43:26 +03:00
isOwner , err := ctxUser . IsOwnedBy ( ctx . User . ID )
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "IsOwnedBy" , err )
2017-12-21 10:43:26 +03:00
return
} else if ! isOwner {
2015-08-08 12:10:34 +03:00
ctx . Error ( 403 )
return
}
}
2019-10-26 09:54:11 +03:00
repo , err := repo_service . ForkRepository ( ctx . User , ctxUser , forkRepo , form . RepoName , 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 {
case models . IsErrRepoAlreadyExist ( err ) :
2016-11-24 10:04:31 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.settings.new_owner_has_same_repo" ) , tplFork , & form )
2015-08-08 12:10:34 +03:00
case models . IsErrNameReserved ( err ) :
2016-11-24 10:04:31 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.form.name_reserved" , err . ( models . ErrNameReserved ) . Name ) , tplFork , & form )
2015-08-08 12:10:34 +03:00
case models . IsErrNamePatternNotAllowed ( err ) :
2016-11-24 10:04:31 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.form.name_pattern_not_allowed" , err . ( models . 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 )
2016-11-27 13:14:25 +03:00
ctx . Redirect ( setting . AppSubURL + "/" + ctxUser . Name + "/" + 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.
2016-07-23 20:08:22 +03:00
if err = issue . ReadBy ( ctx . User . 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
}
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
2019-06-12 02:32:08 +03:00
compareInfo , err := ctx . Repo . GitRepo . GetCompareInfo ( ctx . Repo . Repository . RepoPath ( ) ,
2018-01-19 09:18:51 +03:00
pull . MergeBase , pull . GetGitRefName ( ) )
2015-09-02 16:26:56 +03:00
if err != nil {
2018-01-19 09:18:51 +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
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
}
2019-06-12 02:32:08 +03:00
ctx . Data [ "NumCommits" ] = compareInfo . Commits . Len ( )
ctx . Data [ "NumFiles" ] = compareInfo . NumFiles
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 {
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
2020-01-07 20:06:14 +03:00
baseGitRepo , err := git . OpenRepository ( pull . BaseRepo . RepoPath ( ) )
if err != nil {
ctx . ServerError ( "OpenRepository" , err )
return nil
}
defer baseGitRepo . Close ( )
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
}
commitStatuses , err := models . GetLatestCommitStatus ( repo , sha , 0 )
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 ( ) ,
pull . MergeBase , pull . GetGitRefName ( ) )
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
}
ctx . Data [ "NumCommits" ] = compareInfo . Commits . Len ( )
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 {
2020-01-07 20:06:14 +03:00
headGitRepo , err := git . OpenRepository ( 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
headBranchExist = headGitRepo . IsBranchExist ( pull . HeadBranch )
if headBranchExist {
2020-01-07 20:06:14 +03:00
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 {
2020-04-14 16:53:34 +03:00
ctx . Data [ "UpdateAllowed" ] , err = pull_service . IsUserAllowedToUpdate ( pull , ctx . User )
2020-01-25 05:48:22 +03:00
if err != nil {
ctx . ServerError ( "IsUserAllowedToUpdate" , err )
return nil
}
2020-04-10 14:26:37 +03:00
ctx . Data [ "GetCommitMessages" ] = pull_service . GetCommitMessages ( 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
}
commitStatuses , err := models . GetLatestCommitStatus ( repo , sha , 0 )
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 ( ) ,
2020-06-12 02:49:47 +03:00
git . BranchPrefix + pull . BaseBranch , pull . GetGitRefName ( ) )
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
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
}
2019-06-12 02:32:08 +03:00
ctx . Data [ "NumCommits" ] = compareInfo . Commits . Len ( )
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
2015-09-02 16:26:56 +03:00
var commits * list . List
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
commits = prInfo . Commits
2015-09-02 16:26:56 +03:00
commits = models . ValidateCommitsWithEmails ( commits )
2020-02-27 22:20:55 +03:00
commits = models . ParseCommitsWithSignature ( commits , ctx . Repo . Repository )
2017-05-07 17:40:31 +03:00
commits = models . ParseCommitsWithStatus ( commits , ctx . Repo . Repository )
2015-09-02 16:26:56 +03:00
ctx . Data [ "Commits" ] = commits
ctx . Data [ "CommitCount" ] = commits . Len ( )
2019-12-16 09:20:25 +03:00
getBranchData ( ctx , issue )
2016-11-24 10:04:31 +03:00
ctx . HTML ( 200 , 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
2018-08-14 20:49:33 +03:00
whitespaceFlags := map [ string ] string {
"ignore-all" : "-w" ,
"ignore-change" : "-b" ,
"ignore-eol" : "--ignore-space-at-eol" ,
"" : "" }
2015-09-02 16:26:56 +03:00
var (
diffRepoPath string
startCommitID string
endCommitID string
gitRepo * git . Repository
)
2017-06-21 01:25:38 +03:00
var headTarget string
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
diffRepoPath = ctx . Repo . GitRepo . Path
gitRepo = ctx . Repo . GitRepo
2015-09-02 16:26:56 +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
headTarget = path . Join ( ctx . Repo . Owner . Name , ctx . Repo . Repository . Name )
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
2019-09-06 05:20:09 +03:00
diff , err := gitdiff . GetDiffRangeWithWhitespaceBehavior ( diffRepoPath ,
2016-06-29 18:11:00 +03:00
startCommitID , endCommitID , setting . Git . MaxGitDiffLines ,
2018-08-14 20:49:33 +03:00
setting . Git . MaxGitDiffLineCharacters , setting . Git . MaxGitDiffFiles ,
whitespaceFlags [ ctx . Data [ "WhitespaceBehavior" ] . ( string ) ] )
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
if err = diff . LoadComments ( issue , ctx . User ) ; err != nil {
ctx . ServerError ( "LoadComments" , err )
return
}
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
}
2020-04-18 16:50:25 +03:00
if ctx . IsSigned && ctx . User != nil {
if ctx . Data [ "CanMarkConversation" ] , err = models . CanMarkConversation ( issue , ctx . User ) ; err != nil {
ctx . ServerError ( "CanMarkConversation" , err )
return
}
}
2019-10-04 22:58:54 +03:00
setImageCompareContext ( ctx , baseCommit , commit )
setPathsCompareContext ( ctx , baseCommit , commit , headTarget )
2019-09-16 12:03:22 +03:00
2016-01-31 19:19:02 +03:00
ctx . Data [ "RequireHighlightJS" ] = true
2019-12-18 06:59:59 +03:00
ctx . Data [ "RequireSimpleMDE" ] = true
2018-08-06 07:43:22 +03:00
ctx . Data [ "RequireTribute" ] = true
if ctx . Data [ "Assignees" ] , err = ctx . Repo . Repository . GetAssignees ( ) ; err != nil {
ctx . ServerError ( "GetAssignees" , err )
return
}
ctx . Data [ "CurrentReview" ] , err = models . GetCurrentReview ( ctx . User , issue )
if err != nil && ! models . IsErrReviewNotExist ( err ) {
ctx . ServerError ( "GetCurrentReview" , err )
return
}
2019-12-16 09:20:25 +03:00
getBranchData ( ctx , issue )
2020-03-06 04:57:18 +03:00
ctx . Data [ "IsIssuePoster" ] = ctx . IsSigned && issue . IsPoster ( ctx . User . ID )
2020-04-04 08:39:48 +03:00
ctx . Data [ "HasIssuesOrPullsWritePermission" ] = ctx . Repo . CanWriteIssuesOrPulls ( issue . IsPull )
2016-11-24 10:04:31 +03:00
ctx . HTML ( 200 , tplPullFiles )
2015-09-02 11:08:05 +03:00
}
2015-08-31 10:24:28 +03:00
2020-01-17 09:03:40 +03:00
// UpdatePullRequest merge master into PR
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
}
if err := issue . PullRequest . LoadBaseRepo ( ) ; err != nil {
ctx . InternalServerError ( err )
return
}
if err := issue . PullRequest . LoadHeadRepo ( ) ; err != nil {
ctx . InternalServerError ( err )
return
}
allowedUpdate , err := pull_service . IsUserAllowedToUpdate ( issue . PullRequest , ctx . User )
if err != nil {
ctx . ServerError ( "IsUserAllowedToMerge" , err )
return
}
// ToDo: add check if maintainers are allowed to change branch ... (need migration & co)
if ! allowedUpdate {
ctx . Flash . Error ( ctx . Tr ( "repo.pulls.update_not_allowed" ) )
ctx . Redirect ( ctx . Repo . RepoLink + "/pulls/" + com . ToStr ( issue . Index ) )
return
}
// default merge commit message
message := fmt . Sprintf ( "Merge branch '%s' into %s" , issue . PullRequest . BaseBranch , issue . PullRequest . HeadBranch )
if err = pull_service . Update ( issue . PullRequest , ctx . User , message ) ; err != nil {
if models . IsErrMergeConflicts ( err ) {
conflictError := err . ( models . ErrMergeConflicts )
2020-03-28 07:13:18 +03:00
ctx . Flash . Error ( ctx . Tr ( "repo.pulls.merge_conflict" , utils . SanitizeFlashErrorString ( conflictError . StdErr ) , utils . SanitizeFlashErrorString ( conflictError . StdOut ) ) )
2020-01-17 09:03:40 +03:00
ctx . Redirect ( ctx . Repo . RepoLink + "/pulls/" + com . ToStr ( issue . Index ) )
return
}
ctx . Flash . Error ( err . Error ( ) )
ctx . Redirect ( ctx . Repo . RepoLink + "/pulls/" + com . ToStr ( issue . Index ) )
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" ) )
ctx . Redirect ( ctx . Repo . RepoLink + "/pulls/" + com . ToStr ( issue . Index ) )
}
2016-11-24 10:04:31 +03:00
// MergePullRequest response for merging pull request
2018-01-05 21:56:50 +03:00
func MergePullRequest ( ctx * context . Context , form auth . 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" ) )
ctx . Redirect ( ctx . Repo . RepoLink + "/pulls/" + com . ToStr ( issue . Index ) )
return
}
ctx . Flash . Error ( ctx . Tr ( "repo.issues.closed_title" ) )
ctx . Redirect ( ctx . Repo . RepoLink + "/issues/" + com . ToStr ( issue . Index ) )
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
2020-01-11 10:29:34 +03:00
allowedMerge , err := pull_service . IsUserAllowedToMerge ( pr , ctx . Repo . Permission , ctx . User )
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" ) )
ctx . Redirect ( ctx . Repo . RepoLink + "/pulls/" + com . ToStr ( issue . Index ) )
2020-01-11 10:29:34 +03:00
return
}
2020-01-27 13:26:53 +03:00
if ! pr . CanAutoMerge ( ) {
ctx . Flash . Error ( ctx . Tr ( "repo.pulls.no_merge_not_ready" ) )
ctx . Redirect ( ctx . Repo . RepoLink + "/pulls/" + com . ToStr ( issue . Index ) )
return
}
if pr . HasMerged {
ctx . Flash . Error ( ctx . Tr ( "repo.pulls.has_merged" ) )
ctx . Redirect ( ctx . Repo . RepoLink + "/pulls/" + com . ToStr ( issue . Index ) )
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" ) )
ctx . Redirect ( ctx . Repo . RepoLink + "/pulls/" + com . ToStr ( pr . Index ) )
return
}
2020-01-11 10:29:34 +03:00
if err := pull_service . CheckPRReadyToMerge ( pr ) ; err != nil {
if ! models . IsErrNotAllowedToMerge ( err ) {
ctx . ServerError ( "Merge PR status" , err )
return
}
if isRepoAdmin , err := models . IsUserRepoAdmin ( pr . BaseRepo , ctx . User ) ; err != nil {
ctx . ServerError ( "IsUserRepoAdmin" , err )
return
} else if ! isRepoAdmin {
ctx . Flash . Error ( ctx . Tr ( "repo.pulls.no_merge_not_ready" ) )
ctx . Redirect ( ctx . Repo . RepoLink + "/pulls/" + com . ToStr ( pr . Index ) )
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 ) )
ctx . Redirect ( ctx . Repo . RepoLink + "/pulls/" + com . ToStr ( pr . Index ) )
return
}
message := strings . TrimSpace ( form . MergeTitleField )
if len ( message ) == 0 {
if models . MergeStyle ( form . Do ) == models . MergeStyleMerge {
message = pr . GetDefaultMergeMessage ( )
}
2018-12-27 13:27:08 +03:00
if models . MergeStyle ( form . Do ) == models . MergeStyleRebaseMerge {
message = pr . GetDefaultMergeMessage ( )
}
2018-01-05 21:56:50 +03:00
if models . MergeStyle ( form . Do ) == models . MergeStyleSquash {
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" ) )
ctx . Redirect ( ctx . Repo . RepoLink + "/pulls/" + com . ToStr ( pr . Index ) )
return
}
2019-09-27 03:22:36 +03:00
if err = pull_service . Merge ( pr , ctx . User , ctx . Repo . GitRepo , models . MergeStyle ( form . Do ) , 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" ) )
ctx . Redirect ( ctx . Repo . RepoLink + "/pulls/" + com . ToStr ( pr . Index ) )
return
2019-11-10 11:42:51 +03:00
} else if models . IsErrMergeConflicts ( err ) {
conflictError := err . ( models . ErrMergeConflicts )
2020-02-22 16:08:48 +03:00
ctx . Flash . Error ( ctx . Tr ( "repo.pulls.merge_conflict" , utils . SanitizeFlashErrorString ( conflictError . StdErr ) , utils . SanitizeFlashErrorString ( conflictError . StdOut ) ) )
2019-11-10 11:42:51 +03:00
ctx . Redirect ( ctx . Repo . RepoLink + "/pulls/" + com . ToStr ( pr . Index ) )
return
} else if models . IsErrRebaseConflicts ( err ) {
conflictError := err . ( models . ErrRebaseConflicts )
2020-02-22 16:08:48 +03:00
ctx . Flash . Error ( ctx . Tr ( "repo.pulls.rebase_conflict" , utils . SanitizeFlashErrorString ( conflictError . CommitSHA ) , utils . SanitizeFlashErrorString ( conflictError . StdErr ) , utils . SanitizeFlashErrorString ( conflictError . StdOut ) ) )
2019-11-10 11:42:51 +03:00
ctx . Redirect ( ctx . Repo . RepoLink + "/pulls/" + com . ToStr ( pr . Index ) )
return
} else if models . IsErrMergeUnrelatedHistories ( err ) {
log . Debug ( "MergeUnrelatedHistories error: %v" , err )
ctx . Flash . Error ( ctx . Tr ( "repo.pulls.unrelated_histories" ) )
ctx . Redirect ( ctx . Repo . RepoLink + "/pulls/" + com . ToStr ( pr . Index ) )
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" ) )
ctx . Redirect ( ctx . Repo . RepoLink + "/pulls/" + com . ToStr ( pr . Index ) )
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 {
ctx . Flash . Error ( ctx . Tr ( "repo.pulls.push_rejected" , utils . SanitizeFlashErrorString ( pushrejErr . Message ) ) )
}
ctx . Redirect ( ctx . Repo . RepoLink + "/pulls/" + com . ToStr ( pr . Index ) )
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
}
2019-02-05 14:38:11 +03:00
if err := stopTimerIfAvailable ( ctx . User , issue ) ; err != nil {
ctx . ServerError ( "CreateOrStopIssueStopwatch" , err )
return
}
2015-09-02 16:26:56 +03:00
log . Trace ( "Pull request merged: %d" , pr . ID )
2015-10-19 02:30:39 +03:00
ctx . Redirect ( ctx . Repo . RepoLink + "/pulls/" + com . ToStr ( pr . Index ) )
2015-09-02 16:26:56 +03:00
}
2019-02-05 14:38:11 +03:00
func stopTimerIfAvailable ( user * models . User , issue * models . Issue ) error {
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
2016-03-11 19:56:52 +03:00
func CompareAndPullRequestPost ( ctx * context . Context , form auth . 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
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
2015-09-02 02:07:02 +03:00
renderAttachmentSettings ( ctx )
var (
repo = ctx . Repo . Repository
attachments [ ] string
)
2015-09-02 02:26:39 +03:00
headUser , headRepo , headGitRepo , prInfo , baseBranch , headBranch := ParseCompareInfo ( ctx )
2015-09-02 02:07:02 +03:00
if ctx . Written ( ) {
return
}
2019-11-13 10:01:19 +03:00
defer headGitRepo . Close ( )
2015-09-02 02:07:02 +03:00
2018-11-28 14:26:14 +03:00
labelIDs , assigneeIDs , milestoneID := ValidateRepoMetas ( ctx , form , true )
2015-09-02 02:07:02 +03:00
if ctx . Written ( ) {
return
}
if setting . AttachmentEnabled {
2016-08-11 15:48:08 +03:00
attachments = form . Files
2015-09-02 02:07:02 +03:00
}
if ctx . HasError ( ) {
2016-08-16 00:04:44 +03:00
auth . AssignForm ( form , ctx . Data )
// This stage is already stop creating new pull request, so it does not matter if it has
// something to compare or not.
PrepareCompareDiff ( ctx , headUser , headRepo , headGitRepo , prInfo , baseBranch , headBranch )
if ctx . Written ( ) {
return
}
2019-06-07 23:29:29 +03:00
ctx . HTML ( 200 , tplCompareDiff )
2015-09-02 02:07:02 +03:00
return
}
2019-01-21 14:45:32 +03:00
if util . IsEmptyString ( form . Title ) {
PrepareCompareDiff ( ctx , headUser , headRepo , headGitRepo , prInfo , baseBranch , headBranch )
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 ,
2016-08-14 13:32:24 +03:00
Title : form . Title ,
2016-07-23 20:08:22 +03:00
PosterID : ctx . User . ID ,
2015-09-02 02:07:02 +03:00
Poster : ctx . User ,
MilestoneID : milestoneID ,
IsPull : true ,
Content : form . Content ,
}
2016-02-24 15:56:54 +03:00
pullRequest := & models . PullRequest {
2019-10-18 14:13:31 +03:00
HeadRepoID : headRepo . ID ,
BaseRepoID : repo . ID ,
HeadBranch : headBranch ,
BaseBranch : baseBranch ,
HeadRepo : headRepo ,
BaseRepo : repo ,
MergeBase : prInfo . MergeBase ,
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
2019-12-14 01:21:06 +03:00
if err := pull_service . NewPullRequest ( repo , pullIssue , labelIDs , attachments , pullRequest , assigneeIDs ) ; err != nil {
2018-05-09 19:29:04 +03:00
if models . IsErrUserDoesNotHaveAccessToRepo ( err ) {
ctx . Error ( 400 , "UserDoesNotHaveAccessToRepo" , err . Error ( ) )
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 {
ctx . Flash . Error ( ctx . Tr ( "repo.pulls.push_rejected" , utils . SanitizeFlashErrorString ( pushrejErr . Message ) ) )
}
ctx . Redirect ( ctx . Repo . RepoLink + "/pulls/" + com . ToStr ( pullIssue . Index ) )
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 )
ctx . Redirect ( ctx . Repo . RepoLink + "/pulls/" + com . ToStr ( pullIssue . Index ) )
2014-03-24 14:25:15 +04:00
}
2015-10-24 10:36:47 +03:00
2016-11-24 10:04:31 +03:00
// TriggerTask response for a trigger task request
2016-03-11 19:56:52 +03:00
func TriggerTask ( ctx * context . Context ) {
2016-08-14 13:32:24 +03:00
pusherID := ctx . QueryInt64 ( "pusher" )
2015-12-15 01:06:54 +03:00
branch := ctx . Query ( "branch" )
secret := ctx . Query ( "secret" )
2016-08-14 13:32:24 +03:00
if len ( branch ) == 0 || len ( secret ) == 0 || pusherID <= 0 {
2015-12-15 01:06:54 +03:00
ctx . Error ( 404 )
2016-08-14 13:32:24 +03:00
log . Trace ( "TriggerTask: branch or secret is empty, or pusher ID is not valid" )
2015-12-15 01:06:54 +03:00
return
}
owner , repo := parseOwnerAndRepo ( ctx )
2015-10-24 10:36:47 +03:00
if ctx . Written ( ) {
return
}
2019-07-06 20:03:13 +03:00
got := [ ] byte ( base . EncodeMD5 ( owner . Salt ) )
want := [ ] byte ( secret )
if subtle . ConstantTimeCompare ( got , want ) != 1 {
2015-12-15 01:06:54 +03:00
ctx . Error ( 404 )
log . Trace ( "TriggerTask [%s/%s]: invalid secret" , owner . Name , repo . Name )
2015-10-24 10:36:47 +03:00
return
}
2016-08-14 13:32:24 +03:00
pusher , err := models . GetUserByID ( pusherID )
if err != nil {
if models . IsErrUserNotExist ( err ) {
ctx . Error ( 404 )
} else {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetUserByID" , err )
2016-08-14 13:32:24 +03:00
}
return
}
log . Trace ( "TriggerTask '%s/%s' by %s" , repo . Name , branch , pusher . Name )
2015-10-24 10:36:47 +03:00
2020-01-09 04:47:45 +03:00
go pull_service . AddTestPullRequestTask ( pusher , repo . ID , branch , true , "" , "" )
2015-10-24 10:36:47 +03:00
ctx . Status ( 202 )
}
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
}
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
2019-06-12 22:41:28 +03:00
} else if err = pr . HeadRepo . GetOwner ( ) ; err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "HeadRepo.GetOwner" , err )
2017-06-21 04:00:03 +03:00
return
}
2018-11-28 14:26:14 +03:00
perm , err := models . GetUserRepoPermission ( pr . HeadRepo , ctx . User )
if err != nil {
ctx . ServerError ( "GetUserRepoPermission" , err )
return
}
if ! perm . CanWrite ( models . UnitTypeCode ) {
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
gitRepo , err := git . OpenRepository ( pr . HeadRepo . RepoPath ( ) )
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( fmt . Sprintf ( "OpenRepository[%s]" , pr . HeadRepo . RepoPath ( ) ) , err )
2017-06-21 04:00:03 +03:00
return
}
2019-11-13 10:01:19 +03:00
defer gitRepo . Close ( )
2017-06-21 04:00:03 +03:00
gitBaseRepo , err := git . OpenRepository ( pr . BaseRepo . RepoPath ( ) )
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( fmt . Sprintf ( "OpenRepository[%s]" , pr . BaseRepo . RepoPath ( ) ) , err )
2017-06-21 04:00:03 +03:00
return
}
2019-11-13 10:01:19 +03:00
defer gitBaseRepo . Close ( )
2017-06-21 04:00:03 +03:00
defer func ( ) {
ctx . JSON ( 200 , map [ string ] interface { } {
"redirect" : pr . BaseRepo . Link ( ) + "/pulls/" + com . ToStr ( issue . Index ) ,
} )
} ( )
if pr . HeadBranch == pr . HeadRepo . DefaultBranch || ! gitRepo . IsBranchExist ( pr . HeadBranch ) {
ctx . Flash . Error ( ctx . Tr ( "repo.branch.deletion_failed" , fullBranchName ) )
return
}
// Check if branch is not protected
2017-09-14 11:16:22 +03:00
if protected , err := pr . HeadRepo . IsProtectedBranch ( pr . HeadBranch , ctx . User ) ; err != nil || protected {
2017-06-21 04:00:03 +03:00
if err != nil {
2019-04-02 10:48:31 +03:00
log . Error ( "HeadRepo.IsProtectedBranch: %v" , err )
2017-06-21 04:00:03 +03:00
}
ctx . Flash . Error ( ctx . Tr ( "repo.branch.deletion_failed" , fullBranchName ) )
return
}
// 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
}
if err := gitRepo . DeleteBranch ( pr . HeadBranch , git . DeleteBranchOptions {
Force : true ,
} ) ; err != nil {
2019-04-02 10:48:31 +03:00
log . Error ( "DeleteBranch: %v" , err )
2017-06-21 04:00:03 +03:00
ctx . Flash . Error ( ctx . Tr ( "repo.branch.deletion_failed" , fullBranchName ) )
return
}
2019-12-25 08:59:41 +03:00
if err := repofiles . PushUpdate (
pr . HeadRepo ,
pr . HeadBranch ,
repofiles . PushUpdateOptions {
RefFullName : git . BranchPrefix + pr . HeadBranch ,
OldCommitID : branchCommitID ,
NewCommitID : git . EmptySHA ,
PusherID : ctx . User . ID ,
PusherName : ctx . User . Name ,
RepoUserName : pr . HeadRepo . Owner . Name ,
RepoName : pr . HeadRepo . Name ,
} ) ; err != nil {
log . Error ( "Update: %v" , err )
}
2017-06-21 04:00:03 +03:00
if err := models . AddDeletePRBranchComment ( ctx . User , pr . BaseRepo , issue . ID , pr . HeadBranch ) ; err != nil {
// 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 ) {
2018-01-07 16:10:20 +03:00
issue , err := models . GetIssueByIndex ( ctx . Repo . Repository . ID , ctx . ParamsInt64 ( ":index" ) )
if err != nil {
if models . IsErrIssueNotExist ( err ) {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "GetIssueByIndex" , err )
2018-01-07 16:10:20 +03:00
} else {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetIssueByIndex" , err )
2018-01-07 16:10:20 +03:00
}
return
}
// Return not found if it's not a pull request
if ! issue . IsPull {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "DownloadPullDiff" ,
2018-01-07 16:10:20 +03:00
fmt . Errorf ( "Issue is not a pull request" ) )
return
}
2018-12-13 18:55:43 +03:00
if err = issue . LoadPullRequest ( ) ; err != nil {
ctx . ServerError ( "LoadPullRequest" , err )
return
}
2018-01-07 16:10:20 +03:00
2018-12-13 18:55:43 +03:00
pr := issue . PullRequest
2018-01-07 16:10:20 +03:00
2019-12-14 01:21:06 +03:00
if err := pull_service . DownloadDiffOrPatch ( pr , ctx , patch ) ; err != nil {
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
}
if ! ctx . IsSigned || ( ! issue . IsPoster ( ctx . User . ID ) && ! ctx . Repo . CanWriteIssuesOrPulls ( issue . IsPull ) ) {
ctx . Error ( http . StatusForbidden )
return
}
targetBranch := ctx . QueryTrim ( "target_branch" )
if len ( targetBranch ) == 0 {
ctx . Error ( http . StatusNoContent )
return
}
if err := pull_service . ChangeTargetBranch ( pr , ctx . User , targetBranch ) ; err != nil {
if models . IsErrPullRequestAlreadyExists ( err ) {
err := err . ( models . ErrPullRequestAlreadyExists )
RepoRelPath := ctx . Repo . Owner . Name + "/" + ctx . Repo . Repository . Name
errorMessage := ctx . Tr ( "repo.pulls.has_pull_request" , ctx . Repo . RepoLink , RepoRelPath , err . IssueID )
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
}
notification . NotifyPullRequestChangeTargetBranch ( ctx . User , pr , targetBranch )
ctx . JSON ( http . StatusOK , map [ string ] interface { } {
"base_branch" : pr . BaseBranch ,
} )
}