You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

pull.go 25 kB

API add/generalize pagination (#9452) * paginate results * fixed deadlock * prevented breaking change * updated swagger * go fmt * fixed find topic * go mod tidy * go mod vendor with go1.13.5 * fixed repo find topics * fixed unit test * added Limit method to Engine struct; use engine variable when provided; fixed gitignore * use ItemsPerPage for default pagesize; fix GetWatchers, getOrgUsersByOrgID and GetStargazers; fix GetAllCommits headers; reverted some changed behaviors * set Page value on Home route * improved memory allocations * fixed response headers * removed logfiles * fixed import order * import order * improved swagger * added function to get models.ListOptions from context * removed pagesize diff on unit test * fixed imports * removed unnecessary struct field * fixed go fmt * scoped PR * code improvements * code improvements * go mod tidy * fixed import order * fixed commit statuses session * fixed files headers * fixed headers; added pagination for notifications * go mod tidy * go fmt * removed Private from user search options; added setting.UI.IssuePagingNum as default valeu on repo's issues list * Apply suggestions from code review Co-Authored-By: 6543 <6543@obermui.de> Co-Authored-By: zeripath <art27@cantab.net> * fixed build error * CI.restart() * fixed merge conflicts resolve * fixed conflicts resolve * improved FindTrackedTimesOptions.ToOptions() method * added backwards compatibility on ListReleases request; fixed issue tracked time ToSession * fixed build error; fixed swagger template * fixed swagger template * fixed ListReleases backwards compatibility * added page to user search route Co-authored-by: techknowlogick <matti@mdranta.net> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: zeripath <art27@cantab.net>
5 years ago
API add/generalize pagination (#9452) * paginate results * fixed deadlock * prevented breaking change * updated swagger * go fmt * fixed find topic * go mod tidy * go mod vendor with go1.13.5 * fixed repo find topics * fixed unit test * added Limit method to Engine struct; use engine variable when provided; fixed gitignore * use ItemsPerPage for default pagesize; fix GetWatchers, getOrgUsersByOrgID and GetStargazers; fix GetAllCommits headers; reverted some changed behaviors * set Page value on Home route * improved memory allocations * fixed response headers * removed logfiles * fixed import order * import order * improved swagger * added function to get models.ListOptions from context * removed pagesize diff on unit test * fixed imports * removed unnecessary struct field * fixed go fmt * scoped PR * code improvements * code improvements * go mod tidy * fixed import order * fixed commit statuses session * fixed files headers * fixed headers; added pagination for notifications * go mod tidy * go fmt * removed Private from user search options; added setting.UI.IssuePagingNum as default valeu on repo's issues list * Apply suggestions from code review Co-Authored-By: 6543 <6543@obermui.de> Co-Authored-By: zeripath <art27@cantab.net> * fixed build error * CI.restart() * fixed merge conflicts resolve * fixed conflicts resolve * improved FindTrackedTimesOptions.ToOptions() method * added backwards compatibility on ListReleases request; fixed issue tracked time ToSession * fixed build error; fixed swagger template * fixed swagger template * fixed ListReleases backwards compatibility * added page to user search route Co-authored-by: techknowlogick <matti@mdranta.net> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: zeripath <art27@cantab.net>
5 years ago
API add/generalize pagination (#9452) * paginate results * fixed deadlock * prevented breaking change * updated swagger * go fmt * fixed find topic * go mod tidy * go mod vendor with go1.13.5 * fixed repo find topics * fixed unit test * added Limit method to Engine struct; use engine variable when provided; fixed gitignore * use ItemsPerPage for default pagesize; fix GetWatchers, getOrgUsersByOrgID and GetStargazers; fix GetAllCommits headers; reverted some changed behaviors * set Page value on Home route * improved memory allocations * fixed response headers * removed logfiles * fixed import order * import order * improved swagger * added function to get models.ListOptions from context * removed pagesize diff on unit test * fixed imports * removed unnecessary struct field * fixed go fmt * scoped PR * code improvements * code improvements * go mod tidy * fixed import order * fixed commit statuses session * fixed files headers * fixed headers; added pagination for notifications * go mod tidy * go fmt * removed Private from user search options; added setting.UI.IssuePagingNum as default valeu on repo's issues list * Apply suggestions from code review Co-Authored-By: 6543 <6543@obermui.de> Co-Authored-By: zeripath <art27@cantab.net> * fixed build error * CI.restart() * fixed merge conflicts resolve * fixed conflicts resolve * improved FindTrackedTimesOptions.ToOptions() method * added backwards compatibility on ListReleases request; fixed issue tracked time ToSession * fixed build error; fixed swagger template * fixed swagger template * fixed ListReleases backwards compatibility * added page to user search route Co-authored-by: techknowlogick <matti@mdranta.net> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: zeripath <art27@cantab.net>
5 years ago
API add/generalize pagination (#9452) * paginate results * fixed deadlock * prevented breaking change * updated swagger * go fmt * fixed find topic * go mod tidy * go mod vendor with go1.13.5 * fixed repo find topics * fixed unit test * added Limit method to Engine struct; use engine variable when provided; fixed gitignore * use ItemsPerPage for default pagesize; fix GetWatchers, getOrgUsersByOrgID and GetStargazers; fix GetAllCommits headers; reverted some changed behaviors * set Page value on Home route * improved memory allocations * fixed response headers * removed logfiles * fixed import order * import order * improved swagger * added function to get models.ListOptions from context * removed pagesize diff on unit test * fixed imports * removed unnecessary struct field * fixed go fmt * scoped PR * code improvements * code improvements * go mod tidy * fixed import order * fixed commit statuses session * fixed files headers * fixed headers; added pagination for notifications * go mod tidy * go fmt * removed Private from user search options; added setting.UI.IssuePagingNum as default valeu on repo's issues list * Apply suggestions from code review Co-Authored-By: 6543 <6543@obermui.de> Co-Authored-By: zeripath <art27@cantab.net> * fixed build error * CI.restart() * fixed merge conflicts resolve * fixed conflicts resolve * improved FindTrackedTimesOptions.ToOptions() method * added backwards compatibility on ListReleases request; fixed issue tracked time ToSession * fixed build error; fixed swagger template * fixed swagger template * fixed ListReleases backwards compatibility * added page to user search route Co-authored-by: techknowlogick <matti@mdranta.net> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: zeripath <art27@cantab.net>
5 years ago
API add/generalize pagination (#9452) * paginate results * fixed deadlock * prevented breaking change * updated swagger * go fmt * fixed find topic * go mod tidy * go mod vendor with go1.13.5 * fixed repo find topics * fixed unit test * added Limit method to Engine struct; use engine variable when provided; fixed gitignore * use ItemsPerPage for default pagesize; fix GetWatchers, getOrgUsersByOrgID and GetStargazers; fix GetAllCommits headers; reverted some changed behaviors * set Page value on Home route * improved memory allocations * fixed response headers * removed logfiles * fixed import order * import order * improved swagger * added function to get models.ListOptions from context * removed pagesize diff on unit test * fixed imports * removed unnecessary struct field * fixed go fmt * scoped PR * code improvements * code improvements * go mod tidy * fixed import order * fixed commit statuses session * fixed files headers * fixed headers; added pagination for notifications * go mod tidy * go fmt * removed Private from user search options; added setting.UI.IssuePagingNum as default valeu on repo's issues list * Apply suggestions from code review Co-Authored-By: 6543 <6543@obermui.de> Co-Authored-By: zeripath <art27@cantab.net> * fixed build error * CI.restart() * fixed merge conflicts resolve * fixed conflicts resolve * improved FindTrackedTimesOptions.ToOptions() method * added backwards compatibility on ListReleases request; fixed issue tracked time ToSession * fixed build error; fixed swagger template * fixed swagger template * fixed ListReleases backwards compatibility * added page to user search route Co-authored-by: techknowlogick <matti@mdranta.net> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: zeripath <art27@cantab.net>
5 years ago
Issue due date (#3794) * Started adding deadline to ui * Implemented basic issue due date managing * Improved UI for due date managing * Added at least write access to the repo in order to modify issue due dates * Ui improvements * Added issue comments creation when adding/modifying/removing a due date * Show due date in issue list * Added api support for issue due dates * Fixed lint suggestions * Added deadline to sdk * Updated css * Added support for adding/modifiying deadlines for pull requests via api * Fixed comments not created when updating or removing a deadline * update sdk (will do properly once go-gitea/go-sdk#103 is merged) * enhanced updateIssueDeadline * Removed unnessecary Issue.DeadlineString * UI improvements * Small improvments to comment creation + ui & validation improvements * Check if an issue is overdue is now a seperate function * Updated go-sdk with govendor as it was merged * Simplified isOverdue method * removed unessecary deadline to 0 set * Update swagger definitions * Added missing return * Added an explanary comment * Improved updateIssueDeadline method so it'll only update `deadline_unix` * Small changes and improvements * no need to explicitly load the issue when updating a deadline, just use whats already there * small optimisations * Added check if a deadline was modified before updating it * Moved comment creating logic into its own function * Code cleanup for creating deadline comment * locale improvement * When modifying a deadline, the old deadline is saved with the comment * small improvments to xorm session handling when updating an issue deadline + style nitpicks * style nitpicks * Moved checking for if the user has write acces to middleware
7 years ago
Issue due date (#3794) * Started adding deadline to ui * Implemented basic issue due date managing * Improved UI for due date managing * Added at least write access to the repo in order to modify issue due dates * Ui improvements * Added issue comments creation when adding/modifying/removing a due date * Show due date in issue list * Added api support for issue due dates * Fixed lint suggestions * Added deadline to sdk * Updated css * Added support for adding/modifiying deadlines for pull requests via api * Fixed comments not created when updating or removing a deadline * update sdk (will do properly once go-gitea/go-sdk#103 is merged) * enhanced updateIssueDeadline * Removed unnessecary Issue.DeadlineString * UI improvements * Small improvments to comment creation + ui & validation improvements * Check if an issue is overdue is now a seperate function * Updated go-sdk with govendor as it was merged * Simplified isOverdue method * removed unessecary deadline to 0 set * Update swagger definitions * Added missing return * Added an explanary comment * Improved updateIssueDeadline method so it'll only update `deadline_unix` * Small changes and improvements * no need to explicitly load the issue when updating a deadline, just use whats already there * small optimisations * Added check if a deadline was modified before updating it * Moved comment creating logic into its own function * Code cleanup for creating deadline comment * locale improvement * When modifying a deadline, the old deadline is saved with the comment * small improvments to xorm session handling when updating an issue deadline + style nitpicks * style nitpicks * Moved checking for if the user has write acces to middleware
7 years ago
Issue due date (#3794) * Started adding deadline to ui * Implemented basic issue due date managing * Improved UI for due date managing * Added at least write access to the repo in order to modify issue due dates * Ui improvements * Added issue comments creation when adding/modifying/removing a due date * Show due date in issue list * Added api support for issue due dates * Fixed lint suggestions * Added deadline to sdk * Updated css * Added support for adding/modifiying deadlines for pull requests via api * Fixed comments not created when updating or removing a deadline * update sdk (will do properly once go-gitea/go-sdk#103 is merged) * enhanced updateIssueDeadline * Removed unnessecary Issue.DeadlineString * UI improvements * Small improvments to comment creation + ui & validation improvements * Check if an issue is overdue is now a seperate function * Updated go-sdk with govendor as it was merged * Simplified isOverdue method * removed unessecary deadline to 0 set * Update swagger definitions * Added missing return * Added an explanary comment * Improved updateIssueDeadline method so it'll only update `deadline_unix` * Small changes and improvements * no need to explicitly load the issue when updating a deadline, just use whats already there * small optimisations * Added check if a deadline was modified before updating it * Moved comment creating logic into its own function * Code cleanup for creating deadline comment * locale improvement * When modifying a deadline, the old deadline is saved with the comment * small improvments to xorm session handling when updating an issue deadline + style nitpicks * style nitpicks * Moved checking for if the user has write acces to middleware
7 years ago
Issue due date (#3794) * Started adding deadline to ui * Implemented basic issue due date managing * Improved UI for due date managing * Added at least write access to the repo in order to modify issue due dates * Ui improvements * Added issue comments creation when adding/modifying/removing a due date * Show due date in issue list * Added api support for issue due dates * Fixed lint suggestions * Added deadline to sdk * Updated css * Added support for adding/modifiying deadlines for pull requests via api * Fixed comments not created when updating or removing a deadline * update sdk (will do properly once go-gitea/go-sdk#103 is merged) * enhanced updateIssueDeadline * Removed unnessecary Issue.DeadlineString * UI improvements * Small improvments to comment creation + ui & validation improvements * Check if an issue is overdue is now a seperate function * Updated go-sdk with govendor as it was merged * Simplified isOverdue method * removed unessecary deadline to 0 set * Update swagger definitions * Added missing return * Added an explanary comment * Improved updateIssueDeadline method so it'll only update `deadline_unix` * Small changes and improvements * no need to explicitly load the issue when updating a deadline, just use whats already there * small optimisations * Added check if a deadline was modified before updating it * Moved comment creating logic into its own function * Code cleanup for creating deadline comment * locale improvement * When modifying a deadline, the old deadline is saved with the comment * small improvments to xorm session handling when updating an issue deadline + style nitpicks * style nitpicks * Moved checking for if the user has write acces to middleware
7 years ago
Compare branches, commits and tags with each other (#6991) * Supports tags when comparing commits or branches Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Hide headline when only comparing and don't load unused data Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Merges compare logics to allow comparing branches, commits and tags with eachother Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Display branch or tag instead of commit when used for comparing Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Show pull request form after click on button Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Transfers relevant pull.go changes from master to compare.go Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Fixes error when comparing forks against a commit or tag Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Removes console.log from JavaScript file Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Show icon next to commit reference when comparing branch or tag Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Updates css file Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Fixes import order * Renames template variable * Update routers/repo/compare.go Co-Authored-By: zeripath <art27@cantab.net> * Update from master Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Allow short-shas in compare * Renames prInfo to compareInfo Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Check PR permissions only if compare is pull request Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Adjusts comment Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Use compareInfo instead of prInfo
6 years ago
Compare branches, commits and tags with each other (#6991) * Supports tags when comparing commits or branches Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Hide headline when only comparing and don't load unused data Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Merges compare logics to allow comparing branches, commits and tags with eachother Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Display branch or tag instead of commit when used for comparing Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Show pull request form after click on button Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Transfers relevant pull.go changes from master to compare.go Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Fixes error when comparing forks against a commit or tag Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Removes console.log from JavaScript file Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Show icon next to commit reference when comparing branch or tag Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Updates css file Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Fixes import order * Renames template variable * Update routers/repo/compare.go Co-Authored-By: zeripath <art27@cantab.net> * Update from master Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Allow short-shas in compare * Renames prInfo to compareInfo Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Check PR permissions only if compare is pull request Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Adjusts comment Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Use compareInfo instead of prInfo
6 years ago
Compare branches, commits and tags with each other (#6991) * Supports tags when comparing commits or branches Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Hide headline when only comparing and don't load unused data Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Merges compare logics to allow comparing branches, commits and tags with eachother Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Display branch or tag instead of commit when used for comparing Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Show pull request form after click on button Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Transfers relevant pull.go changes from master to compare.go Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Fixes error when comparing forks against a commit or tag Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Removes console.log from JavaScript file Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Show icon next to commit reference when comparing branch or tag Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Updates css file Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Fixes import order * Renames template variable * Update routers/repo/compare.go Co-Authored-By: zeripath <art27@cantab.net> * Update from master Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Allow short-shas in compare * Renames prInfo to compareInfo Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Check PR permissions only if compare is pull request Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Adjusts comment Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Use compareInfo instead of prInfo
6 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854
  1. // Copyright 2016 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package repo
  5. import (
  6. "fmt"
  7. "net/http"
  8. "strings"
  9. "time"
  10. "code.gitea.io/gitea/models"
  11. "code.gitea.io/gitea/modules/auth"
  12. "code.gitea.io/gitea/modules/context"
  13. "code.gitea.io/gitea/modules/convert"
  14. "code.gitea.io/gitea/modules/git"
  15. "code.gitea.io/gitea/modules/log"
  16. api "code.gitea.io/gitea/modules/structs"
  17. "code.gitea.io/gitea/modules/timeutil"
  18. "code.gitea.io/gitea/routers/api/v1/utils"
  19. issue_service "code.gitea.io/gitea/services/issue"
  20. pull_service "code.gitea.io/gitea/services/pull"
  21. )
  22. // ListPullRequests returns a list of all PRs
  23. func ListPullRequests(ctx *context.APIContext, form api.ListPullRequestsOptions) {
  24. // swagger:operation GET /repos/{owner}/{repo}/pulls repository repoListPullRequests
  25. // ---
  26. // summary: List a repo's pull requests
  27. // produces:
  28. // - application/json
  29. // parameters:
  30. // - name: owner
  31. // in: path
  32. // description: owner of the repo
  33. // type: string
  34. // required: true
  35. // - name: repo
  36. // in: path
  37. // description: name of the repo
  38. // type: string
  39. // required: true
  40. // - name: state
  41. // in: query
  42. // description: "State of pull request: open or closed (optional)"
  43. // type: string
  44. // enum: [closed, open, all]
  45. // - name: sort
  46. // in: query
  47. // description: "Type of sort"
  48. // type: string
  49. // enum: [oldest, recentupdate, leastupdate, mostcomment, leastcomment, priority]
  50. // - name: milestone
  51. // in: query
  52. // description: "ID of the milestone"
  53. // type: integer
  54. // format: int64
  55. // - name: labels
  56. // in: query
  57. // description: "Label IDs"
  58. // type: array
  59. // collectionFormat: multi
  60. // items:
  61. // type: integer
  62. // format: int64
  63. // - name: page
  64. // in: query
  65. // description: page number of results to return (1-based)
  66. // type: integer
  67. // - name: limit
  68. // in: query
  69. // description: page size of results, maximum page size is 50
  70. // type: integer
  71. // responses:
  72. // "200":
  73. // "$ref": "#/responses/PullRequestList"
  74. listOptions := utils.GetListOptions(ctx)
  75. prs, maxResults, err := models.PullRequests(ctx.Repo.Repository.ID, &models.PullRequestsOptions{
  76. ListOptions: listOptions,
  77. State: ctx.QueryTrim("state"),
  78. SortType: ctx.QueryTrim("sort"),
  79. Labels: ctx.QueryStrings("labels"),
  80. MilestoneID: ctx.QueryInt64("milestone"),
  81. })
  82. if err != nil {
  83. ctx.Error(http.StatusInternalServerError, "PullRequests", err)
  84. return
  85. }
  86. apiPrs := make([]*api.PullRequest, len(prs))
  87. for i := range prs {
  88. if err = prs[i].LoadIssue(); err != nil {
  89. ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
  90. return
  91. }
  92. if err = prs[i].LoadAttributes(); err != nil {
  93. ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
  94. return
  95. }
  96. if err = prs[i].LoadBaseRepo(); err != nil {
  97. ctx.Error(http.StatusInternalServerError, "LoadBaseRepo", err)
  98. return
  99. }
  100. if err = prs[i].LoadHeadRepo(); err != nil {
  101. ctx.Error(http.StatusInternalServerError, "LoadHeadRepo", err)
  102. return
  103. }
  104. apiPrs[i] = convert.ToAPIPullRequest(prs[i])
  105. }
  106. ctx.SetLinkHeader(int(maxResults), listOptions.PageSize)
  107. ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", maxResults))
  108. ctx.JSON(http.StatusOK, &apiPrs)
  109. }
  110. // GetPullRequest returns a single PR based on index
  111. func GetPullRequest(ctx *context.APIContext) {
  112. // swagger:operation GET /repos/{owner}/{repo}/pulls/{index} repository repoGetPullRequest
  113. // ---
  114. // summary: Get a pull request
  115. // produces:
  116. // - application/json
  117. // parameters:
  118. // - name: owner
  119. // in: path
  120. // description: owner of the repo
  121. // type: string
  122. // required: true
  123. // - name: repo
  124. // in: path
  125. // description: name of the repo
  126. // type: string
  127. // required: true
  128. // - name: index
  129. // in: path
  130. // description: index of the pull request to get
  131. // type: integer
  132. // format: int64
  133. // required: true
  134. // responses:
  135. // "200":
  136. // "$ref": "#/responses/PullRequest"
  137. // "404":
  138. // "$ref": "#/responses/notFound"
  139. pr, err := models.GetPullRequestByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
  140. if err != nil {
  141. if models.IsErrPullRequestNotExist(err) {
  142. ctx.NotFound()
  143. } else {
  144. ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
  145. }
  146. return
  147. }
  148. if err = pr.LoadBaseRepo(); err != nil {
  149. ctx.Error(http.StatusInternalServerError, "LoadBaseRepo", err)
  150. return
  151. }
  152. if err = pr.LoadHeadRepo(); err != nil {
  153. ctx.Error(http.StatusInternalServerError, "LoadHeadRepo", err)
  154. return
  155. }
  156. ctx.JSON(http.StatusOK, convert.ToAPIPullRequest(pr))
  157. }
  158. // CreatePullRequest does what it says
  159. func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption) {
  160. // swagger:operation POST /repos/{owner}/{repo}/pulls repository repoCreatePullRequest
  161. // ---
  162. // summary: Create a pull request
  163. // consumes:
  164. // - application/json
  165. // produces:
  166. // - application/json
  167. // parameters:
  168. // - name: owner
  169. // in: path
  170. // description: owner of the repo
  171. // type: string
  172. // required: true
  173. // - name: repo
  174. // in: path
  175. // description: name of the repo
  176. // type: string
  177. // required: true
  178. // - name: body
  179. // in: body
  180. // schema:
  181. // "$ref": "#/definitions/CreatePullRequestOption"
  182. // responses:
  183. // "201":
  184. // "$ref": "#/responses/PullRequest"
  185. // "409":
  186. // "$ref": "#/responses/error"
  187. // "422":
  188. // "$ref": "#/responses/validationError"
  189. var (
  190. repo = ctx.Repo.Repository
  191. labelIDs []int64
  192. assigneeID int64
  193. milestoneID int64
  194. )
  195. // Get repo/branch information
  196. _, headRepo, headGitRepo, compareInfo, baseBranch, headBranch := parseCompareInfo(ctx, form)
  197. if ctx.Written() {
  198. return
  199. }
  200. defer headGitRepo.Close()
  201. // Check if another PR exists with the same targets
  202. existingPr, err := models.GetUnmergedPullRequest(headRepo.ID, ctx.Repo.Repository.ID, headBranch, baseBranch)
  203. if err != nil {
  204. if !models.IsErrPullRequestNotExist(err) {
  205. ctx.Error(http.StatusInternalServerError, "GetUnmergedPullRequest", err)
  206. return
  207. }
  208. } else {
  209. err = models.ErrPullRequestAlreadyExists{
  210. ID: existingPr.ID,
  211. IssueID: existingPr.Index,
  212. HeadRepoID: existingPr.HeadRepoID,
  213. BaseRepoID: existingPr.BaseRepoID,
  214. HeadBranch: existingPr.HeadBranch,
  215. BaseBranch: existingPr.BaseBranch,
  216. }
  217. ctx.Error(http.StatusConflict, "GetUnmergedPullRequest", err)
  218. return
  219. }
  220. if len(form.Labels) > 0 {
  221. labels, err := models.GetLabelsInRepoByIDs(ctx.Repo.Repository.ID, form.Labels)
  222. if err != nil {
  223. ctx.Error(http.StatusInternalServerError, "GetLabelsInRepoByIDs", err)
  224. return
  225. }
  226. labelIDs = make([]int64, len(form.Labels))
  227. orgLabelIDs := make([]int64, len(form.Labels))
  228. for i := range labels {
  229. labelIDs[i] = labels[i].ID
  230. }
  231. if ctx.Repo.Owner.IsOrganization() {
  232. orgLabels, err := models.GetLabelsInOrgByIDs(ctx.Repo.Owner.ID, form.Labels)
  233. if err != nil {
  234. ctx.Error(http.StatusInternalServerError, "GetLabelsInOrgByIDs", err)
  235. return
  236. }
  237. for i := range orgLabels {
  238. orgLabelIDs[i] = orgLabels[i].ID
  239. }
  240. }
  241. labelIDs = append(labelIDs, orgLabelIDs...)
  242. }
  243. if form.Milestone > 0 {
  244. milestone, err := models.GetMilestoneByRepoID(ctx.Repo.Repository.ID, milestoneID)
  245. if err != nil {
  246. if models.IsErrMilestoneNotExist(err) {
  247. ctx.NotFound()
  248. } else {
  249. ctx.Error(http.StatusInternalServerError, "GetMilestoneByRepoID", err)
  250. }
  251. return
  252. }
  253. milestoneID = milestone.ID
  254. }
  255. var deadlineUnix timeutil.TimeStamp
  256. if form.Deadline != nil {
  257. deadlineUnix = timeutil.TimeStamp(form.Deadline.Unix())
  258. }
  259. prIssue := &models.Issue{
  260. RepoID: repo.ID,
  261. Title: form.Title,
  262. PosterID: ctx.User.ID,
  263. Poster: ctx.User,
  264. MilestoneID: milestoneID,
  265. AssigneeID: assigneeID,
  266. IsPull: true,
  267. Content: form.Body,
  268. DeadlineUnix: deadlineUnix,
  269. }
  270. pr := &models.PullRequest{
  271. HeadRepoID: headRepo.ID,
  272. BaseRepoID: repo.ID,
  273. HeadBranch: headBranch,
  274. BaseBranch: baseBranch,
  275. HeadRepo: headRepo,
  276. BaseRepo: repo,
  277. MergeBase: compareInfo.MergeBase,
  278. Type: models.PullRequestGitea,
  279. }
  280. // Get all assignee IDs
  281. assigneeIDs, err := models.MakeIDsFromAPIAssigneesToAdd(form.Assignee, form.Assignees)
  282. if err != nil {
  283. if models.IsErrUserNotExist(err) {
  284. ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("Assignee does not exist: [name: %s]", err))
  285. } else {
  286. ctx.Error(http.StatusInternalServerError, "AddAssigneeByName", err)
  287. }
  288. return
  289. }
  290. // Check if the passed assignees is assignable
  291. for _, aID := range assigneeIDs {
  292. assignee, err := models.GetUserByID(aID)
  293. if err != nil {
  294. ctx.Error(http.StatusInternalServerError, "GetUserByID", err)
  295. return
  296. }
  297. valid, err := models.CanBeAssigned(assignee, repo, true)
  298. if err != nil {
  299. ctx.Error(http.StatusInternalServerError, "canBeAssigned", err)
  300. return
  301. }
  302. if !valid {
  303. ctx.Error(http.StatusUnprocessableEntity, "canBeAssigned", models.ErrUserDoesNotHaveAccessToRepo{UserID: aID, RepoName: repo.Name})
  304. return
  305. }
  306. }
  307. if err := pull_service.NewPullRequest(repo, prIssue, labelIDs, []string{}, pr, assigneeIDs); err != nil {
  308. if models.IsErrUserDoesNotHaveAccessToRepo(err) {
  309. ctx.Error(http.StatusBadRequest, "UserDoesNotHaveAccessToRepo", err)
  310. return
  311. }
  312. ctx.Error(http.StatusInternalServerError, "NewPullRequest", err)
  313. return
  314. }
  315. log.Trace("Pull request created: %d/%d", repo.ID, prIssue.ID)
  316. ctx.JSON(http.StatusCreated, convert.ToAPIPullRequest(pr))
  317. }
  318. // EditPullRequest does what it says
  319. func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) {
  320. // swagger:operation PATCH /repos/{owner}/{repo}/pulls/{index} repository repoEditPullRequest
  321. // ---
  322. // summary: Update a pull request. If using deadline only the date will be taken into account, and time of day ignored.
  323. // consumes:
  324. // - application/json
  325. // produces:
  326. // - application/json
  327. // parameters:
  328. // - name: owner
  329. // in: path
  330. // description: owner of the repo
  331. // type: string
  332. // required: true
  333. // - name: repo
  334. // in: path
  335. // description: name of the repo
  336. // type: string
  337. // required: true
  338. // - name: index
  339. // in: path
  340. // description: index of the pull request to edit
  341. // type: integer
  342. // format: int64
  343. // required: true
  344. // - name: body
  345. // in: body
  346. // schema:
  347. // "$ref": "#/definitions/EditPullRequestOption"
  348. // responses:
  349. // "201":
  350. // "$ref": "#/responses/PullRequest"
  351. // "403":
  352. // "$ref": "#/responses/forbidden"
  353. // "412":
  354. // "$ref": "#/responses/error"
  355. // "422":
  356. // "$ref": "#/responses/validationError"
  357. pr, err := models.GetPullRequestByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
  358. if err != nil {
  359. if models.IsErrPullRequestNotExist(err) {
  360. ctx.NotFound()
  361. } else {
  362. ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
  363. }
  364. return
  365. }
  366. err = pr.LoadIssue()
  367. if err != nil {
  368. ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
  369. return
  370. }
  371. issue := pr.Issue
  372. issue.Repo = ctx.Repo.Repository
  373. if !issue.IsPoster(ctx.User.ID) && !ctx.Repo.CanWrite(models.UnitTypePullRequests) {
  374. ctx.Status(http.StatusForbidden)
  375. return
  376. }
  377. if len(form.Title) > 0 {
  378. issue.Title = form.Title
  379. }
  380. if len(form.Body) > 0 {
  381. issue.Content = form.Body
  382. }
  383. // Update or remove deadline if set
  384. if form.Deadline != nil || form.RemoveDeadline != nil {
  385. var deadlineUnix timeutil.TimeStamp
  386. if (form.RemoveDeadline == nil || !*form.RemoveDeadline) && !form.Deadline.IsZero() {
  387. deadline := time.Date(form.Deadline.Year(), form.Deadline.Month(), form.Deadline.Day(),
  388. 23, 59, 59, 0, form.Deadline.Location())
  389. deadlineUnix = timeutil.TimeStamp(deadline.Unix())
  390. }
  391. if err := models.UpdateIssueDeadline(issue, deadlineUnix, ctx.User); err != nil {
  392. ctx.Error(http.StatusInternalServerError, "UpdateIssueDeadline", err)
  393. return
  394. }
  395. issue.DeadlineUnix = deadlineUnix
  396. }
  397. // Add/delete assignees
  398. // Deleting is done the GitHub way (quote from their api documentation):
  399. // https://developer.github.com/v3/issues/#edit-an-issue
  400. // "assignees" (array): Logins for Users to assign to this issue.
  401. // Pass one or more user logins to replace the set of assignees on this Issue.
  402. // Send an empty array ([]) to clear all assignees from the Issue.
  403. if ctx.Repo.CanWrite(models.UnitTypePullRequests) && (form.Assignees != nil || len(form.Assignee) > 0) {
  404. err = issue_service.UpdateAssignees(issue, form.Assignee, form.Assignees, ctx.User)
  405. if err != nil {
  406. if models.IsErrUserNotExist(err) {
  407. ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("Assignee does not exist: [name: %s]", err))
  408. } else {
  409. ctx.Error(http.StatusInternalServerError, "UpdateAssignees", err)
  410. }
  411. return
  412. }
  413. }
  414. if ctx.Repo.CanWrite(models.UnitTypePullRequests) && form.Milestone != 0 &&
  415. issue.MilestoneID != form.Milestone {
  416. oldMilestoneID := issue.MilestoneID
  417. issue.MilestoneID = form.Milestone
  418. if err = issue_service.ChangeMilestoneAssign(issue, ctx.User, oldMilestoneID); err != nil {
  419. ctx.Error(http.StatusInternalServerError, "ChangeMilestoneAssign", err)
  420. return
  421. }
  422. }
  423. if ctx.Repo.CanWrite(models.UnitTypePullRequests) && form.Labels != nil {
  424. labels, err := models.GetLabelsInRepoByIDs(ctx.Repo.Repository.ID, form.Labels)
  425. if err != nil {
  426. ctx.Error(http.StatusInternalServerError, "GetLabelsInRepoByIDsError", err)
  427. return
  428. }
  429. if ctx.Repo.Owner.IsOrganization() {
  430. orgLabels, err := models.GetLabelsInOrgByIDs(ctx.Repo.Owner.ID, form.Labels)
  431. if err != nil {
  432. ctx.Error(http.StatusInternalServerError, "GetLabelsInOrgByIDs", err)
  433. return
  434. }
  435. labels = append(labels, orgLabels...)
  436. }
  437. if err = issue.ReplaceLabels(labels, ctx.User); err != nil {
  438. ctx.Error(http.StatusInternalServerError, "ReplaceLabelsError", err)
  439. return
  440. }
  441. }
  442. if err = models.UpdateIssueByAPI(issue); err != nil {
  443. ctx.Error(http.StatusInternalServerError, "UpdateIssueByAPI", err)
  444. return
  445. }
  446. if form.State != nil {
  447. if err = issue_service.ChangeStatus(issue, ctx.User, api.StateClosed == api.StateType(*form.State)); err != nil {
  448. if models.IsErrDependenciesLeft(err) {
  449. ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this pull request because it still has open dependencies")
  450. return
  451. }
  452. ctx.Error(http.StatusInternalServerError, "ChangeStatus", err)
  453. return
  454. }
  455. }
  456. // Refetch from database
  457. pr, err = models.GetPullRequestByIndex(ctx.Repo.Repository.ID, pr.Index)
  458. if err != nil {
  459. if models.IsErrPullRequestNotExist(err) {
  460. ctx.NotFound()
  461. } else {
  462. ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
  463. }
  464. return
  465. }
  466. // TODO this should be 200, not 201
  467. ctx.JSON(http.StatusCreated, convert.ToAPIPullRequest(pr))
  468. }
  469. // IsPullRequestMerged checks if a PR exists given an index
  470. func IsPullRequestMerged(ctx *context.APIContext) {
  471. // swagger:operation GET /repos/{owner}/{repo}/pulls/{index}/merge repository repoPullRequestIsMerged
  472. // ---
  473. // summary: Check if a pull request has been merged
  474. // produces:
  475. // - application/json
  476. // parameters:
  477. // - name: owner
  478. // in: path
  479. // description: owner of the repo
  480. // type: string
  481. // required: true
  482. // - name: repo
  483. // in: path
  484. // description: name of the repo
  485. // type: string
  486. // required: true
  487. // - name: index
  488. // in: path
  489. // description: index of the pull request
  490. // type: integer
  491. // format: int64
  492. // required: true
  493. // responses:
  494. // "204":
  495. // description: pull request has been merged
  496. // "404":
  497. // description: pull request has not been merged
  498. pr, err := models.GetPullRequestByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
  499. if err != nil {
  500. if models.IsErrPullRequestNotExist(err) {
  501. ctx.NotFound()
  502. } else {
  503. ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
  504. }
  505. return
  506. }
  507. if pr.HasMerged {
  508. ctx.Status(http.StatusNoContent)
  509. }
  510. ctx.NotFound()
  511. }
  512. // MergePullRequest merges a PR given an index
  513. func MergePullRequest(ctx *context.APIContext, form auth.MergePullRequestForm) {
  514. // swagger:operation POST /repos/{owner}/{repo}/pulls/{index}/merge repository repoMergePullRequest
  515. // ---
  516. // summary: Merge a pull request
  517. // produces:
  518. // - application/json
  519. // parameters:
  520. // - name: owner
  521. // in: path
  522. // description: owner of the repo
  523. // type: string
  524. // required: true
  525. // - name: repo
  526. // in: path
  527. // description: name of the repo
  528. // type: string
  529. // required: true
  530. // - name: index
  531. // in: path
  532. // description: index of the pull request to merge
  533. // type: integer
  534. // format: int64
  535. // required: true
  536. // - name: body
  537. // in: body
  538. // schema:
  539. // $ref: "#/definitions/MergePullRequestOption"
  540. // responses:
  541. // "200":
  542. // "$ref": "#/responses/empty"
  543. // "405":
  544. // "$ref": "#/responses/empty"
  545. // "409":
  546. // "$ref": "#/responses/error"
  547. pr, err := models.GetPullRequestByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
  548. if err != nil {
  549. if models.IsErrPullRequestNotExist(err) {
  550. ctx.NotFound("GetPullRequestByIndex", err)
  551. } else {
  552. ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
  553. }
  554. return
  555. }
  556. if err = pr.LoadHeadRepo(); err != nil {
  557. ctx.ServerError("LoadHeadRepo", err)
  558. return
  559. }
  560. err = pr.LoadIssue()
  561. if err != nil {
  562. ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
  563. return
  564. }
  565. pr.Issue.Repo = ctx.Repo.Repository
  566. if ctx.IsSigned {
  567. // Update issue-user.
  568. if err = pr.Issue.ReadBy(ctx.User.ID); err != nil {
  569. ctx.Error(http.StatusInternalServerError, "ReadBy", err)
  570. return
  571. }
  572. }
  573. if pr.Issue.IsClosed {
  574. ctx.NotFound()
  575. return
  576. }
  577. allowedMerge, err := pull_service.IsUserAllowedToMerge(pr, ctx.Repo.Permission, ctx.User)
  578. if err != nil {
  579. ctx.Error(http.StatusInternalServerError, "IsUSerAllowedToMerge", err)
  580. return
  581. }
  582. if !allowedMerge {
  583. ctx.Error(http.StatusMethodNotAllowed, "Merge", "User not allowed to merge PR")
  584. return
  585. }
  586. if !pr.CanAutoMerge() || pr.HasMerged || pr.IsWorkInProgress() {
  587. ctx.Status(http.StatusMethodNotAllowed)
  588. return
  589. }
  590. if err := pull_service.CheckPRReadyToMerge(pr); err != nil {
  591. if !models.IsErrNotAllowedToMerge(err) {
  592. ctx.Error(http.StatusInternalServerError, "CheckPRReadyToMerge", err)
  593. return
  594. }
  595. if form.ForceMerge != nil && *form.ForceMerge {
  596. if isRepoAdmin, err := models.IsUserRepoAdmin(pr.BaseRepo, ctx.User); err != nil {
  597. ctx.Error(http.StatusInternalServerError, "IsUserRepoAdmin", err)
  598. return
  599. } else if !isRepoAdmin {
  600. ctx.Error(http.StatusMethodNotAllowed, "Merge", "Only repository admin can merge if not all checks are ok (force merge)")
  601. }
  602. } else {
  603. ctx.Error(http.StatusMethodNotAllowed, "PR is not ready to be merged", err)
  604. return
  605. }
  606. }
  607. if _, err := pull_service.IsSignedIfRequired(pr, ctx.User); err != nil {
  608. if !models.IsErrWontSign(err) {
  609. ctx.Error(http.StatusInternalServerError, "IsSignedIfRequired", err)
  610. return
  611. }
  612. ctx.Error(http.StatusMethodNotAllowed, fmt.Sprintf("Protected branch %s requires signed commits but this merge would not be signed", pr.BaseBranch), err)
  613. return
  614. }
  615. if len(form.Do) == 0 {
  616. form.Do = string(models.MergeStyleMerge)
  617. }
  618. message := strings.TrimSpace(form.MergeTitleField)
  619. if len(message) == 0 {
  620. if models.MergeStyle(form.Do) == models.MergeStyleMerge {
  621. message = pr.GetDefaultMergeMessage()
  622. }
  623. if models.MergeStyle(form.Do) == models.MergeStyleSquash {
  624. message = pr.GetDefaultSquashMessage()
  625. }
  626. }
  627. form.MergeMessageField = strings.TrimSpace(form.MergeMessageField)
  628. if len(form.MergeMessageField) > 0 {
  629. message += "\n\n" + form.MergeMessageField
  630. }
  631. if err := pull_service.Merge(pr, ctx.User, ctx.Repo.GitRepo, models.MergeStyle(form.Do), message); err != nil {
  632. if models.IsErrInvalidMergeStyle(err) {
  633. ctx.Status(http.StatusMethodNotAllowed)
  634. return
  635. } else if models.IsErrMergeConflicts(err) {
  636. conflictError := err.(models.ErrMergeConflicts)
  637. ctx.JSON(http.StatusConflict, conflictError)
  638. } else if models.IsErrRebaseConflicts(err) {
  639. conflictError := err.(models.ErrRebaseConflicts)
  640. ctx.JSON(http.StatusConflict, conflictError)
  641. } else if models.IsErrMergeUnrelatedHistories(err) {
  642. conflictError := err.(models.ErrMergeUnrelatedHistories)
  643. ctx.JSON(http.StatusConflict, conflictError)
  644. } else if git.IsErrPushOutOfDate(err) {
  645. ctx.Error(http.StatusConflict, "Merge", "merge push out of date")
  646. return
  647. } else if git.IsErrPushRejected(err) {
  648. errPushRej := err.(*git.ErrPushRejected)
  649. if len(errPushRej.Message) == 0 {
  650. ctx.Error(http.StatusConflict, "Merge", "PushRejected without remote error message")
  651. return
  652. }
  653. ctx.Error(http.StatusConflict, "Merge", "PushRejected with remote message: "+errPushRej.Message)
  654. return
  655. }
  656. ctx.Error(http.StatusInternalServerError, "Merge", err)
  657. return
  658. }
  659. log.Trace("Pull request merged: %d", pr.ID)
  660. ctx.Status(http.StatusOK)
  661. }
  662. func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) (*models.User, *models.Repository, *git.Repository, *git.CompareInfo, string, string) {
  663. baseRepo := ctx.Repo.Repository
  664. // Get compared branches information
  665. // format: <base branch>...[<head repo>:]<head branch>
  666. // base<-head: master...head:feature
  667. // same repo: master...feature
  668. // TODO: Validate form first?
  669. baseBranch := form.Base
  670. var (
  671. headUser *models.User
  672. headBranch string
  673. isSameRepo bool
  674. err error
  675. )
  676. // If there is no head repository, it means pull request between same repository.
  677. headInfos := strings.Split(form.Head, ":")
  678. if len(headInfos) == 1 {
  679. isSameRepo = true
  680. headUser = ctx.Repo.Owner
  681. headBranch = headInfos[0]
  682. } else if len(headInfos) == 2 {
  683. headUser, err = models.GetUserByName(headInfos[0])
  684. if err != nil {
  685. if models.IsErrUserNotExist(err) {
  686. ctx.NotFound("GetUserByName")
  687. } else {
  688. ctx.ServerError("GetUserByName", err)
  689. }
  690. return nil, nil, nil, nil, "", ""
  691. }
  692. headBranch = headInfos[1]
  693. } else {
  694. ctx.NotFound()
  695. return nil, nil, nil, nil, "", ""
  696. }
  697. ctx.Repo.PullRequest.SameRepo = isSameRepo
  698. log.Info("Base branch: %s", baseBranch)
  699. log.Info("Repo path: %s", ctx.Repo.GitRepo.Path)
  700. // Check if base branch is valid.
  701. if !ctx.Repo.GitRepo.IsBranchExist(baseBranch) {
  702. ctx.NotFound("IsBranchExist")
  703. return nil, nil, nil, nil, "", ""
  704. }
  705. // Check if current user has fork of repository or in the same repository.
  706. headRepo, has := models.HasForkedRepo(headUser.ID, baseRepo.ID)
  707. if !has && !isSameRepo {
  708. log.Trace("parseCompareInfo[%d]: does not have fork or in same repository", baseRepo.ID)
  709. ctx.NotFound("HasForkedRepo")
  710. return nil, nil, nil, nil, "", ""
  711. }
  712. var headGitRepo *git.Repository
  713. if isSameRepo {
  714. headRepo = ctx.Repo.Repository
  715. headGitRepo = ctx.Repo.GitRepo
  716. } else {
  717. headGitRepo, err = git.OpenRepository(models.RepoPath(headUser.Name, headRepo.Name))
  718. if err != nil {
  719. ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
  720. return nil, nil, nil, nil, "", ""
  721. }
  722. }
  723. // user should have permission to read baseRepo's codes and pulls, NOT headRepo's
  724. permBase, err := models.GetUserRepoPermission(baseRepo, ctx.User)
  725. if err != nil {
  726. headGitRepo.Close()
  727. ctx.ServerError("GetUserRepoPermission", err)
  728. return nil, nil, nil, nil, "", ""
  729. }
  730. if !permBase.CanReadIssuesOrPulls(true) || !permBase.CanRead(models.UnitTypeCode) {
  731. if log.IsTrace() {
  732. log.Trace("Permission Denied: User %-v cannot create/read pull requests or cannot read code in Repo %-v\nUser in baseRepo has Permissions: %-+v",
  733. ctx.User,
  734. baseRepo,
  735. permBase)
  736. }
  737. headGitRepo.Close()
  738. ctx.NotFound("Can't read pulls or can't read UnitTypeCode")
  739. return nil, nil, nil, nil, "", ""
  740. }
  741. // user should have permission to read headrepo's codes
  742. permHead, err := models.GetUserRepoPermission(headRepo, ctx.User)
  743. if err != nil {
  744. headGitRepo.Close()
  745. ctx.ServerError("GetUserRepoPermission", err)
  746. return nil, nil, nil, nil, "", ""
  747. }
  748. if !permHead.CanRead(models.UnitTypeCode) {
  749. if log.IsTrace() {
  750. log.Trace("Permission Denied: User: %-v cannot read code in Repo: %-v\nUser in headRepo has Permissions: %-+v",
  751. ctx.User,
  752. headRepo,
  753. permHead)
  754. }
  755. headGitRepo.Close()
  756. ctx.NotFound("Can't read headRepo UnitTypeCode")
  757. return nil, nil, nil, nil, "", ""
  758. }
  759. // Check if head branch is valid.
  760. if !headGitRepo.IsBranchExist(headBranch) {
  761. headGitRepo.Close()
  762. ctx.NotFound()
  763. return nil, nil, nil, nil, "", ""
  764. }
  765. compareInfo, err := headGitRepo.GetCompareInfo(models.RepoPath(baseRepo.Owner.Name, baseRepo.Name), baseBranch, headBranch)
  766. if err != nil {
  767. headGitRepo.Close()
  768. ctx.Error(http.StatusInternalServerError, "GetCompareInfo", err)
  769. return nil, nil, nil, nil, "", ""
  770. }
  771. return headUser, headRepo, headGitRepo, compareInfo, baseBranch, headBranch
  772. }