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.

641 lines
16 KiB

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
  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. "code.gitea.io/git"
  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/log"
  14. "code.gitea.io/gitea/modules/util"
  15. api "code.gitea.io/sdk/gitea"
  16. )
  17. // ListPullRequests returns a list of all PRs
  18. func ListPullRequests(ctx *context.APIContext, form api.ListPullRequestsOptions) {
  19. // swagger:operation GET /repos/{owner}/{repo}/pulls repository repoListPullRequests
  20. // ---
  21. // summary: List a repo's pull requests
  22. // produces:
  23. // - application/json
  24. // parameters:
  25. // - name: owner
  26. // in: path
  27. // description: owner of the repo
  28. // type: string
  29. // required: true
  30. // - name: repo
  31. // in: path
  32. // description: name of the repo
  33. // type: string
  34. // required: true
  35. // responses:
  36. // "200":
  37. // "$ref": "#/responses/PullRequestList"
  38. prs, maxResults, err := models.PullRequests(ctx.Repo.Repository.ID, &models.PullRequestsOptions{
  39. Page: ctx.QueryInt("page"),
  40. State: ctx.QueryTrim("state"),
  41. SortType: ctx.QueryTrim("sort"),
  42. Labels: ctx.QueryStrings("labels"),
  43. MilestoneID: ctx.QueryInt64("milestone"),
  44. })
  45. if err != nil {
  46. ctx.Error(500, "PullRequests", err)
  47. return
  48. }
  49. apiPrs := make([]*api.PullRequest, len(prs))
  50. for i := range prs {
  51. if err = prs[i].LoadIssue(); err != nil {
  52. ctx.Error(500, "LoadIssue", err)
  53. return
  54. }
  55. if err = prs[i].LoadAttributes(); err != nil {
  56. ctx.Error(500, "LoadAttributes", err)
  57. return
  58. }
  59. if err = prs[i].GetBaseRepo(); err != nil {
  60. ctx.Error(500, "GetBaseRepo", err)
  61. return
  62. }
  63. if err = prs[i].GetHeadRepo(); err != nil {
  64. ctx.Error(500, "GetHeadRepo", err)
  65. return
  66. }
  67. apiPrs[i] = prs[i].APIFormat()
  68. }
  69. ctx.SetLinkHeader(int(maxResults), models.ItemsPerPage)
  70. ctx.JSON(200, &apiPrs)
  71. }
  72. // GetPullRequest returns a single PR based on index
  73. func GetPullRequest(ctx *context.APIContext) {
  74. // swagger:operation GET /repos/{owner}/{repo}/pulls/{index} repository repoGetPullRequest
  75. // ---
  76. // summary: Get a pull request
  77. // produces:
  78. // - application/json
  79. // parameters:
  80. // - name: owner
  81. // in: path
  82. // description: owner of the repo
  83. // type: string
  84. // required: true
  85. // - name: repo
  86. // in: path
  87. // description: name of the repo
  88. // type: string
  89. // required: true
  90. // - name: index
  91. // in: path
  92. // description: index of the pull request to get
  93. // type: integer
  94. // required: true
  95. // responses:
  96. // "200":
  97. // "$ref": "#/responses/PullRequest"
  98. pr, err := models.GetPullRequestByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
  99. if err != nil {
  100. if models.IsErrPullRequestNotExist(err) {
  101. ctx.Status(404)
  102. } else {
  103. ctx.Error(500, "GetPullRequestByIndex", err)
  104. }
  105. return
  106. }
  107. if err = pr.GetBaseRepo(); err != nil {
  108. ctx.Error(500, "GetBaseRepo", err)
  109. return
  110. }
  111. if err = pr.GetHeadRepo(); err != nil {
  112. ctx.Error(500, "GetHeadRepo", err)
  113. return
  114. }
  115. ctx.JSON(200, pr.APIFormat())
  116. }
  117. // CreatePullRequest does what it says
  118. func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption) {
  119. // swagger:operation POST /repos/{owner}/{repo}/pulls repository repoCreatePullRequest
  120. // ---
  121. // summary: Create a pull request
  122. // consumes:
  123. // - application/json
  124. // produces:
  125. // - application/json
  126. // parameters:
  127. // - name: owner
  128. // in: path
  129. // description: owner of the repo
  130. // type: string
  131. // required: true
  132. // - name: repo
  133. // in: path
  134. // description: name of the repo
  135. // type: string
  136. // required: true
  137. // - name: body
  138. // in: body
  139. // schema:
  140. // "$ref": "#/definitions/CreatePullRequestOption"
  141. // responses:
  142. // "201":
  143. // "$ref": "#/responses/PullRequest"
  144. var (
  145. repo = ctx.Repo.Repository
  146. labelIDs []int64
  147. assigneeID int64
  148. milestoneID int64
  149. )
  150. // Get repo/branch information
  151. headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch := parseCompareInfo(ctx, form)
  152. if ctx.Written() {
  153. return
  154. }
  155. // Check if another PR exists with the same targets
  156. existingPr, err := models.GetUnmergedPullRequest(headRepo.ID, ctx.Repo.Repository.ID, headBranch, baseBranch)
  157. if err != nil {
  158. if !models.IsErrPullRequestNotExist(err) {
  159. ctx.Error(500, "GetUnmergedPullRequest", err)
  160. return
  161. }
  162. } else {
  163. err = models.ErrPullRequestAlreadyExists{
  164. ID: existingPr.ID,
  165. IssueID: existingPr.Index,
  166. HeadRepoID: existingPr.HeadRepoID,
  167. BaseRepoID: existingPr.BaseRepoID,
  168. HeadBranch: existingPr.HeadBranch,
  169. BaseBranch: existingPr.BaseBranch,
  170. }
  171. ctx.Error(409, "GetUnmergedPullRequest", err)
  172. return
  173. }
  174. if len(form.Labels) > 0 {
  175. labels, err := models.GetLabelsInRepoByIDs(ctx.Repo.Repository.ID, form.Labels)
  176. if err != nil {
  177. ctx.Error(500, "GetLabelsInRepoByIDs", err)
  178. return
  179. }
  180. labelIDs = make([]int64, len(labels))
  181. for i := range labels {
  182. labelIDs[i] = labels[i].ID
  183. }
  184. }
  185. if form.Milestone > 0 {
  186. milestone, err := models.GetMilestoneByRepoID(ctx.Repo.Repository.ID, milestoneID)
  187. if err != nil {
  188. if models.IsErrMilestoneNotExist(err) {
  189. ctx.Status(404)
  190. } else {
  191. ctx.Error(500, "GetMilestoneByRepoID", err)
  192. }
  193. return
  194. }
  195. milestoneID = milestone.ID
  196. }
  197. patch, err := headGitRepo.GetPatch(prInfo.MergeBase, headBranch)
  198. if err != nil {
  199. ctx.Error(500, "GetPatch", err)
  200. return
  201. }
  202. var deadlineUnix util.TimeStamp
  203. if form.Deadline != nil {
  204. deadlineUnix = util.TimeStamp(form.Deadline.Unix())
  205. }
  206. prIssue := &models.Issue{
  207. RepoID: repo.ID,
  208. Index: repo.NextIssueIndex(),
  209. Title: form.Title,
  210. PosterID: ctx.User.ID,
  211. Poster: ctx.User,
  212. MilestoneID: milestoneID,
  213. AssigneeID: assigneeID,
  214. IsPull: true,
  215. Content: form.Body,
  216. DeadlineUnix: deadlineUnix,
  217. }
  218. pr := &models.PullRequest{
  219. HeadRepoID: headRepo.ID,
  220. BaseRepoID: repo.ID,
  221. HeadUserName: headUser.Name,
  222. HeadBranch: headBranch,
  223. BaseBranch: baseBranch,
  224. HeadRepo: headRepo,
  225. BaseRepo: repo,
  226. MergeBase: prInfo.MergeBase,
  227. Type: models.PullRequestGitea,
  228. }
  229. // Get all assignee IDs
  230. assigneeIDs, err := models.MakeIDsFromAPIAssigneesToAdd(form.Assignee, form.Assignees)
  231. if err != nil {
  232. if models.IsErrUserNotExist(err) {
  233. ctx.Error(422, "", fmt.Sprintf("Assignee does not exist: [name: %s]", err))
  234. } else {
  235. ctx.Error(500, "AddAssigneeByName", err)
  236. }
  237. return
  238. }
  239. if err := models.NewPullRequest(repo, prIssue, labelIDs, []string{}, pr, patch, assigneeIDs); err != nil {
  240. if models.IsErrUserDoesNotHaveAccessToRepo(err) {
  241. ctx.Error(400, "UserDoesNotHaveAccessToRepo", err)
  242. return
  243. }
  244. ctx.Error(500, "NewPullRequest", err)
  245. return
  246. } else if err := pr.PushToBaseRepo(); err != nil {
  247. ctx.Error(500, "PushToBaseRepo", err)
  248. return
  249. }
  250. log.Trace("Pull request created: %d/%d", repo.ID, prIssue.ID)
  251. ctx.JSON(201, pr.APIFormat())
  252. }
  253. // EditPullRequest does what it says
  254. func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) {
  255. // swagger:operation PATCH /repos/{owner}/{repo}/pulls/{index} repository repoEditPullRequest
  256. // ---
  257. // summary: Update a pull request
  258. // consumes:
  259. // - application/json
  260. // produces:
  261. // - application/json
  262. // parameters:
  263. // - name: owner
  264. // in: path
  265. // description: owner of the repo
  266. // type: string
  267. // required: true
  268. // - name: repo
  269. // in: path
  270. // description: name of the repo
  271. // type: string
  272. // required: true
  273. // - name: index
  274. // in: path
  275. // description: index of the pull request to edit
  276. // type: integer
  277. // required: true
  278. // - name: body
  279. // in: body
  280. // schema:
  281. // "$ref": "#/definitions/EditPullRequestOption"
  282. // responses:
  283. // "201":
  284. // "$ref": "#/responses/PullRequest"
  285. pr, err := models.GetPullRequestByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
  286. if err != nil {
  287. if models.IsErrPullRequestNotExist(err) {
  288. ctx.Status(404)
  289. } else {
  290. ctx.Error(500, "GetPullRequestByIndex", err)
  291. }
  292. return
  293. }
  294. pr.LoadIssue()
  295. issue := pr.Issue
  296. if !issue.IsPoster(ctx.User.ID) && !ctx.Repo.IsWriter() {
  297. ctx.Status(403)
  298. return
  299. }
  300. if len(form.Title) > 0 {
  301. issue.Title = form.Title
  302. }
  303. if len(form.Body) > 0 {
  304. issue.Content = form.Body
  305. }
  306. // Update Deadline
  307. var deadlineUnix util.TimeStamp
  308. if form.Deadline != nil && !form.Deadline.IsZero() {
  309. deadlineUnix = util.TimeStamp(form.Deadline.Unix())
  310. }
  311. if err := models.UpdateIssueDeadline(issue, deadlineUnix, ctx.User); err != nil {
  312. ctx.Error(500, "UpdateIssueDeadline", err)
  313. return
  314. }
  315. // Add/delete assignees
  316. // Deleting is done the Github way (quote from their api documentation):
  317. // https://developer.github.com/v3/issues/#edit-an-issue
  318. // "assignees" (array): Logins for Users to assign to this issue.
  319. // Pass one or more user logins to replace the set of assignees on this Issue.
  320. // Send an empty array ([]) to clear all assignees from the Issue.
  321. if ctx.Repo.IsWriter() && (form.Assignees != nil || len(form.Assignee) > 0) {
  322. err = models.UpdateAPIAssignee(issue, form.Assignee, form.Assignees, ctx.User)
  323. if err != nil {
  324. if models.IsErrUserNotExist(err) {
  325. ctx.Error(422, "", fmt.Sprintf("Assignee does not exist: [name: %s]", err))
  326. } else {
  327. ctx.Error(500, "UpdateAPIAssignee", err)
  328. }
  329. return
  330. }
  331. }
  332. if ctx.Repo.IsWriter() && form.Milestone != 0 &&
  333. issue.MilestoneID != form.Milestone {
  334. oldMilestoneID := issue.MilestoneID
  335. issue.MilestoneID = form.Milestone
  336. if err = models.ChangeMilestoneAssign(issue, ctx.User, oldMilestoneID); err != nil {
  337. ctx.Error(500, "ChangeMilestoneAssign", err)
  338. return
  339. }
  340. }
  341. if err = models.UpdateIssue(issue); err != nil {
  342. ctx.Error(500, "UpdateIssue", err)
  343. return
  344. }
  345. if form.State != nil {
  346. if err = issue.ChangeStatus(ctx.User, ctx.Repo.Repository, api.StateClosed == api.StateType(*form.State)); err != nil {
  347. if models.IsErrDependenciesLeft(err) {
  348. ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this pull request because it still has open dependencies")
  349. return
  350. }
  351. ctx.Error(500, "ChangeStatus", err)
  352. return
  353. }
  354. }
  355. // Refetch from database
  356. pr, err = models.GetPullRequestByIndex(ctx.Repo.Repository.ID, pr.Index)
  357. if err != nil {
  358. if models.IsErrPullRequestNotExist(err) {
  359. ctx.Status(404)
  360. } else {
  361. ctx.Error(500, "GetPullRequestByIndex", err)
  362. }
  363. return
  364. }
  365. // TODO this should be 200, not 201
  366. ctx.JSON(201, pr.APIFormat())
  367. }
  368. // IsPullRequestMerged checks if a PR exists given an index
  369. func IsPullRequestMerged(ctx *context.APIContext) {
  370. // swagger:operation GET /repos/{owner}/{repo}/pulls/{index}/merge repository repoPullRequestIsMerged
  371. // ---
  372. // summary: Check if a pull request has been merged
  373. // produces:
  374. // - application/json
  375. // parameters:
  376. // - name: owner
  377. // in: path
  378. // description: owner of the repo
  379. // type: string
  380. // required: true
  381. // - name: repo
  382. // in: path
  383. // description: name of the repo
  384. // type: string
  385. // required: true
  386. // - name: index
  387. // in: path
  388. // description: index of the pull request
  389. // type: integer
  390. // required: true
  391. // responses:
  392. // "204":
  393. // description: pull request has been merged
  394. // schema:
  395. // "$ref": "#/responses/empty"
  396. // "404":
  397. // description: pull request has not been merged
  398. // schema:
  399. // "$ref": "#/responses/empty"
  400. pr, err := models.GetPullRequestByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
  401. if err != nil {
  402. if models.IsErrPullRequestNotExist(err) {
  403. ctx.Status(404)
  404. } else {
  405. ctx.Error(500, "GetPullRequestByIndex", err)
  406. }
  407. return
  408. }
  409. if pr.HasMerged {
  410. ctx.Status(204)
  411. }
  412. ctx.Status(404)
  413. }
  414. // MergePullRequest merges a PR given an index
  415. func MergePullRequest(ctx *context.APIContext, form auth.MergePullRequestForm) {
  416. // swagger:operation POST /repos/{owner}/{repo}/pulls/{index}/merge repository repoMergePullRequest
  417. // ---
  418. // summary: Merge a pull request
  419. // produces:
  420. // - application/json
  421. // parameters:
  422. // - name: owner
  423. // in: path
  424. // description: owner of the repo
  425. // type: string
  426. // required: true
  427. // - name: repo
  428. // in: path
  429. // description: name of the repo
  430. // type: string
  431. // required: true
  432. // - name: index
  433. // in: path
  434. // description: index of the pull request to merge
  435. // type: integer
  436. // required: true
  437. // responses:
  438. // "200":
  439. // "$ref": "#/responses/empty"
  440. // "405":
  441. // "$ref": "#/responses/empty"
  442. pr, err := models.GetPullRequestByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
  443. if err != nil {
  444. if models.IsErrPullRequestNotExist(err) {
  445. ctx.NotFound("GetPullRequestByIndex", err)
  446. } else {
  447. ctx.Error(500, "GetPullRequestByIndex", err)
  448. }
  449. return
  450. }
  451. if err = pr.GetHeadRepo(); err != nil {
  452. ctx.ServerError("GetHeadRepo", err)
  453. return
  454. }
  455. pr.LoadIssue()
  456. pr.Issue.Repo = ctx.Repo.Repository
  457. if ctx.IsSigned {
  458. // Update issue-user.
  459. if err = pr.Issue.ReadBy(ctx.User.ID); err != nil {
  460. ctx.Error(500, "ReadBy", err)
  461. return
  462. }
  463. }
  464. if pr.Issue.IsClosed {
  465. ctx.Status(404)
  466. return
  467. }
  468. if !pr.CanAutoMerge() || pr.HasMerged || pr.IsWorkInProgress() {
  469. ctx.Status(405)
  470. return
  471. }
  472. if len(form.Do) == 0 {
  473. form.Do = string(models.MergeStyleMerge)
  474. }
  475. message := strings.TrimSpace(form.MergeTitleField)
  476. if len(message) == 0 {
  477. if models.MergeStyle(form.Do) == models.MergeStyleMerge {
  478. message = pr.GetDefaultMergeMessage()
  479. }
  480. if models.MergeStyle(form.Do) == models.MergeStyleSquash {
  481. message = pr.GetDefaultSquashMessage()
  482. }
  483. }
  484. form.MergeMessageField = strings.TrimSpace(form.MergeMessageField)
  485. if len(form.MergeMessageField) > 0 {
  486. message += "\n\n" + form.MergeMessageField
  487. }
  488. if err := pr.Merge(ctx.User, ctx.Repo.GitRepo, models.MergeStyle(form.Do), message); err != nil {
  489. if models.IsErrInvalidMergeStyle(err) {
  490. ctx.Status(405)
  491. return
  492. }
  493. ctx.Error(500, "Merge", err)
  494. return
  495. }
  496. log.Trace("Pull request merged: %d", pr.ID)
  497. ctx.Status(200)
  498. }
  499. func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) (*models.User, *models.Repository, *git.Repository, *git.PullRequestInfo, string, string) {
  500. baseRepo := ctx.Repo.Repository
  501. // Get compared branches information
  502. // format: <base branch>...[<head repo>:]<head branch>
  503. // base<-head: master...head:feature
  504. // same repo: master...feature
  505. // TODO: Validate form first?
  506. baseBranch := form.Base
  507. var (
  508. headUser *models.User
  509. headBranch string
  510. isSameRepo bool
  511. err error
  512. )
  513. // If there is no head repository, it means pull request between same repository.
  514. headInfos := strings.Split(form.Head, ":")
  515. if len(headInfos) == 1 {
  516. isSameRepo = true
  517. headUser = ctx.Repo.Owner
  518. headBranch = headInfos[0]
  519. } else if len(headInfos) == 2 {
  520. headUser, err = models.GetUserByName(headInfos[0])
  521. if err != nil {
  522. if models.IsErrUserNotExist(err) {
  523. ctx.NotFound("GetUserByName", nil)
  524. } else {
  525. ctx.ServerError("GetUserByName", err)
  526. }
  527. return nil, nil, nil, nil, "", ""
  528. }
  529. headBranch = headInfos[1]
  530. } else {
  531. ctx.Status(404)
  532. return nil, nil, nil, nil, "", ""
  533. }
  534. ctx.Repo.PullRequest.SameRepo = isSameRepo
  535. log.Info("Base branch: %s", baseBranch)
  536. log.Info("Repo path: %s", ctx.Repo.GitRepo.Path)
  537. // Check if base branch is valid.
  538. if !ctx.Repo.GitRepo.IsBranchExist(baseBranch) {
  539. ctx.Status(404)
  540. return nil, nil, nil, nil, "", ""
  541. }
  542. // Check if current user has fork of repository or in the same repository.
  543. headRepo, has := models.HasForkedRepo(headUser.ID, baseRepo.ID)
  544. if !has && !isSameRepo {
  545. log.Trace("parseCompareInfo[%d]: does not have fork or in same repository", baseRepo.ID)
  546. ctx.Status(404)
  547. return nil, nil, nil, nil, "", ""
  548. }
  549. var headGitRepo *git.Repository
  550. if isSameRepo {
  551. headRepo = ctx.Repo.Repository
  552. headGitRepo = ctx.Repo.GitRepo
  553. } else {
  554. headGitRepo, err = git.OpenRepository(models.RepoPath(headUser.Name, headRepo.Name))
  555. if err != nil {
  556. ctx.Error(500, "OpenRepository", err)
  557. return nil, nil, nil, nil, "", ""
  558. }
  559. }
  560. if !ctx.User.IsWriterOfRepo(headRepo) && !ctx.User.IsAdmin {
  561. log.Trace("ParseCompareInfo[%d]: does not have write access or site admin", baseRepo.ID)
  562. ctx.Status(404)
  563. return nil, nil, nil, nil, "", ""
  564. }
  565. // Check if head branch is valid.
  566. if !headGitRepo.IsBranchExist(headBranch) {
  567. ctx.Status(404)
  568. return nil, nil, nil, nil, "", ""
  569. }
  570. prInfo, err := headGitRepo.GetPullRequestInfo(models.RepoPath(baseRepo.Owner.Name, baseRepo.Name), baseBranch, headBranch)
  571. if err != nil {
  572. ctx.Error(500, "GetPullRequestInfo", err)
  573. return nil, nil, nil, nil, "", ""
  574. }
  575. return headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch
  576. }