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"
2016-10-12 16:28:51 +03:00
"fmt"
2018-01-07 16:10:20 +03:00
"io"
2015-09-02 02:07:02 +03:00
"path"
2015-08-08 17:43:14 +03:00
"strings"
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"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/setting"
2019-01-21 14:45:32 +03:00
"code.gitea.io/gitea/modules/util"
2017-01-28 18:59:58 +03:00
2016-11-11 15:11:45 +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"
tplComparePull base . TplName = "repo/pulls/compare"
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
)
2016-03-11 19:56:52 +03:00
func getForkRepository ( ctx * context . Context ) * models . Repository {
2015-08-08 17:43:14 +03:00
forkRepo , err := models . GetRepositoryByID ( ctx . ParamsInt64 ( ":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
2018-11-28 14:26:14 +03:00
perm , err := models . GetUserRepoPermission ( forkRepo , ctx . User )
if err != nil {
ctx . ServerError ( "GetUserRepoPermission" , err )
return nil
}
2019-01-18 03:01:04 +03:00
if forkRepo . IsEmpty || ! perm . CanRead ( models . UnitTypeCode ) {
2019-04-22 23:40:51 +03:00
if log . IsTrace ( ) {
if forkRepo . IsEmpty {
log . Trace ( "Empty fork repository %-v" , forkRepo )
} else {
log . Trace ( "Permission Denied: User %-v cannot read %-v of forkRepo %-v\n" +
"User in forkRepo has Permissions: %-+v" ,
ctx . User ,
models . UnitTypeCode ,
ctx . Repo ,
perm )
}
}
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "getForkRepository" , nil )
2015-09-01 18:57:02 +03:00
return nil
}
2015-08-08 12:10:34 +03:00
ctx . Data [ "repo_name" ] = forkRepo . Name
2015-11-18 23:11:37 +03:00
ctx . Data [ "description" ] = forkRepo . Description
2015-08-08 12:10:34 +03:00
ctx . Data [ "IsPrivate" ] = forkRepo . IsPrivate
2017-10-15 18:06:07 +03:00
canForkToUser := forkRepo . OwnerID != ctx . User . ID && ! ctx . User . HasForkedRepo ( forkRepo . ID )
2015-08-08 12:10:34 +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
}
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
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
}
}
2017-09-03 11:20:24 +03:00
repo , err := models . 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
}
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
}
2016-08-16 20:19:09 +03:00
if err = issue . PullRequest . GetHeadRepo ( ) ; err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetHeadRepo" , 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 ) {
if ctx . Repo . Owner . Name == pull . HeadUserName {
ctx . Data [ "HeadTarget" ] = pull . HeadBranch
} else if pull . HeadRepo == nil {
ctx . Data [ "HeadTarget" ] = pull . HeadUserName + ":" + pull . HeadBranch
} else {
ctx . Data [ "HeadTarget" ] = pull . HeadUserName + "/" + pull . HeadRepo . Name + ":" + pull . HeadBranch
}
ctx . Data [ "BaseTarget" ] = pull . BaseBranch
}
2016-11-24 10:04:31 +03:00
// PrepareMergedViewPullInfo show meta information for a merged pull request view page
2018-01-19 09:18:51 +03:00
func PrepareMergedViewPullInfo ( ctx * context . Context , issue * models . Issue ) * git . PullRequestInfo {
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
2018-01-19 09:18:51 +03:00
prInfo , err := ctx . Repo . GitRepo . GetPullRequestInfo ( ctx . Repo . Repository . RepoPath ( ) ,
pull . MergeBase , pull . GetGitRefName ( ) )
2017-11-25 16:47:19 +03:00
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
2018-01-19 09:18:51 +03:00
ctx . Data [ "BaseTarget" ] = "deleted"
ctx . Data [ "NumCommits" ] = 0
ctx . Data [ "NumFiles" ] = 0
return nil
}
ctx . ServerError ( "GetPullRequestInfo" , err )
return nil
2015-09-02 16:26:56 +03:00
}
2018-01-19 09:18:51 +03:00
ctx . Data [ "NumCommits" ] = prInfo . Commits . Len ( )
ctx . Data [ "NumFiles" ] = prInfo . NumFiles
return prInfo
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
2016-08-16 20:19:09 +03:00
func PrepareViewPullInfo ( ctx * context . Context , issue * models . Issue ) * git . PullRequestInfo {
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
2017-10-04 20:35:01 +03:00
var err error
2015-10-24 10:36:47 +03:00
if err = pull . GetHeadRepo ( ) ; err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetHeadRepo" , err )
2015-10-24 10:36:47 +03:00
return nil
}
2017-10-04 20:35:01 +03:00
setMergeTarget ( ctx , pull )
var headGitRepo * git . Repository
2015-10-05 03:54:06 +03:00
if pull . HeadRepo != nil {
2015-11-27 01:33:45 +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
}
2015-09-02 11:08:05 +03:00
}
2015-10-19 02:30:39 +03:00
if pull . HeadRepo == nil || ! headGitRepo . IsBranchExist ( pull . HeadBranch ) {
2018-08-01 06:00:35 +03:00
ctx . Data [ "IsPullRequestBroken" ] = true
2015-09-02 16:26:56 +03:00
ctx . Data [ "HeadTarget" ] = "deleted"
ctx . Data [ "NumCommits" ] = 0
ctx . Data [ "NumFiles" ] = 0
return nil
}
2015-09-02 11:08:05 +03:00
prInfo , err := headGitRepo . GetPullRequestInfo ( models . RepoPath ( repo . Owner . Name , repo . Name ) ,
2015-10-19 02:30:39 +03:00
pull . BaseBranch , pull . HeadBranch )
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
2016-07-23 13:35:16 +03:00
ctx . Data [ "BaseTarget" ] = "deleted"
ctx . Data [ "NumCommits" ] = 0
ctx . Data [ "NumFiles" ] = 0
return nil
}
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetPullRequestInfo" , 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
}
2015-09-02 11:08:05 +03:00
ctx . Data [ "NumCommits" ] = prInfo . Commits . Len ( )
ctx . Data [ "NumFiles" ] = prInfo . NumFiles
return prInfo
}
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
if pull . HasMerged {
2018-01-19 09:18:51 +03:00
prInfo := PrepareMergedViewPullInfo ( ctx , issue )
2015-09-02 16:26:56 +03:00
if ctx . Written ( ) {
return
2018-01-19 09:18:51 +03:00
} else if prInfo == nil {
ctx . NotFound ( "ViewPullCommits" , nil )
return
2015-09-02 16:26:56 +03:00
}
2017-06-21 01:25:38 +03:00
ctx . Data [ "Username" ] = ctx . Repo . Owner . Name
ctx . Data [ "Reponame" ] = ctx . Repo . Repository . Name
2018-01-19 09:18:51 +03:00
commits = prInfo . Commits
2015-09-02 16:26:56 +03:00
} else {
2016-08-16 20:19:09 +03:00
prInfo := PrepareViewPullInfo ( ctx , issue )
2015-09-02 16:26:56 +03:00
if ctx . Written ( ) {
return
} else if prInfo == nil {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "ViewPullCommits" , nil )
2015-09-02 16:26:56 +03:00
return
}
2017-06-21 01:25:38 +03:00
ctx . Data [ "Username" ] = pull . HeadUserName
ctx . Data [ "Reponame" ] = pull . HeadRepo . Name
2015-09-02 16:26:56 +03:00
commits = prInfo . Commits
2015-09-02 11:08:05 +03:00
}
2015-09-02 16:26:56 +03:00
commits = models . ValidateCommitsWithEmails ( commits )
2017-05-07 17:40:31 +03:00
commits = models . ParseCommitsWithSignature ( commits )
commits = models . ParseCommitsWithStatus ( commits , ctx . Repo . Repository )
2015-09-02 16:26:56 +03:00
ctx . Data [ "Commits" ] = commits
ctx . Data [ "CommitCount" ] = commits . Len ( )
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
2015-09-02 16:26:56 +03:00
if pull . HasMerged {
2018-01-19 09:18:51 +03:00
prInfo := PrepareMergedViewPullInfo ( ctx , issue )
2015-09-02 16:26:56 +03:00
if ctx . Written ( ) {
return
2018-01-19 09:18:51 +03:00
} else if prInfo == nil {
ctx . NotFound ( "ViewPullFiles" , nil )
return
2015-09-02 16:26:56 +03:00
}
2015-09-02 11:08:05 +03:00
2015-09-02 16:26:56 +03:00
diffRepoPath = ctx . Repo . GitRepo . Path
2018-01-19 09:18:51 +03:00
gitRepo = ctx . Repo . GitRepo
headCommitID , err := gitRepo . GetRefCommitID ( pull . GetGitRefName ( ) )
2017-11-25 16:47:19 +03:00
if err != nil {
2018-01-19 09:18:51 +03:00
ctx . ServerError ( "GetRefCommitID" , err )
2017-11-25 16:47:19 +03:00
return
}
2018-01-19 09:18:51 +03:00
startCommitID = prInfo . MergeBase
endCommitID = headCommitID
2017-06-21 01:25:38 +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
2015-09-02 16:26:56 +03:00
} else {
2016-08-16 20:19:09 +03:00
prInfo := PrepareViewPullInfo ( ctx , issue )
2015-09-02 16:26:56 +03:00
if ctx . Written ( ) {
return
} else if prInfo == nil {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "ViewPullFiles" , nil )
2015-09-02 16:26:56 +03:00
return
}
2015-09-02 11:08:05 +03:00
2015-09-02 16:26:56 +03:00
headRepoPath := models . RepoPath ( pull . HeadUserName , pull . HeadRepo . Name )
2015-09-02 11:08:05 +03:00
2015-09-02 16:26:56 +03:00
headGitRepo , err := git . OpenRepository ( headRepoPath )
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "OpenRepository" , err )
2015-09-02 16:26:56 +03:00
return
}
2015-12-10 04:46:05 +03:00
headCommitID , err := headGitRepo . GetBranchCommitID ( pull . HeadBranch )
2015-09-02 16:26:56 +03:00
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetBranchCommitID" , err )
2015-09-02 16:26:56 +03:00
return
}
diffRepoPath = headRepoPath
startCommitID = prInfo . MergeBase
endCommitID = headCommitID
gitRepo = headGitRepo
2017-06-21 01:25:38 +03:00
headTarget = path . Join ( pull . HeadUserName , pull . HeadRepo . Name )
ctx . Data [ "Username" ] = pull . HeadUserName
ctx . Data [ "Reponame" ] = pull . HeadRepo . Name
2015-09-02 11:08:05 +03:00
}
2018-08-14 20:49:33 +03:00
diff , err := models . 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
ctx . Data [ "DiffNotAvailable" ] = diff . NumFiles ( ) == 0
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
}
2015-09-02 16:26:56 +03:00
ctx . Data [ "IsImageFile" ] = commit . IsImageFile
2017-11-27 03:58:54 +03:00
ctx . Data [ "SourcePath" ] = setting . AppSubURL + "/" + path . Join ( headTarget , "src" , "commit" , endCommitID )
ctx . Data [ "BeforeSourcePath" ] = setting . AppSubURL + "/" + path . Join ( headTarget , "src" , "commit" , startCommitID )
ctx . Data [ "RawPath" ] = setting . AppSubURL + "/" + path . Join ( headTarget , "raw" , "commit" , endCommitID )
2016-01-31 19:19:02 +03:00
ctx . Data [ "RequireHighlightJS" ] = 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
}
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
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 {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "MergePullRequest" , nil )
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
2015-10-19 00:18:45 +03:00
if ! pr . CanAutoMerge ( ) || pr . HasMerged {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "MergePullRequest" , nil )
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
}
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
}
2018-01-05 21:56:50 +03:00
if err = pr . Merge ( ctx . User , ctx . Repo . GitRepo , models . MergeStyle ( form . Do ) , message ) ; err != nil {
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
}
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
}
2018-10-18 14:23:05 +03:00
notification . NotifyMergePullRequest ( pr , ctx . User , ctx . Repo . GitRepo )
2017-01-28 18:59:58 +03:00
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
// ParseCompareInfo parse compare info between two commit for preparing pull request
2016-03-11 19:56:52 +03:00
func ParseCompareInfo ( ctx * context . Context ) ( * models . User , * models . Repository , * git . Repository , * git . PullRequestInfo , string , string ) {
2016-03-05 00:14:02 +03:00
baseRepo := ctx . Repo . Repository
2016-03-04 23:43:01 +03:00
// Get compared branches information
// format: <base branch>...[<head repo>:]<head branch>
// base<-head: master...head:feature
// same repo: master...feature
2018-01-08 18:17:24 +03:00
var (
headUser * models . User
headBranch string
isSameRepo bool
infoPath string
err error
)
2019-03-18 17:00:23 +03:00
infoPath = ctx . Params ( "*" )
2018-01-08 18:17:24 +03:00
infos := strings . Split ( infoPath , "..." )
2015-08-08 17:43:14 +03:00
if len ( infos ) != 2 {
2016-03-05 00:14:02 +03:00
log . Trace ( "ParseCompareInfo[%d]: not enough compared branches information %s" , baseRepo . ID , infos )
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "CompareAndPullRequest" , nil )
2015-09-02 02:07:02 +03:00
return nil , nil , nil , nil , "" , ""
2015-08-08 17:43:14 +03:00
}
baseBranch := infos [ 0 ]
ctx . Data [ "BaseBranch" ] = baseBranch
2016-03-04 23:43:01 +03:00
// If there is no head repository, it means pull request between same repository.
2015-08-08 17:43:14 +03:00
headInfos := strings . Split ( infos [ 1 ] , ":" )
2016-03-04 23:43:01 +03:00
if len ( headInfos ) == 1 {
isSameRepo = true
headUser = ctx . Repo . Owner
headBranch = headInfos [ 0 ]
2015-08-08 17:43:14 +03:00
2016-03-04 23:43:01 +03:00
} else if len ( headInfos ) == 2 {
headUser , err = models . GetUserByName ( headInfos [ 0 ] )
if err != nil {
if models . IsErrUserNotExist ( err ) {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "GetUserByName" , nil )
2016-03-04 23:43:01 +03:00
} else {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetUserByName" , err )
2016-03-04 23:43:01 +03:00
}
return nil , nil , nil , nil , "" , ""
2015-09-02 02:07:02 +03:00
}
2016-03-04 23:43:01 +03:00
headBranch = headInfos [ 1 ]
2017-03-03 11:53:59 +03:00
isSameRepo = headUser . ID == ctx . Repo . Owner . ID
2016-03-04 23:43:01 +03:00
} else {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "CompareAndPullRequest" , nil )
2015-09-02 02:07:02 +03:00
return nil , nil , nil , nil , "" , ""
}
2015-12-20 09:06:54 +03:00
ctx . Data [ "HeadUser" ] = headUser
2016-03-04 23:43:01 +03:00
ctx . Data [ "HeadBranch" ] = headBranch
2016-03-07 07:57:46 +03:00
ctx . Repo . PullRequest . SameRepo = isSameRepo
2015-09-02 02:07:02 +03:00
// Check if base branch is valid.
if ! ctx . Repo . GitRepo . IsBranchExist ( baseBranch ) {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "IsBranchExist" , nil )
2015-09-02 02:07:02 +03:00
return nil , nil , nil , nil , "" , ""
}
2015-08-08 17:43:14 +03:00
2016-03-04 23:43:01 +03:00
// Check if current user has fork of repository or in the same repository.
2016-07-23 20:08:22 +03:00
headRepo , has := models . HasForkedRepo ( headUser . ID , baseRepo . ID )
2016-03-05 00:14:02 +03:00
if ! has && ! isSameRepo {
log . Trace ( "ParseCompareInfo[%d]: does not have fork or in same repository" , baseRepo . ID )
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "ParseCompareInfo" , nil )
2015-09-02 02:07:02 +03:00
return nil , nil , nil , nil , "" , ""
2015-08-08 17:43:14 +03:00
}
2016-03-04 23:43:01 +03:00
var headGitRepo * git . Repository
if isSameRepo {
headRepo = ctx . Repo . Repository
headGitRepo = ctx . Repo . GitRepo
2019-02-12 11:37:32 +03:00
ctx . Data [ "BaseName" ] = headUser . Name
2016-03-04 23:43:01 +03:00
} else {
headGitRepo , err = git . OpenRepository ( models . RepoPath ( headUser . Name , headRepo . Name ) )
2019-02-12 11:37:32 +03:00
ctx . Data [ "BaseName" ] = baseRepo . OwnerName
2016-03-04 23:43:01 +03:00
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "OpenRepository" , err )
2016-03-04 23:43:01 +03:00
return nil , nil , nil , nil , "" , ""
}
2015-09-02 02:07:02 +03:00
}
2019-05-07 20:20:23 +03:00
// user should have permission to read baseRepo's codes and pulls, NOT headRepo's
permBase , err := models . GetUserRepoPermission ( baseRepo , ctx . User )
2018-11-28 14:26:14 +03:00
if err != nil {
ctx . ServerError ( "GetUserRepoPermission" , err )
return nil , nil , nil , nil , "" , ""
}
2019-05-07 20:20:23 +03:00
if ! permBase . CanReadIssuesOrPulls ( true ) || ! permBase . CanRead ( models . UnitTypeCode ) {
2019-04-22 23:40:51 +03:00
if log . IsTrace ( ) {
2019-05-07 20:20:23 +03:00
log . Trace ( "Permission Denied: User: %-v cannot create/read pull requests or cannot read code in Repo: %-v\nUser in baseRepo has Permissions: %-+v" ,
ctx . User ,
baseRepo ,
permBase )
}
ctx . NotFound ( "ParseCompareInfo" , nil )
return nil , nil , nil , nil , "" , ""
}
// user should have permission to read headrepo's codes
permHead , err := models . GetUserRepoPermission ( headRepo , ctx . User )
if err != nil {
ctx . ServerError ( "GetUserRepoPermission" , err )
return nil , nil , nil , nil , "" , ""
}
if ! permHead . CanRead ( models . UnitTypeCode ) {
if log . IsTrace ( ) {
log . Trace ( "Permission Denied: User: %-v cannot read code requests in Repo: %-v\nUser in headRepo has Permissions: %-+v" ,
2019-04-22 23:40:51 +03:00
ctx . User ,
headRepo ,
2019-05-07 20:20:23 +03:00
permHead )
2019-04-22 23:40:51 +03:00
}
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "ParseCompareInfo" , nil )
2016-03-05 00:14:02 +03:00
return nil , nil , nil , nil , "" , ""
}
2015-09-02 02:07:02 +03:00
// Check if head branch is valid.
if ! headGitRepo . IsBranchExist ( headBranch ) {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "IsBranchExist" , nil )
2015-09-02 02:07:02 +03:00
return nil , nil , nil , nil , "" , ""
2015-08-08 17:43:14 +03:00
}
2015-09-02 02:07:02 +03:00
2015-08-08 17:43:14 +03:00
headBranches , err := headGitRepo . GetBranches ( )
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetBranches" , err )
2015-09-02 02:07:02 +03:00
return nil , nil , nil , nil , "" , ""
2015-08-08 17:43:14 +03:00
}
ctx . Data [ "HeadBranches" ] = headBranches
2016-03-05 00:14:02 +03:00
prInfo , err := headGitRepo . GetPullRequestInfo ( models . RepoPath ( baseRepo . Owner . Name , baseRepo . Name ) , baseBranch , headBranch )
2015-09-02 02:07:02 +03:00
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetPullRequestInfo" , err )
2015-09-02 02:07:02 +03:00
return nil , nil , nil , nil , "" , ""
}
ctx . Data [ "BeforeCommitID" ] = prInfo . MergeBase
return headUser , headRepo , headGitRepo , prInfo , baseBranch , headBranch
}
2016-11-24 10:04:31 +03:00
// PrepareCompareDiff render pull request preview diff page
2015-09-02 02:07:02 +03:00
func PrepareCompareDiff (
2016-03-11 19:56:52 +03:00
ctx * context . Context ,
2015-09-02 02:07:02 +03:00
headUser * models . User ,
headRepo * models . Repository ,
headGitRepo * git . Repository ,
prInfo * git . PullRequestInfo ,
2015-09-02 12:09:12 +03:00
baseBranch , headBranch string ) bool {
2015-09-02 02:07:02 +03:00
var (
2019-01-17 00:50:11 +03:00
repo = ctx . Repo . Repository
err error
title string
2015-09-02 02:07:02 +03:00
)
// Get diff information.
2016-07-15 16:53:43 +03:00
ctx . Data [ "CommitRepoLink" ] = headRepo . Link ( )
2015-09-02 02:07:02 +03:00
2015-12-10 04:46:05 +03:00
headCommitID , err := headGitRepo . GetBranchCommitID ( headBranch )
2015-09-02 02:07:02 +03:00
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetBranchCommitID" , err )
2015-09-02 12:09:12 +03:00
return false
2015-09-02 02:07:02 +03:00
}
ctx . Data [ "AfterCommitID" ] = headCommitID
2015-09-02 12:09:12 +03:00
if headCommitID == prInfo . MergeBase {
ctx . Data [ "IsNothingToCompare" ] = true
return true
}
2015-09-02 02:07:02 +03:00
diff , err := models . GetDiffRange ( models . RepoPath ( headUser . Name , headRepo . Name ) ,
2016-06-29 18:11:00 +03:00
prInfo . MergeBase , headCommitID , setting . Git . MaxGitDiffLines ,
setting . Git . MaxGitDiffLineCharacters , setting . Git . MaxGitDiffFiles )
2015-09-02 02:07:02 +03:00
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetDiffRange" , err )
2015-09-02 12:09:12 +03:00
return false
2015-09-02 02:07:02 +03:00
}
ctx . Data [ "Diff" ] = diff
ctx . Data [ "DiffNotAvailable" ] = diff . NumFiles ( ) == 0
headCommit , err := headGitRepo . GetCommit ( headCommitID )
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetCommit" , err )
2015-09-02 12:09:12 +03:00
return false
2015-09-02 02:07:02 +03:00
}
prInfo . Commits = models . ValidateCommitsWithEmails ( prInfo . Commits )
2017-05-07 17:40:31 +03:00
prInfo . Commits = models . ParseCommitsWithSignature ( prInfo . Commits )
prInfo . Commits = models . ParseCommitsWithStatus ( prInfo . Commits , headRepo )
2015-09-02 02:07:02 +03:00
ctx . Data [ "Commits" ] = prInfo . Commits
ctx . Data [ "CommitCount" ] = prInfo . Commits . Len ( )
2019-01-17 00:50:11 +03:00
if prInfo . Commits . Len ( ) == 1 {
c := prInfo . Commits . Front ( ) . Value . ( models . SignCommitWithStatuses )
title = strings . TrimSpace ( c . UserCommit . Summary ( ) )
body := strings . Split ( strings . TrimSpace ( c . UserCommit . Message ( ) ) , "\n" )
if len ( body ) > 1 {
ctx . Data [ "content" ] = strings . Join ( body [ 1 : ] , "\n" )
}
} else {
title = headBranch
}
ctx . Data [ "title" ] = title
2015-09-02 02:07:02 +03:00
ctx . Data [ "Username" ] = headUser . Name
ctx . Data [ "Reponame" ] = headRepo . Name
2015-09-02 11:08:05 +03:00
ctx . Data [ "IsImageFile" ] = headCommit . IsImageFile
headTarget := path . Join ( headUser . Name , repo . Name )
2017-11-27 03:58:54 +03:00
ctx . Data [ "SourcePath" ] = setting . AppSubURL + "/" + path . Join ( headTarget , "src" , "commit" , headCommitID )
ctx . Data [ "BeforeSourcePath" ] = setting . AppSubURL + "/" + path . Join ( headTarget , "src" , "commit" , prInfo . MergeBase )
ctx . Data [ "RawPath" ] = setting . AppSubURL + "/" + path . Join ( headTarget , "raw" , "commit" , headCommitID )
2015-09-02 12:09:12 +03:00
return false
2015-09-02 02:07:02 +03:00
}
2016-11-24 10:04:31 +03:00
// CompareAndPullRequest render pull request preview page
2016-03-11 19:56:52 +03:00
func CompareAndPullRequest ( ctx * context . Context ) {
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-01-31 19:19:02 +03:00
ctx . Data [ "RequireHighlightJS" ] = true
2017-12-11 09:03:04 +03:00
ctx . Data [ "RequireTribute" ] = true
2018-08-13 22:04:39 +03:00
ctx . Data [ "PullRequestWorkInProgressPrefixes" ] = setting . Repository . PullRequest . WorkInProgressPrefixes
2016-11-24 10:04:31 +03:00
setTemplateIfExists ( ctx , pullRequestTemplateKey , pullRequestTemplateCandidates )
2015-09-02 02:07:02 +03:00
renderAttachmentSettings ( ctx )
headUser , headRepo , headGitRepo , prInfo , baseBranch , headBranch := ParseCompareInfo ( ctx )
if ctx . Written ( ) {
return
}
2015-10-19 02:30:39 +03:00
pr , err := models . GetUnmergedPullRequest ( headRepo . ID , ctx . Repo . Repository . ID , headBranch , baseBranch )
if err != nil {
if ! models . IsErrPullRequestNotExist ( err ) {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetUnmergedPullRequest" , err )
2015-10-19 02:30:39 +03:00
return
}
} else {
ctx . Data [ "HasPullRequest" ] = true
ctx . Data [ "PullRequest" ] = pr
2016-11-24 10:04:31 +03:00
ctx . HTML ( 200 , tplComparePull )
2015-10-19 02:30:39 +03:00
return
}
2015-09-02 02:07:02 +03:00
2015-09-02 12:09:12 +03:00
nothingToCompare := PrepareCompareDiff ( ctx , headUser , headRepo , headGitRepo , prInfo , baseBranch , headBranch )
2015-08-31 10:24:28 +03:00
if ctx . Written ( ) {
return
}
2015-09-02 12:09:12 +03:00
if ! nothingToCompare {
// Setup information for new form.
RetrieveRepoMetas ( ctx , ctx . Repo . Repository )
if ctx . Written ( ) {
return
}
}
2016-11-24 10:04:31 +03:00
ctx . HTML ( 200 , tplComparePull )
2015-08-08 17:43:14 +03:00
}
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
}
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
}
2016-11-24 10:04:31 +03:00
ctx . HTML ( 200 , tplComparePull )
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
}
ctx . RenderWithErr ( ctx . Tr ( "repo.issues.new.title_empty" ) , tplComparePull , form )
return
}
2016-08-16 00:04:44 +03:00
patch , err := headGitRepo . GetPatch ( prInfo . MergeBase , headBranch )
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetPatch" , err )
2016-08-16 00:04:44 +03:00
return
}
2019-05-18 05:37:49 +03:00
maxIndex , err := models . GetMaxIndexOfIssue ( repo . ID )
if err != nil {
ctx . ServerError ( "GetPatch" , err )
return
}
2016-02-24 15:56:54 +03:00
pullIssue := & models . Issue {
2015-09-02 02:07:02 +03:00
RepoID : repo . ID ,
2019-05-18 05:37:49 +03:00
Index : maxIndex + 1 ,
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 {
2015-09-02 02:26:39 +03:00
HeadRepoID : headRepo . ID ,
BaseRepoID : repo . ID ,
HeadUserName : headUser . Name ,
2015-10-19 02:30:39 +03:00
HeadBranch : headBranch ,
2015-09-02 02:26:39 +03:00
BaseBranch : baseBranch ,
2016-02-24 15:56:54 +03:00
HeadRepo : headRepo ,
BaseRepo : repo ,
2015-09-02 02:26:39 +03:00
MergeBase : prInfo . MergeBase ,
2016-11-07 18:37:32 +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
if err := models . NewPullRequest ( repo , pullIssue , labelIDs , attachments , pullRequest , patch , assigneeIDs ) ; err != nil {
if models . IsErrUserDoesNotHaveAccessToRepo ( err ) {
ctx . Error ( 400 , "UserDoesNotHaveAccessToRepo" , err . Error ( ) )
return
}
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "NewPullRequest" , err )
2015-09-02 02:07:02 +03:00
return
2016-03-04 23:43:01 +03:00
} else if err := pullRequest . PushToBaseRepo ( ) ; err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "PushToBaseRepo" , err )
2016-02-24 15:56:54 +03:00
return
2015-12-10 19:18:56 +03:00
}
2018-10-18 14:23:05 +03:00
notification . NotifyNewPullRequest ( pullRequest )
2017-01-28 18:59:58 +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
}
2015-12-15 01:06:54 +03:00
if secret != base . EncodeMD5 ( owner . Salt ) {
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
go models . HookQueue . Add ( repo . ID )
2016-08-14 13:32:24 +03:00
go models . 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
}
2018-12-13 18:55:43 +03:00
if err := pr . GetHeadRepo ( ) ; err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetHeadRepo" , 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
} else if pr . GetBaseRepo ( ) ; err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetBaseRepo" , err )
2017-06-21 04:00:03 +03:00
return
} else if 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
}
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
}
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
}
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 ) {
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-05 13:56:52 +03:00
} else {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetIssueByIndex" , err )
2018-01-05 13:56:52 +03:00
}
return
}
2018-01-07 16:10:20 +03:00
// Return not found if it's not a pull request
2018-01-05 13:56:52 +03:00
if ! issue . IsPull {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "DownloadPullDiff" ,
2018-01-05 13:56:52 +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-05 13:56:52 +03:00
2018-12-13 18:55:43 +03:00
pr := issue . PullRequest
2018-01-05 13:56:52 +03:00
if err = pr . GetBaseRepo ( ) ; err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetBaseRepo" , err )
2018-01-05 13:56:52 +03:00
return
}
patch , err := pr . BaseRepo . PatchPath ( pr . Index )
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "PatchPath" , err )
2018-01-05 13:56:52 +03:00
return
}
ctx . ServeFileContent ( patch )
}
2018-01-07 16:10:20 +03:00
// DownloadPullPatch render a pull's raw patch
func DownloadPullPatch ( ctx * context . Context ) {
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
if err = pr . GetHeadRepo ( ) ; err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetHeadRepo" , err )
2018-01-07 16:10:20 +03:00
return
}
headGitRepo , err := git . OpenRepository ( pr . HeadRepo . RepoPath ( ) )
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "OpenRepository" , err )
2018-01-07 16:10:20 +03:00
return
}
patch , err := headGitRepo . GetFormatPatch ( pr . MergeBase , pr . HeadBranch )
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetFormatPatch" , err )
2018-01-07 16:10:20 +03:00
return
}
_ , err = io . Copy ( ctx , patch )
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "io.Copy" , err )
2018-01-07 16:10:20 +03:00
return
}
}