@@ -71,25 +71,45 @@ func ParseCompareInfo(ctx *context.Context) (*models.User, *models.Repository, *
71
71
baseRepo := ctx .Repo .Repository
72
72
73
73
// Get compared branches information
74
+ // A full compare url is of the form:
75
+ //
76
+ // 1. /{:baseOwner}/{:baseRepoName}/compare/{:baseBranch}...{:headBranch}
77
+ // 2. /{:baseOwner}/{:baseRepoName}/compare/{:baseBranch}...{:headOwner}:{:headBranch}
78
+ // 3. /{:baseOwner}/{:baseRepoName}/compare/{:baseBranch}...{:headOwner}/{:headRepoName}:{:headBranch}
79
+ //
80
+ // Here we obtain the infoPath "{:baseBranch}...[{:headOwner}/{:headRepoName}:]{:headBranch}" as ctx.Params("*")
81
+ // with the :baseRepo in ctx.Repo.
82
+ //
83
+ // Note: Generally :headRepoName is not provided here - we are only passed :headOwner.
84
+ //
85
+ // How do we determine the :headRepo?
86
+ //
87
+ // 1. If :headOwner is not set then the :headRepo = :baseRepo
88
+ // 2. If :headOwner is set - then look for the fork of :baseRepo owned by :headOwner
89
+ // 3. But... :baseRepo could be a fork of :headOwner's repo - so check that
90
+ // 4. Now, :baseRepo and :headRepos could be forks of the same repo - so check that
91
+ //
74
92
// format: <base branch>...[<head repo>:]<head branch>
75
93
// base<-head: master...head:feature
76
94
// same repo: master...feature
77
95
78
96
var (
79
97
headUser * models.User
98
+ headRepo * models.Repository
80
99
headBranch string
81
100
isSameRepo bool
82
101
infoPath string
83
102
err error
84
103
)
85
104
infoPath = ctx .Params ("*" )
86
- infos := strings .Split (infoPath , "..." )
105
+ infos := strings .SplitN (infoPath , "..." , 2 )
87
106
if len (infos ) != 2 {
88
107
log .Trace ("ParseCompareInfo[%d]: not enough compared branches information %s" , baseRepo .ID , infos )
89
108
ctx .NotFound ("CompareAndPullRequest" , nil )
90
109
return nil , nil , nil , nil , "" , ""
91
110
}
92
111
112
+ ctx .Data ["BaseName" ] = baseRepo .OwnerName
93
113
baseBranch := infos [0 ]
94
114
ctx .Data ["BaseBranch" ] = baseBranch
95
115
@@ -101,17 +121,44 @@ func ParseCompareInfo(ctx *context.Context) (*models.User, *models.Repository, *
101
121
headBranch = headInfos [0 ]
102
122
103
123
} else if len (headInfos ) == 2 {
104
- headUser , err = models .GetUserByName (headInfos [0 ])
105
- if err != nil {
106
- if models .IsErrUserNotExist (err ) {
107
- ctx .NotFound ("GetUserByName" , nil )
108
- } else {
109
- ctx .ServerError ("GetUserByName" , err )
124
+ headInfosSplit := strings .Split (headInfos [0 ], "/" )
125
+ if len (headInfosSplit ) == 1 {
126
+ headUser , err = models .GetUserByName (headInfos [0 ])
127
+ if err != nil {
128
+ if models .IsErrUserNotExist (err ) {
129
+ ctx .NotFound ("GetUserByName" , nil )
130
+ } else {
131
+ ctx .ServerError ("GetUserByName" , err )
132
+ }
133
+ return nil , nil , nil , nil , "" , ""
110
134
}
111
- return nil , nil , nil , nil , "" , ""
135
+ headBranch = headInfos [1 ]
136
+ isSameRepo = headUser .ID == ctx .Repo .Owner .ID
137
+ if isSameRepo {
138
+ headRepo = baseRepo
139
+ }
140
+ } else {
141
+ headRepo , err = models .GetRepositoryByOwnerAndName (headInfosSplit [0 ], headInfosSplit [1 ])
142
+ if err != nil {
143
+ if models .IsErrRepoNotExist (err ) {
144
+ ctx .NotFound ("GetRepositoryByOwnerAndName" , nil )
145
+ } else {
146
+ ctx .ServerError ("GetRepositoryByOwnerAndName" , err )
147
+ }
148
+ return nil , nil , nil , nil , "" , ""
149
+ }
150
+ if err := headRepo .GetOwner (); err != nil {
151
+ if models .IsErrUserNotExist (err ) {
152
+ ctx .NotFound ("GetUserByName" , nil )
153
+ } else {
154
+ ctx .ServerError ("GetUserByName" , err )
155
+ }
156
+ return nil , nil , nil , nil , "" , ""
157
+ }
158
+ headBranch = headInfos [1 ]
159
+ headUser = headRepo .Owner
160
+ isSameRepo = headRepo .ID == ctx .Repo .Repository .ID
112
161
}
113
- headBranch = headInfos [1 ]
114
- isSameRepo = headUser .ID == ctx .Repo .Owner .ID
115
162
} else {
116
163
ctx .NotFound ("CompareAndPullRequest" , nil )
117
164
return nil , nil , nil , nil , "" , ""
@@ -139,28 +186,92 @@ func ParseCompareInfo(ctx *context.Context) (*models.User, *models.Repository, *
139
186
ctx .Data ["BaseIsBranch" ] = baseIsBranch
140
187
ctx .Data ["BaseIsTag" ] = baseIsTag
141
188
142
- // Check if current user has fork of repository or in the same repository.
143
- headRepo , has := models .HasForkedRepo (headUser .ID , baseRepo .ID )
144
- if ! has && ! isSameRepo {
189
+ // Now we have the repository that represents the base
190
+
191
+ // The current base and head repositories and branches may not
192
+ // actually be the intended branches that the user wants to
193
+ // create a pull-request from - but also determining the head
194
+ // repo is difficult.
195
+
196
+ // We will want therefore to offer a few repositories to set as
197
+ // our base and head
198
+
199
+ // 1. First if the baseRepo is a fork get the "RootRepo" it was
200
+ // forked from
201
+ var rootRepo * models.Repository
202
+ if baseRepo .IsFork {
203
+ err = baseRepo .GetBaseRepo ()
204
+ if err != nil {
205
+ if ! models .IsErrRepoNotExist (err ) {
206
+ ctx .ServerError ("Unable to find root repo" , err )
207
+ return nil , nil , nil , nil , "" , ""
208
+ }
209
+ } else {
210
+ rootRepo = baseRepo .BaseRepo
211
+ }
212
+ }
213
+
214
+ // 2. Now if the current user is not the owner of the baseRepo,
215
+ // check if they have a fork of the base repo and offer that as
216
+ // "OwnForkRepo"
217
+ var ownForkRepo * models.Repository
218
+ if baseRepo .OwnerID != ctx .User .ID {
219
+ repo , has := models .HasForkedRepo (ctx .User .ID , baseRepo .ID )
220
+ if has {
221
+ ownForkRepo = repo
222
+ ctx .Data ["OwnForkRepo" ] = ownForkRepo
223
+ }
224
+ }
225
+
226
+ has := headRepo != nil
227
+ // 3. If the base is a forked from "RootRepo" and the owner of
228
+ // the "RootRepo" is the :headUser - set headRepo to that
229
+ if ! has && rootRepo != nil && rootRepo .OwnerID == headUser .ID {
230
+ headRepo = rootRepo
231
+ has = true
232
+ }
233
+
234
+ // 4. If the ctx.User has their own fork of the baseRepo and the headUser is the ctx.User
235
+ // set the headRepo to the ownFork
236
+ if ! has && ownForkRepo != nil && ownForkRepo .OwnerID == headUser .ID {
237
+ headRepo = ownForkRepo
238
+ has = true
239
+ }
240
+
241
+ // 5. If the headOwner has a fork of the baseRepo - use that
242
+ if ! has {
243
+ headRepo , has = models .HasForkedRepo (headUser .ID , baseRepo .ID )
244
+ }
245
+
246
+ // 6. If the baseRepo is a fork and the headUser has a fork of that use that
247
+ if ! has && baseRepo .IsFork {
248
+ headRepo , has = models .HasForkedRepo (headUser .ID , baseRepo .ForkID )
249
+ }
250
+
251
+ // 7. Otherwise if we're not the same repo and haven't found a repo give up
252
+ if ! isSameRepo && ! has {
145
253
ctx .Data ["PageIsComparePull" ] = false
146
254
}
147
255
256
+ // 8. Finally open the git repo
148
257
var headGitRepo * git.Repository
149
258
if isSameRepo {
150
259
headRepo = ctx .Repo .Repository
151
260
headGitRepo = ctx .Repo .GitRepo
152
- ctx .Data ["BaseName" ] = headUser .Name
153
- } else {
154
- headGitRepo , err = git .OpenRepository (models .RepoPath (headUser .Name , headRepo .Name ))
155
- ctx .Data ["BaseName" ] = baseRepo .OwnerName
261
+ } else if has {
262
+ headGitRepo , err = git .OpenRepository (headRepo .RepoPath ())
156
263
if err != nil {
157
264
ctx .ServerError ("OpenRepository" , err )
158
265
return nil , nil , nil , nil , "" , ""
159
266
}
160
267
defer headGitRepo .Close ()
161
268
}
162
269
163
- // user should have permission to read baseRepo's codes and pulls, NOT headRepo's
270
+ ctx .Data ["HeadRepo" ] = headRepo
271
+
272
+ // Now we need to assert that the ctx.User has permission to read
273
+ // the baseRepo's code and pulls
274
+ // (NOT headRepo's)
164
275
permBase , err := models .GetUserRepoPermission (baseRepo , ctx .User )
165
276
if err != nil {
166
277
ctx .ServerError ("GetUserRepoPermission" , err )
@@ -177,8 +288,9 @@ func ParseCompareInfo(ctx *context.Context) (*models.User, *models.Repository, *
177
288
return nil , nil , nil , nil , "" , ""
178
289
}
179
290
291
+ // If we're not merging from the same repo:
180
292
if ! isSameRepo {
181
- // user should have permission to read headrepo 's codes
293
+ // Assert ctx.User has permission to read headRepo 's codes
182
294
permHead , err := models .GetUserRepoPermission (headRepo , ctx .User )
183
295
if err != nil {
184
296
ctx .ServerError ("GetUserRepoPermission" , err )
@@ -196,6 +308,44 @@ func ParseCompareInfo(ctx *context.Context) (*models.User, *models.Repository, *
196
308
}
197
309
}
198
310
311
+ // If we have a rootRepo and it's different from:
312
+ // 1. the computed base
313
+ // 2. the computed head
314
+ // then get the branches of it
315
+ if rootRepo != nil &&
316
+ rootRepo .ID != headRepo .ID &&
317
+ rootRepo .ID != baseRepo .ID {
318
+ perm , branches , err := getBranchesForRepo (ctx .User , rootRepo )
319
+ if err != nil {
320
+ ctx .ServerError ("GetBranchesForRepo" , err )
321
+ return nil , nil , nil , nil , "" , ""
322
+ }
323
+ if perm {
324
+ ctx .Data ["RootRepo" ] = rootRepo
325
+ ctx .Data ["RootRepoBranches" ] = branches
326
+ }
327
+ }
328
+
329
+ // If we have a ownForkRepo and it's different from:
330
+ // 1. The computed base
331
+ // 2. The computed hea
332
+ // 3. The rootRepo (if we have one)
333
+ // then get the branches from it.
334
+ if ownForkRepo != nil &&
335
+ ownForkRepo .ID != headRepo .ID &&
336
+ ownForkRepo .ID != baseRepo .ID &&
337
+ (rootRepo == nil || ownForkRepo .ID != rootRepo .ID ) {
338
+ perm , branches , err := getBranchesForRepo (ctx .User , ownForkRepo )
339
+ if err != nil {
340
+ ctx .ServerError ("GetBranchesForRepo" , err )
341
+ return nil , nil , nil , nil , "" , ""
342
+ }
343
+ if perm {
344
+ ctx .Data ["OwnForkRepo" ] = ownForkRepo
345
+ ctx .Data ["OwnForkRepoBranches" ] = branches
346
+ }
347
+ }
348
+
199
349
// Check if head branch is valid.
200
350
headIsCommit := headGitRepo .IsCommitExist (headBranch )
201
351
headIsBranch := headGitRepo .IsBranchExist (headBranch )
@@ -343,28 +493,25 @@ func PrepareCompareDiff(
343
493
return false
344
494
}
345
495
346
- // parseBaseRepoInfo parse base repository if current repo is forked.
347
- // The "base" here means the repository where current repo forks from,
348
- // not the repository fetch from current URL.
349
- func parseBaseRepoInfo (ctx * context.Context , repo * models.Repository ) error {
350
- if ! repo .IsFork {
351
- return nil
496
+ func getBranchesForRepo (user * models.User , repo * models.Repository ) (bool , []string , error ) {
497
+ perm , err := models .GetUserRepoPermission (repo , user )
498
+ if err != nil {
499
+ return false , nil , err
352
500
}
353
- if err := repo . GetBaseRepo (); err != nil {
354
- return err
501
+ if ! perm . CanRead ( models . UnitTypeCode ) {
502
+ return false , nil , nil
355
503
}
356
-
357
- baseGitRepo , err := git .OpenRepository (repo .BaseRepo .RepoPath ())
504
+ gitRepo , err := git .OpenRepository (repo .RepoPath ())
358
505
if err != nil {
359
- return err
506
+ return false , nil , err
360
507
}
361
- defer baseGitRepo .Close ()
508
+ defer gitRepo .Close ()
362
509
363
- ctx . Data [ "BaseRepoBranches" ] , err = baseGitRepo .GetBranches ()
510
+ branches , err := gitRepo .GetBranches ()
364
511
if err != nil {
365
- return err
512
+ return false , nil , err
366
513
}
367
- return nil
514
+ return true , branches , nil
368
515
}
369
516
370
517
// CompareDiff show different from one commit to another commit
@@ -375,12 +522,6 @@ func CompareDiff(ctx *context.Context) {
375
522
}
376
523
defer headGitRepo .Close ()
377
524
378
- var err error
379
- if err = parseBaseRepoInfo (ctx , headRepo ); err != nil {
380
- ctx .ServerError ("parseBaseRepoInfo" , err )
381
- return
382
- }
383
-
384
525
nothingToCompare := PrepareCompareDiff (ctx , headUser , headRepo , headGitRepo , compareInfo , baseBranch , headBranch )
385
526
if ctx .Written () {
386
527
return
0 commit comments