@ -71,25 +71,45 @@ func ParseCompareInfo(ctx *context.Context) (*models.User, *models.Repository, *
baseRepo := ctx . Repo . Repository
baseRepo := ctx . Repo . Repository
// Get compared branches information
// Get compared branches information
// A full compare url is of the form:
//
// 1. /{:baseOwner}/{:baseRepoName}/compare/{:baseBranch}...{:headBranch}
// 2. /{:baseOwner}/{:baseRepoName}/compare/{:baseBranch}...{:headOwner}:{:headBranch}
// 3. /{:baseOwner}/{:baseRepoName}/compare/{:baseBranch}...{:headOwner}/{:headRepoName}:{:headBranch}
//
// Here we obtain the infoPath "{:baseBranch}...[{:headOwner}/{:headRepoName}:]{:headBranch}" as ctx.Params("*")
// with the :baseRepo in ctx.Repo.
//
// Note: Generally :headRepoName is not provided here - we are only passed :headOwner.
//
// How do we determine the :headRepo?
//
// 1. If :headOwner is not set then the :headRepo = :baseRepo
// 2. If :headOwner is set - then look for the fork of :baseRepo owned by :headOwner
// 3. But... :baseRepo could be a fork of :headOwner's repo - so check that
// 4. Now, :baseRepo and :headRepos could be forks of the same repo - so check that
//
// format: <base branch>...[<head repo>:]<head branch>
// format: <base branch>...[<head repo>:]<head branch>
// base<-head: master...head:feature
// base<-head: master...head:feature
// same repo: master...feature
// same repo: master...feature
var (
var (
headUser * models . User
headUser * models . User
headRepo * models . Repository
headBranch string
headBranch string
isSameRepo bool
isSameRepo bool
infoPath string
infoPath string
err error
err error
)
)
infoPath = ctx . Params ( "*" )
infoPath = ctx . Params ( "*" )
infos := strings . Split ( infoPath , "..." )
infos := strings . SplitN ( infoPath , "..." , 2 )
if len ( infos ) != 2 {
if len ( infos ) != 2 {
log . Trace ( "ParseCompareInfo[%d]: not enough compared branches information %s" , baseRepo . ID , infos )
log . Trace ( "ParseCompareInfo[%d]: not enough compared branches information %s" , baseRepo . ID , infos )
ctx . NotFound ( "CompareAndPullRequest" , nil )
ctx . NotFound ( "CompareAndPullRequest" , nil )
return nil , nil , nil , nil , "" , ""
return nil , nil , nil , nil , "" , ""
}
}
ctx . Data [ "BaseName" ] = baseRepo . OwnerName
baseBranch := infos [ 0 ]
baseBranch := infos [ 0 ]
ctx . Data [ "BaseBranch" ] = baseBranch
ctx . Data [ "BaseBranch" ] = baseBranch
@ -101,17 +121,44 @@ func ParseCompareInfo(ctx *context.Context) (*models.User, *models.Repository, *
headBranch = headInfos [ 0 ]
headBranch = headInfos [ 0 ]
} else if len ( headInfos ) == 2 {
} else if len ( headInfos ) == 2 {
headUser , err = models . GetUserByName ( headInfos [ 0 ] )
headInfosSplit := strings . Split ( headInfos [ 0 ] , "/" )
if err != nil {
if len ( headInfosSplit ) == 1 {
if models . IsErrUserNotExist ( err ) {
headUser , err = models . GetUserByName ( headInfos [ 0 ] )
ctx . NotFound ( "GetUserByName" , nil )
if err != nil {
} else {
if models . IsErrUserNotExist ( err ) {
ctx . ServerError ( "GetUserByName" , err )
ctx . NotFound ( "GetUserByName" , nil )
} else {
ctx . ServerError ( "GetUserByName" , err )
}
return nil , nil , nil , nil , "" , ""
}
}
return nil , nil , nil , nil , "" , ""
headBranch = headInfos [ 1 ]
isSameRepo = headUser . ID == ctx . Repo . Owner . ID
if isSameRepo {
headRepo = baseRepo
}
} else {
headRepo , err = models . GetRepositoryByOwnerAndName ( headInfosSplit [ 0 ] , headInfosSplit [ 1 ] )
if err != nil {
if models . IsErrRepoNotExist ( err ) {
ctx . NotFound ( "GetRepositoryByOwnerAndName" , nil )
} else {
ctx . ServerError ( "GetRepositoryByOwnerAndName" , err )
}
return nil , nil , nil , nil , "" , ""
}
if err := headRepo . GetOwner ( ) ; err != nil {
if models . IsErrUserNotExist ( err ) {
ctx . NotFound ( "GetUserByName" , nil )
} else {
ctx . ServerError ( "GetUserByName" , err )
}
return nil , nil , nil , nil , "" , ""
}
headBranch = headInfos [ 1 ]
headUser = headRepo . Owner
isSameRepo = headRepo . ID == ctx . Repo . Repository . ID
}
}
headBranch = headInfos [ 1 ]
isSameRepo = headUser . ID == ctx . Repo . Owner . ID
} else {
} else {
ctx . NotFound ( "CompareAndPullRequest" , nil )
ctx . NotFound ( "CompareAndPullRequest" , nil )
return nil , nil , nil , nil , "" , ""
return nil , nil , nil , nil , "" , ""
@ -139,20 +186,80 @@ func ParseCompareInfo(ctx *context.Context) (*models.User, *models.Repository, *
ctx . Data [ "BaseIsBranch" ] = baseIsBranch
ctx . Data [ "BaseIsBranch" ] = baseIsBranch
ctx . Data [ "BaseIsTag" ] = baseIsTag
ctx . Data [ "BaseIsTag" ] = baseIsTag
// Check if current user has fork of repository or in the same repository.
// Now we have the repository that represents the base
headRepo , has := models . HasForkedRepo ( headUser . ID , baseRepo . ID )
if ! has && ! isSameRepo {
// The current base and head repositories and branches may not
// actually be the intended branches that the user wants to
// create a pull-request from - but also determining the head
// repo is difficult.
// We will want therefore to offer a few repositories to set as
// our base and head
// 1. First if the baseRepo is a fork get the "RootRepo" it was
// forked from
var rootRepo * models . Repository
if baseRepo . IsFork {
err = baseRepo . GetBaseRepo ( )
if err != nil {
if ! models . IsErrRepoNotExist ( err ) {
ctx . ServerError ( "Unable to find root repo" , err )
return nil , nil , nil , nil , "" , ""
}
} else {
rootRepo = baseRepo . BaseRepo
}
}
// 2. Now if the current user is not the owner of the baseRepo,
// check if they have a fork of the base repo and offer that as
// "OwnForkRepo"
var ownForkRepo * models . Repository
if baseRepo . OwnerID != ctx . User . ID {
repo , has := models . HasForkedRepo ( ctx . User . ID , baseRepo . ID )
if has {
ownForkRepo = repo
ctx . Data [ "OwnForkRepo" ] = ownForkRepo
}
}
has := headRepo != nil
// 3. If the base is a forked from "RootRepo" and the owner of
// the "RootRepo" is the :headUser - set headRepo to that
if ! has && rootRepo != nil && rootRepo . OwnerID == headUser . ID {
headRepo = rootRepo
has = true
}
// 4. If the ctx.User has their own fork of the baseRepo and the headUser is the ctx.User
// set the headRepo to the ownFork
if ! has && ownForkRepo != nil && ownForkRepo . OwnerID == headUser . ID {
headRepo = ownForkRepo
has = true
}
// 5. If the headOwner has a fork of the baseRepo - use that
if ! has {
headRepo , has = models . HasForkedRepo ( headUser . ID , baseRepo . ID )
}
// 6. If the baseRepo is a fork and the headUser has a fork of that use that
if ! has && baseRepo . IsFork {
headRepo , has = models . HasForkedRepo ( headUser . ID , baseRepo . ForkID )
}
// 7. Otherwise if we're not the same repo and haven't found a repo give up
if ! isSameRepo && ! has {
ctx . Data [ "PageIsComparePull" ] = false
ctx . Data [ "PageIsComparePull" ] = false
}
}
// 8. Finally open the git repo
var headGitRepo * git . Repository
var headGitRepo * git . Repository
if isSameRepo {
if isSameRepo {
headRepo = ctx . Repo . Repository
headRepo = ctx . Repo . Repository
headGitRepo = ctx . Repo . GitRepo
headGitRepo = ctx . Repo . GitRepo
ctx . Data [ "BaseName" ] = headUser . Name
} else if has {
} else {
headGitRepo , err = git . OpenRepository ( headRepo . RepoPath ( ) )
headGitRepo , err = git . OpenRepository ( models . RepoPath ( headUser . Name , headRepo . Name ) )
ctx . Data [ "BaseName" ] = baseRepo . OwnerName
if err != nil {
if err != nil {
ctx . ServerError ( "OpenRepository" , err )
ctx . ServerError ( "OpenRepository" , err )
return nil , nil , nil , nil , "" , ""
return nil , nil , nil , nil , "" , ""
@ -160,7 +267,11 @@ func ParseCompareInfo(ctx *context.Context) (*models.User, *models.Repository, *
defer headGitRepo . Close ( )
defer headGitRepo . Close ( )
}
}
// user should have permission to read baseRepo's codes and pulls, NOT headRepo's
ctx . Data [ "HeadRepo" ] = headRepo
// Now we need to assert that the ctx.User has permission to read
// the baseRepo's code and pulls
// (NOT headRepo's)
permBase , err := models . GetUserRepoPermission ( baseRepo , ctx . User )
permBase , err := models . GetUserRepoPermission ( baseRepo , ctx . User )
if err != nil {
if err != nil {
ctx . ServerError ( "GetUserRepoPermission" , err )
ctx . ServerError ( "GetUserRepoPermission" , err )
@ -177,8 +288,9 @@ func ParseCompareInfo(ctx *context.Context) (*models.User, *models.Repository, *
return nil , nil , nil , nil , "" , ""
return nil , nil , nil , nil , "" , ""
}
}
// If we're not merging from the same repo:
if ! isSameRepo {
if ! isSameRepo {
// user should have permission to read headr epo's codes
// Assert ctx.User has permission to read headR epo's codes
permHead , err := models . GetUserRepoPermission ( headRepo , ctx . User )
permHead , err := models . GetUserRepoPermission ( headRepo , ctx . User )
if err != nil {
if err != nil {
ctx . ServerError ( "GetUserRepoPermission" , err )
ctx . ServerError ( "GetUserRepoPermission" , err )
@ -196,6 +308,44 @@ func ParseCompareInfo(ctx *context.Context) (*models.User, *models.Repository, *
}
}
}
}
// If we have a rootRepo and it's different from:
// 1. the computed base
// 2. the computed head
// then get the branches of it
if rootRepo != nil &&
rootRepo . ID != headRepo . ID &&
rootRepo . ID != baseRepo . ID {
perm , branches , err := getBranchesForRepo ( ctx . User , rootRepo )
if err != nil {
ctx . ServerError ( "GetBranchesForRepo" , err )
return nil , nil , nil , nil , "" , ""
}
if perm {
ctx . Data [ "RootRepo" ] = rootRepo
ctx . Data [ "RootRepoBranches" ] = branches
}
}
// If we have a ownForkRepo and it's different from:
// 1. The computed base
// 2. The computed hea
// 3. The rootRepo (if we have one)
// then get the branches from it.
if ownForkRepo != nil &&
ownForkRepo . ID != headRepo . ID &&
ownForkRepo . ID != baseRepo . ID &&
( rootRepo == nil || ownForkRepo . ID != rootRepo . ID ) {
perm , branches , err := getBranchesForRepo ( ctx . User , ownForkRepo )
if err != nil {
ctx . ServerError ( "GetBranchesForRepo" , err )
return nil , nil , nil , nil , "" , ""
}
if perm {
ctx . Data [ "OwnForkRepo" ] = ownForkRepo
ctx . Data [ "OwnForkRepoBranches" ] = branches
}
}
// Check if head branch is valid.
// Check if head branch is valid.
headIsCommit := headGitRepo . IsCommitExist ( headBranch )
headIsCommit := headGitRepo . IsCommitExist ( headBranch )
headIsBranch := headGitRepo . IsBranchExist ( headBranch )
headIsBranch := headGitRepo . IsBranchExist ( headBranch )
@ -343,28 +493,25 @@ func PrepareCompareDiff(
return false
return false
}
}
// parseBaseRepoInfo parse base repository if current repo is forked.
func getBranchesForRepo ( user * models . User , repo * models . Repository ) ( bool , [ ] string , error ) {
// The "base" here means the repository where current repo forks from,
perm , err := models . GetUserRepoPermission ( repo , user )
// not the repository fetch from current URL.
if err != nil {
func parseBaseRepoInfo ( ctx * context . Context , repo * models . Repository ) error {
return false , nil , err
if ! repo . IsFork {
return nil
}
}
if err := repo . GetBaseRepo ( ) ; err != nil {
if ! perm . CanRead ( models . UnitTypeCode ) {
return err
return false , nil , nil
}
}
gitRepo , err := git . OpenRepository ( repo . RepoPath ( ) )
baseGitRepo , err := git . OpenRepository ( repo . BaseRepo . RepoPath ( ) )
if err != nil {
if err != nil {
return err
return false , nil , err
}
}
defer baseG itRepo. Close ( )
defer g itRepo. Close ( )
ctx . Data [ "BaseRepoBranches" ] , err = baseG itRepo. GetBranches ( )
branches , err := g itRepo. GetBranches ( )
if err != nil {
if err != nil {
return err
return false , nil , err
}
}
return nil
return true , branches , nil
}
}
// CompareDiff show different from one commit to another commit
// CompareDiff show different from one commit to another commit
@ -375,12 +522,6 @@ func CompareDiff(ctx *context.Context) {
}
}
defer headGitRepo . Close ( )
defer headGitRepo . Close ( )
var err error
if err = parseBaseRepoInfo ( ctx , headRepo ) ; err != nil {
ctx . ServerError ( "parseBaseRepoInfo" , err )
return
}
nothingToCompare := PrepareCompareDiff ( ctx , headUser , headRepo , headGitRepo , compareInfo , baseBranch , headBranch )
nothingToCompare := PrepareCompareDiff ( ctx , headUser , headRepo , headGitRepo , compareInfo , baseBranch , headBranch )
if ctx . Written ( ) {
if ctx . Written ( ) {
return
return