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

862 lines
24 KiB

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