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 16 kB

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