@@ -389,8 +389,7 @@ func CreatePullRequest(ctx *context.APIContext) {
389
389
390
390
form := * web .GetForm (ctx ).(* api.CreatePullRequestOption )
391
391
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" )
394
393
return
395
394
}
396
395
@@ -401,14 +400,22 @@ func CreatePullRequest(ctx *context.APIContext) {
401
400
)
402
401
403
402
// Get repo/branch information
404
- headRepo , headGitRepo , compareInfo , baseBranch , headBranch := parseCompareInfo (ctx , form )
403
+ compareResult , closer := parseCompareInfo (ctx , form )
405
404
if ctx .Written () {
406
405
return
407
406
}
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
+ }
409
413
410
414
// 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
+ )
412
419
if err != nil {
413
420
if ! issues_model .IsErrPullRequestNotExist (err ) {
414
421
ctx .Error (http .StatusInternalServerError , "GetUnmergedPullRequest" , err )
@@ -484,13 +491,13 @@ func CreatePullRequest(ctx *context.APIContext) {
484
491
DeadlineUnix : deadlineUnix ,
485
492
}
486
493
pr := & issues_model.PullRequest {
487
- HeadRepoID : headRepo .ID ,
494
+ HeadRepoID : compareResult . headRepo .ID ,
488
495
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 ,
492
499
BaseRepo : repo ,
493
- MergeBase : compareInfo .MergeBase ,
500
+ MergeBase : compareResult . compareInfo .MergeBase ,
494
501
Type : issues_model .PullRequestGitea ,
495
502
}
496
503
@@ -1080,71 +1087,62 @@ func MergePullRequest(ctx *context.APIContext) {
1080
1087
ctx .Status (http .StatusOK )
1081
1088
}
1082
1089
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
+ }
1085
1097
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
1086
1101
// Get compared branches information
1087
1102
// format: <base branch>...[<head repo>:]<head branch>
1088
1103
// base<-head: master...head:feature
1089
1104
// same repo: master...feature
1105
+ baseRepo := ctx .Repo .Repository
1106
+ baseRefToGuess := form .Base
1090
1107
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.
1108
1113
} 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 ]
1109
1116
headUser , err = user_model .GetUserByName (ctx , headInfos [0 ])
1110
1117
if err != nil {
1111
1118
if user_model .IsErrUserNotExist (err ) {
1112
1119
ctx .NotFound ("GetUserByName" )
1113
1120
} else {
1114
1121
ctx .Error (http .StatusInternalServerError , "GetUserByName" , err )
1115
1122
}
1116
- return nil , nil , nil , "" , ""
1123
+ return nil , nil
1117
1124
}
1118
- headBranch = headInfos [1 ]
1119
- // The head repository can also point to the same repo
1120
- isSameRepo = ctx .Repo .Owner .ID == headUser .ID
1121
1125
} else {
1122
1126
ctx .NotFound ()
1123
- return nil , nil , nil , "" , ""
1127
+ return nil , nil
1124
1128
}
1125
1129
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
1133
1131
1134
1132
// Check if current user has fork of repository or in the same repository.
1135
1133
headRepo := repo_model .GetForkedRepo (ctx , headUser .ID , baseRepo .ID )
1136
1134
if headRepo == nil && ! isSameRepo {
1137
- err : = baseRepo .GetBaseRepo (ctx )
1135
+ err = baseRepo .GetBaseRepo (ctx )
1138
1136
if err != nil {
1139
1137
ctx .Error (http .StatusInternalServerError , "GetBaseRepo" , err )
1140
- return nil , nil , nil , "" , ""
1138
+ return nil , nil
1141
1139
}
1142
1140
1143
1141
// Check if baseRepo's base repository is the same as headUser's repository.
1144
1142
if baseRepo .BaseRepo == nil || baseRepo .BaseRepo .OwnerID != headUser .ID {
1145
1143
log .Trace ("parseCompareInfo[%d]: does not have fork or in same repository" , baseRepo .ID )
1146
1144
ctx .NotFound ("GetBaseRepo" )
1147
- return nil , nil , nil , "" , ""
1145
+ return nil , nil
1148
1146
}
1149
1147
// Assign headRepo so it can be used below.
1150
1148
headRepo = baseRepo .BaseRepo
@@ -1154,67 +1152,68 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
1154
1152
if isSameRepo {
1155
1153
headRepo = ctx .Repo .Repository
1156
1154
headGitRepo = ctx .Repo .GitRepo
1155
+ closer = func () {} // no need to close the head repo because it shares the base repo
1157
1156
} else {
1158
1157
headGitRepo , err = gitrepo .OpenRepository (ctx , headRepo )
1159
1158
if err != nil {
1160
1159
ctx .Error (http .StatusInternalServerError , "OpenRepository" , err )
1161
- return nil , nil , nil , "" , ""
1160
+ return nil , nil
1162
1161
}
1162
+ closer = func () { _ = headGitRepo .Close () }
1163
1163
}
1164
+ defer func () {
1165
+ if result == nil && ! isSameRepo {
1166
+ _ = headGitRepo .Close ()
1167
+ }
1168
+ }()
1164
1169
1165
1170
// user should have permission to read baseRepo's codes and pulls, NOT headRepo's
1166
1171
permBase , err := access_model .GetUserRepoPermission (ctx , baseRepo , ctx .Doer )
1167
1172
if err != nil {
1168
- headGitRepo .Close ()
1169
1173
ctx .Error (http .StatusInternalServerError , "GetUserRepoPermission" , err )
1170
- return nil , nil , nil , "" , ""
1174
+ return nil , nil
1171
1175
}
1176
+
1172
1177
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 )
1180
1179
ctx .NotFound ("Can't read pulls or can't read UnitTypeCode" )
1181
- return nil , nil , nil , "" , ""
1180
+ return nil , nil
1182
1181
}
1183
1182
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.
1185
1185
permHead , err := access_model .GetUserRepoPermission (ctx , headRepo , ctx .Doer )
1186
1186
if err != nil {
1187
- headGitRepo .Close ()
1188
1187
ctx .Error (http .StatusInternalServerError , "GetUserRepoPermission" , err )
1189
- return nil , nil , nil , "" , ""
1188
+ return nil , nil
1190
1189
}
1191
1190
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 )
1199
1192
ctx .NotFound ("Can't read headRepo UnitTypeCode" )
1200
- return nil , nil , nil , "" , ""
1193
+ return nil , nil
1201
1194
}
1202
1195
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 {
1206
1205
ctx .NotFound ()
1207
- return nil , nil , nil , "" , ""
1206
+ return nil , nil
1208
1207
}
1209
1208
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 )
1211
1210
if err != nil {
1212
- headGitRepo .Close ()
1213
1211
ctx .Error (http .StatusInternalServerError , "GetCompareInfo" , err )
1214
- return nil , nil , nil , "" , ""
1212
+ return nil , nil
1215
1213
}
1216
1214
1217
- return headRepo , headGitRepo , compareInfo , baseBranch , headBranch
1215
+ result = & parseCompareInfoResult {headRepo : headRepo , headGitRepo : headGitRepo , compareInfo : compareInfo , baseRef : baseRef , headRef : headRef }
1216
+ return result , closer
1218
1217
}
1219
1218
1220
1219
// UpdatePullRequest merge PR's baseBranch into headBranch
0 commit comments