@@ -389,8 +389,7 @@ func CreatePullRequest(ctx *context.APIContext) {
389389
390390 form := * web .GetForm (ctx ).(* api.CreatePullRequestOption )
391391 if form .Head == form .Base {
392- ctx .Error (http .StatusUnprocessableEntity , "BaseHeadSame" ,
393- "Invalid PullRequest: There are no changes between the head and the base" )
392+ ctx .Error (http .StatusUnprocessableEntity , "BaseHeadSame" , "Invalid PullRequest: There are no changes between the head and the base" )
394393 return
395394 }
396395
@@ -401,14 +400,22 @@ func CreatePullRequest(ctx *context.APIContext) {
401400 )
402401
403402 // Get repo/branch information
404- headRepo , headGitRepo , compareInfo , baseBranch , headBranch := parseCompareInfo (ctx , form )
403+ compareResult , closer := parseCompareInfo (ctx , form )
405404 if ctx .Written () {
406405 return
407406 }
408- defer headGitRepo .Close ()
407+ defer closer ()
408+
409+ if ! compareResult .baseRef .IsBranch () || ! compareResult .headRef .IsBranch () {
410+ ctx .Error (http .StatusUnprocessableEntity , "BaseHeadInvalidRefType" , "Invalid PullRequest: base and head must be branches" )
411+ return
412+ }
409413
410414 // Check if another PR exists with the same targets
411- existingPr , err := issues_model .GetUnmergedPullRequest (ctx , headRepo .ID , ctx .Repo .Repository .ID , headBranch , baseBranch , issues_model .PullRequestFlowGithub )
415+ existingPr , err := issues_model .GetUnmergedPullRequest (ctx , compareResult .headRepo .ID , ctx .Repo .Repository .ID ,
416+ compareResult .headRef .ShortName (), compareResult .baseRef .ShortName (),
417+ issues_model .PullRequestFlowGithub ,
418+ )
412419 if err != nil {
413420 if ! issues_model .IsErrPullRequestNotExist (err ) {
414421 ctx .Error (http .StatusInternalServerError , "GetUnmergedPullRequest" , err )
@@ -484,13 +491,13 @@ func CreatePullRequest(ctx *context.APIContext) {
484491 DeadlineUnix : deadlineUnix ,
485492 }
486493 pr := & issues_model.PullRequest {
487- HeadRepoID : headRepo .ID ,
494+ HeadRepoID : compareResult . headRepo .ID ,
488495 BaseRepoID : repo .ID ,
489- HeadBranch : headBranch ,
490- BaseBranch : baseBranch ,
491- HeadRepo : headRepo ,
496+ HeadBranch : compareResult . headRef . ShortName () ,
497+ BaseBranch : compareResult . baseRef . ShortName () ,
498+ HeadRepo : compareResult . headRepo ,
492499 BaseRepo : repo ,
493- MergeBase : compareInfo .MergeBase ,
500+ MergeBase : compareResult . compareInfo .MergeBase ,
494501 Type : issues_model .PullRequestGitea ,
495502 }
496503
@@ -1080,71 +1087,62 @@ func MergePullRequest(ctx *context.APIContext) {
10801087 ctx .Status (http .StatusOK )
10811088}
10821089
1083- func parseCompareInfo (ctx * context.APIContext , form api.CreatePullRequestOption ) (* repo_model.Repository , * git.Repository , * git.CompareInfo , string , string ) {
1084- baseRepo := ctx .Repo .Repository
1090+ type parseCompareInfoResult struct {
1091+ headRepo * repo_model.Repository
1092+ headGitRepo * git.Repository
1093+ compareInfo * git.CompareInfo
1094+ baseRef git.RefName
1095+ headRef git.RefName
1096+ }
10851097
1098+ // parseCompareInfo returns non-nil if it succeeds, it always writes to the context and returns nil if it fails
1099+ func parseCompareInfo (ctx * context.APIContext , form api.CreatePullRequestOption ) (result * parseCompareInfoResult , closer func ()) {
1100+ var err error
10861101 // Get compared branches information
10871102 // format: <base branch>...[<head repo>:]<head branch>
10881103 // base<-head: master...head:feature
10891104 // same repo: master...feature
1105+ baseRepo := ctx .Repo .Repository
1106+ baseRefToGuess := form .Base
10901107
1091- // TODO: Validate form first?
1092-
1093- baseBranch := form .Base
1094-
1095- var (
1096- headUser * user_model.User
1097- headBranch string
1098- isSameRepo bool
1099- err error
1100- )
1101-
1102- // If there is no head repository, it means pull request between same repository.
1103- headInfos := strings .Split (form .Head , ":" )
1104- if len (headInfos ) == 1 {
1105- isSameRepo = true
1106- headUser = ctx .Repo .Owner
1107- headBranch = headInfos [0 ]
1108+ headUser := ctx .Repo .Owner
1109+ headRefToGuess := form .Head
1110+ if headInfos := strings .Split (form .Head , ":" ); len (headInfos ) == 1 {
1111+ // If there is no head repository, it means pull request between same repository.
1112+ // Do nothing here because the head variables have been assigned above.
11081113 } else if len (headInfos ) == 2 {
1114+ // There is a head repository (the head repository could also be the same base repo)
1115+ headRefToGuess = headInfos [1 ]
11091116 headUser , err = user_model .GetUserByName (ctx , headInfos [0 ])
11101117 if err != nil {
11111118 if user_model .IsErrUserNotExist (err ) {
11121119 ctx .NotFound ("GetUserByName" )
11131120 } else {
11141121 ctx .Error (http .StatusInternalServerError , "GetUserByName" , err )
11151122 }
1116- return nil , nil , nil , "" , ""
1123+ return nil , nil
11171124 }
1118- headBranch = headInfos [1 ]
1119- // The head repository can also point to the same repo
1120- isSameRepo = ctx .Repo .Owner .ID == headUser .ID
11211125 } else {
11221126 ctx .NotFound ()
1123- return nil , nil , nil , "" , ""
1127+ return nil , nil
11241128 }
11251129
1126- ctx .Repo .PullRequest .SameRepo = isSameRepo
1127- log .Trace ("Repo path: %q, base branch: %q, head branch: %q" , ctx .Repo .GitRepo .Path , baseBranch , headBranch )
1128- // Check if base branch is valid.
1129- if ! ctx .Repo .GitRepo .IsBranchExist (baseBranch ) && ! ctx .Repo .GitRepo .IsTagExist (baseBranch ) {
1130- ctx .NotFound ("BaseNotExist" )
1131- return nil , nil , nil , "" , ""
1132- }
1130+ isSameRepo := ctx .Repo .Owner .ID == headUser .ID
11331131
11341132 // Check if current user has fork of repository or in the same repository.
11351133 headRepo := repo_model .GetForkedRepo (ctx , headUser .ID , baseRepo .ID )
11361134 if headRepo == nil && ! isSameRepo {
1137- err : = baseRepo .GetBaseRepo (ctx )
1135+ err = baseRepo .GetBaseRepo (ctx )
11381136 if err != nil {
11391137 ctx .Error (http .StatusInternalServerError , "GetBaseRepo" , err )
1140- return nil , nil , nil , "" , ""
1138+ return nil , nil
11411139 }
11421140
11431141 // Check if baseRepo's base repository is the same as headUser's repository.
11441142 if baseRepo .BaseRepo == nil || baseRepo .BaseRepo .OwnerID != headUser .ID {
11451143 log .Trace ("parseCompareInfo[%d]: does not have fork or in same repository" , baseRepo .ID )
11461144 ctx .NotFound ("GetBaseRepo" )
1147- return nil , nil , nil , "" , ""
1145+ return nil , nil
11481146 }
11491147 // Assign headRepo so it can be used below.
11501148 headRepo = baseRepo .BaseRepo
@@ -1154,67 +1152,68 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
11541152 if isSameRepo {
11551153 headRepo = ctx .Repo .Repository
11561154 headGitRepo = ctx .Repo .GitRepo
1155+ closer = func () {} // no need to close the head repo because it shares the base repo
11571156 } else {
11581157 headGitRepo , err = gitrepo .OpenRepository (ctx , headRepo )
11591158 if err != nil {
11601159 ctx .Error (http .StatusInternalServerError , "OpenRepository" , err )
1161- return nil , nil , nil , "" , ""
1160+ return nil , nil
11621161 }
1162+ closer = func () { _ = headGitRepo .Close () }
11631163 }
1164+ defer func () {
1165+ if result == nil && ! isSameRepo {
1166+ _ = headGitRepo .Close ()
1167+ }
1168+ }()
11641169
11651170 // user should have permission to read baseRepo's codes and pulls, NOT headRepo's
11661171 permBase , err := access_model .GetUserRepoPermission (ctx , baseRepo , ctx .Doer )
11671172 if err != nil {
1168- headGitRepo .Close ()
11691173 ctx .Error (http .StatusInternalServerError , "GetUserRepoPermission" , err )
1170- return nil , nil , nil , "" , ""
1174+ return nil , nil
11711175 }
1176+
11721177 if ! permBase .CanReadIssuesOrPulls (true ) || ! permBase .CanRead (unit .TypeCode ) {
1173- if log .IsTrace () {
1174- log .Trace ("Permission Denied: User %-v cannot create/read pull requests or cannot read code in Repo %-v\n User in baseRepo has Permissions: %-+v" ,
1175- ctx .Doer ,
1176- baseRepo ,
1177- permBase )
1178- }
1179- headGitRepo .Close ()
1178+ log .Trace ("Permission Denied: User %-v cannot create/read pull requests or cannot read code in Repo %-v\n User in baseRepo has Permissions: %-+v" , ctx .Doer , baseRepo , permBase )
11801179 ctx .NotFound ("Can't read pulls or can't read UnitTypeCode" )
1181- return nil , nil , nil , "" , ""
1180+ return nil , nil
11821181 }
11831182
1184- // user should have permission to read headrepo's codes
1183+ // user should have permission to read headRepo's codes
1184+ // TODO: could the logic be simplified if the headRepo is the same as the baseRepo? Need to think more about it.
11851185 permHead , err := access_model .GetUserRepoPermission (ctx , headRepo , ctx .Doer )
11861186 if err != nil {
1187- headGitRepo .Close ()
11881187 ctx .Error (http .StatusInternalServerError , "GetUserRepoPermission" , err )
1189- return nil , nil , nil , "" , ""
1188+ return nil , nil
11901189 }
11911190 if ! permHead .CanRead (unit .TypeCode ) {
1192- if log .IsTrace () {
1193- log .Trace ("Permission Denied: User: %-v cannot read code in Repo: %-v\n User in headRepo has Permissions: %-+v" ,
1194- ctx .Doer ,
1195- headRepo ,
1196- permHead )
1197- }
1198- headGitRepo .Close ()
1191+ log .Trace ("Permission Denied: User: %-v cannot read code in Repo: %-v\n User in headRepo has Permissions: %-+v" , ctx .Doer , headRepo , permHead )
11991192 ctx .NotFound ("Can't read headRepo UnitTypeCode" )
1200- return nil , nil , nil , "" , ""
1193+ return nil , nil
12011194 }
12021195
1203- // Check if head branch is valid.
1204- if ! headGitRepo .IsBranchExist (headBranch ) && ! headGitRepo .IsTagExist (headBranch ) {
1205- headGitRepo .Close ()
1196+ baseRef := ctx .Repo .GitRepo .UnstableGuessRefByShortName (baseRefToGuess )
1197+ headRef := headGitRepo .UnstableGuessRefByShortName (headRefToGuess )
1198+
1199+ log .Trace ("Repo path: %q, base ref: %q->%q, head ref: %q->%q" , ctx .Repo .GitRepo .Path , baseRefToGuess , baseRef , headRefToGuess , headRef )
1200+
1201+ baseRefValid := baseRef .IsBranch () || baseRef .IsTag () || git .IsStringLikelyCommitID (git .ObjectFormatFromName (ctx .Repo .Repository .ObjectFormatName ), baseRef .ShortName ())
1202+ headRefValid := headRef .IsBranch () || headRef .IsTag () || git .IsStringLikelyCommitID (git .ObjectFormatFromName (headRepo .ObjectFormatName ), headRef .ShortName ())
1203+ // Check if base&head ref are valid.
1204+ if ! baseRefValid || ! headRefValid {
12061205 ctx .NotFound ()
1207- return nil , nil , nil , "" , ""
1206+ return nil , nil
12081207 }
12091208
1210- compareInfo , err := headGitRepo .GetCompareInfo (repo_model .RepoPath (baseRepo .Owner .Name , baseRepo .Name ), baseBranch , headBranch , false , false )
1209+ compareInfo , err := headGitRepo .GetCompareInfo (repo_model .RepoPath (baseRepo .Owner .Name , baseRepo .Name ), baseRef . ShortName (), headRef . ShortName () , false , false )
12111210 if err != nil {
1212- headGitRepo .Close ()
12131211 ctx .Error (http .StatusInternalServerError , "GetCompareInfo" , err )
1214- return nil , nil , nil , "" , ""
1212+ return nil , nil
12151213 }
12161214
1217- return headRepo , headGitRepo , compareInfo , baseBranch , headBranch
1215+ result = & parseCompareInfoResult {headRepo : headRepo , headGitRepo : headGitRepo , compareInfo : compareInfo , baseRef : baseRef , headRef : headRef }
1216+ return result , closer
12181217}
12191218
12201219// UpdatePullRequest merge PR's baseBranch into headBranch
0 commit comments