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

API add/generalize pagination (#9452) * paginate results * fixed deadlock * prevented breaking change * updated swagger * go fmt * fixed find topic * go mod tidy * go mod vendor with go1.13.5 * fixed repo find topics * fixed unit test * added Limit method to Engine struct; use engine variable when provided; fixed gitignore * use ItemsPerPage for default pagesize; fix GetWatchers, getOrgUsersByOrgID and GetStargazers; fix GetAllCommits headers; reverted some changed behaviors * set Page value on Home route * improved memory allocations * fixed response headers * removed logfiles * fixed import order * import order * improved swagger * added function to get models.ListOptions from context * removed pagesize diff on unit test * fixed imports * removed unnecessary struct field * fixed go fmt * scoped PR * code improvements * code improvements * go mod tidy * fixed import order * fixed commit statuses session * fixed files headers * fixed headers; added pagination for notifications * go mod tidy * go fmt * removed Private from user search options; added setting.UI.IssuePagingNum as default valeu on repo's issues list * Apply suggestions from code review Co-Authored-By: 6543 <6543@obermui.de> Co-Authored-By: zeripath <art27@cantab.net> * fixed build error * CI.restart() * fixed merge conflicts resolve * fixed conflicts resolve * improved FindTrackedTimesOptions.ToOptions() method * added backwards compatibility on ListReleases request; fixed issue tracked time ToSession * fixed build error; fixed swagger template * fixed swagger template * fixed ListReleases backwards compatibility * added page to user search route Co-authored-by: techknowlogick <matti@mdranta.net> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: zeripath <art27@cantab.net>
5 years ago
API add/generalize pagination (#9452) * paginate results * fixed deadlock * prevented breaking change * updated swagger * go fmt * fixed find topic * go mod tidy * go mod vendor with go1.13.5 * fixed repo find topics * fixed unit test * added Limit method to Engine struct; use engine variable when provided; fixed gitignore * use ItemsPerPage for default pagesize; fix GetWatchers, getOrgUsersByOrgID and GetStargazers; fix GetAllCommits headers; reverted some changed behaviors * set Page value on Home route * improved memory allocations * fixed response headers * removed logfiles * fixed import order * import order * improved swagger * added function to get models.ListOptions from context * removed pagesize diff on unit test * fixed imports * removed unnecessary struct field * fixed go fmt * scoped PR * code improvements * code improvements * go mod tidy * fixed import order * fixed commit statuses session * fixed files headers * fixed headers; added pagination for notifications * go mod tidy * go fmt * removed Private from user search options; added setting.UI.IssuePagingNum as default valeu on repo's issues list * Apply suggestions from code review Co-Authored-By: 6543 <6543@obermui.de> Co-Authored-By: zeripath <art27@cantab.net> * fixed build error * CI.restart() * fixed merge conflicts resolve * fixed conflicts resolve * improved FindTrackedTimesOptions.ToOptions() method * added backwards compatibility on ListReleases request; fixed issue tracked time ToSession * fixed build error; fixed swagger template * fixed swagger template * fixed ListReleases backwards compatibility * added page to user search route Co-authored-by: techknowlogick <matti@mdranta.net> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: zeripath <art27@cantab.net>
5 years ago
API add/generalize pagination (#9452) * paginate results * fixed deadlock * prevented breaking change * updated swagger * go fmt * fixed find topic * go mod tidy * go mod vendor with go1.13.5 * fixed repo find topics * fixed unit test * added Limit method to Engine struct; use engine variable when provided; fixed gitignore * use ItemsPerPage for default pagesize; fix GetWatchers, getOrgUsersByOrgID and GetStargazers; fix GetAllCommits headers; reverted some changed behaviors * set Page value on Home route * improved memory allocations * fixed response headers * removed logfiles * fixed import order * import order * improved swagger * added function to get models.ListOptions from context * removed pagesize diff on unit test * fixed imports * removed unnecessary struct field * fixed go fmt * scoped PR * code improvements * code improvements * go mod tidy * fixed import order * fixed commit statuses session * fixed files headers * fixed headers; added pagination for notifications * go mod tidy * go fmt * removed Private from user search options; added setting.UI.IssuePagingNum as default valeu on repo's issues list * Apply suggestions from code review Co-Authored-By: 6543 <6543@obermui.de> Co-Authored-By: zeripath <art27@cantab.net> * fixed build error * CI.restart() * fixed merge conflicts resolve * fixed conflicts resolve * improved FindTrackedTimesOptions.ToOptions() method * added backwards compatibility on ListReleases request; fixed issue tracked time ToSession * fixed build error; fixed swagger template * fixed swagger template * fixed ListReleases backwards compatibility * added page to user search route Co-authored-by: techknowlogick <matti@mdranta.net> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: zeripath <art27@cantab.net>
5 years ago
API add/generalize pagination (#9452) * paginate results * fixed deadlock * prevented breaking change * updated swagger * go fmt * fixed find topic * go mod tidy * go mod vendor with go1.13.5 * fixed repo find topics * fixed unit test * added Limit method to Engine struct; use engine variable when provided; fixed gitignore * use ItemsPerPage for default pagesize; fix GetWatchers, getOrgUsersByOrgID and GetStargazers; fix GetAllCommits headers; reverted some changed behaviors * set Page value on Home route * improved memory allocations * fixed response headers * removed logfiles * fixed import order * import order * improved swagger * added function to get models.ListOptions from context * removed pagesize diff on unit test * fixed imports * removed unnecessary struct field * fixed go fmt * scoped PR * code improvements * code improvements * go mod tidy * fixed import order * fixed commit statuses session * fixed files headers * fixed headers; added pagination for notifications * go mod tidy * go fmt * removed Private from user search options; added setting.UI.IssuePagingNum as default valeu on repo's issues list * Apply suggestions from code review Co-Authored-By: 6543 <6543@obermui.de> Co-Authored-By: zeripath <art27@cantab.net> * fixed build error * CI.restart() * fixed merge conflicts resolve * fixed conflicts resolve * improved FindTrackedTimesOptions.ToOptions() method * added backwards compatibility on ListReleases request; fixed issue tracked time ToSession * fixed build error; fixed swagger template * fixed swagger template * fixed ListReleases backwards compatibility * added page to user search route Co-authored-by: techknowlogick <matti@mdranta.net> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: zeripath <art27@cantab.net>
5 years ago
API add/generalize pagination (#9452) * paginate results * fixed deadlock * prevented breaking change * updated swagger * go fmt * fixed find topic * go mod tidy * go mod vendor with go1.13.5 * fixed repo find topics * fixed unit test * added Limit method to Engine struct; use engine variable when provided; fixed gitignore * use ItemsPerPage for default pagesize; fix GetWatchers, getOrgUsersByOrgID and GetStargazers; fix GetAllCommits headers; reverted some changed behaviors * set Page value on Home route * improved memory allocations * fixed response headers * removed logfiles * fixed import order * import order * improved swagger * added function to get models.ListOptions from context * removed pagesize diff on unit test * fixed imports * removed unnecessary struct field * fixed go fmt * scoped PR * code improvements * code improvements * go mod tidy * fixed import order * fixed commit statuses session * fixed files headers * fixed headers; added pagination for notifications * go mod tidy * go fmt * removed Private from user search options; added setting.UI.IssuePagingNum as default valeu on repo's issues list * Apply suggestions from code review Co-Authored-By: 6543 <6543@obermui.de> Co-Authored-By: zeripath <art27@cantab.net> * fixed build error * CI.restart() * fixed merge conflicts resolve * fixed conflicts resolve * improved FindTrackedTimesOptions.ToOptions() method * added backwards compatibility on ListReleases request; fixed issue tracked time ToSession * fixed build error; fixed swagger template * fixed swagger template * fixed ListReleases backwards compatibility * added page to user search route Co-authored-by: techknowlogick <matti@mdranta.net> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: zeripath <art27@cantab.net>
5 years ago
API add/generalize pagination (#9452) * paginate results * fixed deadlock * prevented breaking change * updated swagger * go fmt * fixed find topic * go mod tidy * go mod vendor with go1.13.5 * fixed repo find topics * fixed unit test * added Limit method to Engine struct; use engine variable when provided; fixed gitignore * use ItemsPerPage for default pagesize; fix GetWatchers, getOrgUsersByOrgID and GetStargazers; fix GetAllCommits headers; reverted some changed behaviors * set Page value on Home route * improved memory allocations * fixed response headers * removed logfiles * fixed import order * import order * improved swagger * added function to get models.ListOptions from context * removed pagesize diff on unit test * fixed imports * removed unnecessary struct field * fixed go fmt * scoped PR * code improvements * code improvements * go mod tidy * fixed import order * fixed commit statuses session * fixed files headers * fixed headers; added pagination for notifications * go mod tidy * go fmt * removed Private from user search options; added setting.UI.IssuePagingNum as default valeu on repo's issues list * Apply suggestions from code review Co-Authored-By: 6543 <6543@obermui.de> Co-Authored-By: zeripath <art27@cantab.net> * fixed build error * CI.restart() * fixed merge conflicts resolve * fixed conflicts resolve * improved FindTrackedTimesOptions.ToOptions() method * added backwards compatibility on ListReleases request; fixed issue tracked time ToSession * fixed build error; fixed swagger template * fixed swagger template * fixed ListReleases backwards compatibility * added page to user search route Co-authored-by: techknowlogick <matti@mdranta.net> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: zeripath <art27@cantab.net>
5 years ago
Issue due date (#3794) * Started adding deadline to ui * Implemented basic issue due date managing * Improved UI for due date managing * Added at least write access to the repo in order to modify issue due dates * Ui improvements * Added issue comments creation when adding/modifying/removing a due date * Show due date in issue list * Added api support for issue due dates * Fixed lint suggestions * Added deadline to sdk * Updated css * Added support for adding/modifiying deadlines for pull requests via api * Fixed comments not created when updating or removing a deadline * update sdk (will do properly once go-gitea/go-sdk#103 is merged) * enhanced updateIssueDeadline * Removed unnessecary Issue.DeadlineString * UI improvements * Small improvments to comment creation + ui & validation improvements * Check if an issue is overdue is now a seperate function * Updated go-sdk with govendor as it was merged * Simplified isOverdue method * removed unessecary deadline to 0 set * Update swagger definitions * Added missing return * Added an explanary comment * Improved updateIssueDeadline method so it'll only update `deadline_unix` * Small changes and improvements * no need to explicitly load the issue when updating a deadline, just use whats already there * small optimisations * Added check if a deadline was modified before updating it * Moved comment creating logic into its own function * Code cleanup for creating deadline comment * locale improvement * When modifying a deadline, the old deadline is saved with the comment * small improvments to xorm session handling when updating an issue deadline + style nitpicks * style nitpicks * Moved checking for if the user has write acces to middleware
7 years ago
Issue due date (#3794) * Started adding deadline to ui * Implemented basic issue due date managing * Improved UI for due date managing * Added at least write access to the repo in order to modify issue due dates * Ui improvements * Added issue comments creation when adding/modifying/removing a due date * Show due date in issue list * Added api support for issue due dates * Fixed lint suggestions * Added deadline to sdk * Updated css * Added support for adding/modifiying deadlines for pull requests via api * Fixed comments not created when updating or removing a deadline * update sdk (will do properly once go-gitea/go-sdk#103 is merged) * enhanced updateIssueDeadline * Removed unnessecary Issue.DeadlineString * UI improvements * Small improvments to comment creation + ui & validation improvements * Check if an issue is overdue is now a seperate function * Updated go-sdk with govendor as it was merged * Simplified isOverdue method * removed unessecary deadline to 0 set * Update swagger definitions * Added missing return * Added an explanary comment * Improved updateIssueDeadline method so it'll only update `deadline_unix` * Small changes and improvements * no need to explicitly load the issue when updating a deadline, just use whats already there * small optimisations * Added check if a deadline was modified before updating it * Moved comment creating logic into its own function * Code cleanup for creating deadline comment * locale improvement * When modifying a deadline, the old deadline is saved with the comment * small improvments to xorm session handling when updating an issue deadline + style nitpicks * style nitpicks * Moved checking for if the user has write acces to middleware
7 years ago
Issue due date (#3794) * Started adding deadline to ui * Implemented basic issue due date managing * Improved UI for due date managing * Added at least write access to the repo in order to modify issue due dates * Ui improvements * Added issue comments creation when adding/modifying/removing a due date * Show due date in issue list * Added api support for issue due dates * Fixed lint suggestions * Added deadline to sdk * Updated css * Added support for adding/modifiying deadlines for pull requests via api * Fixed comments not created when updating or removing a deadline * update sdk (will do properly once go-gitea/go-sdk#103 is merged) * enhanced updateIssueDeadline * Removed unnessecary Issue.DeadlineString * UI improvements * Small improvments to comment creation + ui & validation improvements * Check if an issue is overdue is now a seperate function * Updated go-sdk with govendor as it was merged * Simplified isOverdue method * removed unessecary deadline to 0 set * Update swagger definitions * Added missing return * Added an explanary comment * Improved updateIssueDeadline method so it'll only update `deadline_unix` * Small changes and improvements * no need to explicitly load the issue when updating a deadline, just use whats already there * small optimisations * Added check if a deadline was modified before updating it * Moved comment creating logic into its own function * Code cleanup for creating deadline comment * locale improvement * When modifying a deadline, the old deadline is saved with the comment * small improvments to xorm session handling when updating an issue deadline + style nitpicks * style nitpicks * Moved checking for if the user has write acces to middleware
7 years ago
Issue due date (#3794) * Started adding deadline to ui * Implemented basic issue due date managing * Improved UI for due date managing * Added at least write access to the repo in order to modify issue due dates * Ui improvements * Added issue comments creation when adding/modifying/removing a due date * Show due date in issue list * Added api support for issue due dates * Fixed lint suggestions * Added deadline to sdk * Updated css * Added support for adding/modifiying deadlines for pull requests via api * Fixed comments not created when updating or removing a deadline * update sdk (will do properly once go-gitea/go-sdk#103 is merged) * enhanced updateIssueDeadline * Removed unnessecary Issue.DeadlineString * UI improvements * Small improvments to comment creation + ui & validation improvements * Check if an issue is overdue is now a seperate function * Updated go-sdk with govendor as it was merged * Simplified isOverdue method * removed unessecary deadline to 0 set * Update swagger definitions * Added missing return * Added an explanary comment * Improved updateIssueDeadline method so it'll only update `deadline_unix` * Small changes and improvements * no need to explicitly load the issue when updating a deadline, just use whats already there * small optimisations * Added check if a deadline was modified before updating it * Moved comment creating logic into its own function * Code cleanup for creating deadline comment * locale improvement * When modifying a deadline, the old deadline is saved with the comment * small improvments to xorm session handling when updating an issue deadline + style nitpicks * style nitpicks * Moved checking for if the user has write acces to middleware
7 years ago
Compare branches, commits and tags with each other (#6991) * Supports tags when comparing commits or branches Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Hide headline when only comparing and don't load unused data Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Merges compare logics to allow comparing branches, commits and tags with eachother Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Display branch or tag instead of commit when used for comparing Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Show pull request form after click on button Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Transfers relevant pull.go changes from master to compare.go Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Fixes error when comparing forks against a commit or tag Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Removes console.log from JavaScript file Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Show icon next to commit reference when comparing branch or tag Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Updates css file Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Fixes import order * Renames template variable * Update routers/repo/compare.go Co-Authored-By: zeripath <art27@cantab.net> * Update from master Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Allow short-shas in compare * Renames prInfo to compareInfo Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Check PR permissions only if compare is pull request Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Adjusts comment Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Use compareInfo instead of prInfo
6 years ago
Compare branches, commits and tags with each other (#6991) * Supports tags when comparing commits or branches Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Hide headline when only comparing and don't load unused data Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Merges compare logics to allow comparing branches, commits and tags with eachother Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Display branch or tag instead of commit when used for comparing Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Show pull request form after click on button Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Transfers relevant pull.go changes from master to compare.go Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Fixes error when comparing forks against a commit or tag Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Removes console.log from JavaScript file Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Show icon next to commit reference when comparing branch or tag Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Updates css file Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Fixes import order * Renames template variable * Update routers/repo/compare.go Co-Authored-By: zeripath <art27@cantab.net> * Update from master Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Allow short-shas in compare * Renames prInfo to compareInfo Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Check PR permissions only if compare is pull request Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Adjusts comment Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Use compareInfo instead of prInfo
6 years ago
Compare branches, commits and tags with each other (#6991) * Supports tags when comparing commits or branches Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Hide headline when only comparing and don't load unused data Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Merges compare logics to allow comparing branches, commits and tags with eachother Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Display branch or tag instead of commit when used for comparing Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Show pull request form after click on button Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Transfers relevant pull.go changes from master to compare.go Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Fixes error when comparing forks against a commit or tag Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Removes console.log from JavaScript file Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Show icon next to commit reference when comparing branch or tag Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Updates css file Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Fixes import order * Renames template variable * Update routers/repo/compare.go Co-Authored-By: zeripath <art27@cantab.net> * Update from master Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Allow short-shas in compare * Renames prInfo to compareInfo Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Check PR permissions only if compare is pull request Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Adjusts comment Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Use compareInfo instead of prInfo
6 years ago
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067
  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
  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.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link")
  110. ctx.JSON(http.StatusOK, &apiPrs)
  111. }
  112. // GetPullRequest returns a single PR based on index
  113. func GetPullRequest(ctx *context.APIContext) {
  114. // swagger:operation GET /repos/{owner}/{repo}/pulls/{index} repository repoGetPullRequest
  115. // ---
  116. // summary: Get a pull request
  117. // produces:
  118. // - application/json
  119. // parameters:
  120. // - name: owner
  121. // in: path
  122. // description: owner of the repo
  123. // type: string
  124. // required: true
  125. // - name: repo
  126. // in: path
  127. // description: name of the repo
  128. // type: string
  129. // required: true
  130. // - name: index
  131. // in: path
  132. // description: index of the pull request to get
  133. // type: integer
  134. // format: int64
  135. // required: true
  136. // responses:
  137. // "200":
  138. // "$ref": "#/responses/PullRequest"
  139. // "404":
  140. // "$ref": "#/responses/notFound"
  141. pr, err := models.GetPullRequestByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
  142. if err != nil {
  143. if models.IsErrPullRequestNotExist(err) {
  144. ctx.NotFound()
  145. } else {
  146. ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
  147. }
  148. return
  149. }
  150. if err = pr.LoadBaseRepo(); err != nil {
  151. ctx.Error(http.StatusInternalServerError, "LoadBaseRepo", err)
  152. return
  153. }
  154. if err = pr.LoadHeadRepo(); err != nil {
  155. ctx.Error(http.StatusInternalServerError, "LoadHeadRepo", err)
  156. return
  157. }
  158. ctx.JSON(http.StatusOK, convert.ToAPIPullRequest(pr))
  159. }
  160. // DownloadPullDiff render a pull's raw diff
  161. func DownloadPullDiff(ctx *context.APIContext) {
  162. // swagger:operation GET /repos/{owner}/{repo}/pulls/{index}.diff repository repoDownloadPullDiff
  163. // ---
  164. // summary: Get a pull request diff
  165. // produces:
  166. // - text/plain
  167. // parameters:
  168. // - name: owner
  169. // in: path
  170. // description: owner of the repo
  171. // type: string
  172. // required: true
  173. // - name: repo
  174. // in: path
  175. // description: name of the repo
  176. // type: string
  177. // required: true
  178. // - name: index
  179. // in: path
  180. // description: index of the pull request to get
  181. // type: integer
  182. // format: int64
  183. // required: true
  184. // responses:
  185. // "200":
  186. // "$ref": "#/responses/string"
  187. // "404":
  188. // "$ref": "#/responses/notFound"
  189. DownloadPullDiffOrPatch(ctx, false)
  190. }
  191. // DownloadPullPatch render a pull's raw patch
  192. func DownloadPullPatch(ctx *context.APIContext) {
  193. // swagger:operation GET /repos/{owner}/{repo}/pulls/{index}.patch repository repoDownloadPullPatch
  194. // ---
  195. // summary: Get a pull request patch file
  196. // produces:
  197. // - text/plain
  198. // parameters:
  199. // - name: owner
  200. // in: path
  201. // description: owner of the repo
  202. // type: string
  203. // required: true
  204. // - name: repo
  205. // in: path
  206. // description: name of the repo
  207. // type: string
  208. // required: true
  209. // - name: index
  210. // in: path
  211. // description: index of the pull request to get
  212. // type: integer
  213. // format: int64
  214. // required: true
  215. // responses:
  216. // "200":
  217. // "$ref": "#/responses/string"
  218. // "404":
  219. // "$ref": "#/responses/notFound"
  220. DownloadPullDiffOrPatch(ctx, true)
  221. }
  222. // DownloadPullDiffOrPatch render a pull's raw diff or patch
  223. func DownloadPullDiffOrPatch(ctx *context.APIContext, patch bool) {
  224. pr, err := models.GetPullRequestByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
  225. if err != nil {
  226. if models.IsErrPullRequestNotExist(err) {
  227. ctx.NotFound()
  228. } else {
  229. ctx.InternalServerError(err)
  230. }
  231. return
  232. }
  233. if err := pull_service.DownloadDiffOrPatch(pr, ctx, patch); err != nil {
  234. ctx.InternalServerError(err)
  235. return
  236. }
  237. }
  238. // CreatePullRequest does what it says
  239. func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption) {
  240. // swagger:operation POST /repos/{owner}/{repo}/pulls repository repoCreatePullRequest
  241. // ---
  242. // summary: Create a pull request
  243. // consumes:
  244. // - application/json
  245. // produces:
  246. // - application/json
  247. // parameters:
  248. // - name: owner
  249. // in: path
  250. // description: owner of the repo
  251. // type: string
  252. // required: true
  253. // - name: repo
  254. // in: path
  255. // description: name of the repo
  256. // type: string
  257. // required: true
  258. // - name: body
  259. // in: body
  260. // schema:
  261. // "$ref": "#/definitions/CreatePullRequestOption"
  262. // responses:
  263. // "201":
  264. // "$ref": "#/responses/PullRequest"
  265. // "409":
  266. // "$ref": "#/responses/error"
  267. // "422":
  268. // "$ref": "#/responses/validationError"
  269. var (
  270. repo = ctx.Repo.Repository
  271. labelIDs []int64
  272. assigneeID int64
  273. milestoneID int64
  274. )
  275. // Get repo/branch information
  276. _, headRepo, headGitRepo, compareInfo, baseBranch, headBranch := parseCompareInfo(ctx, form)
  277. if ctx.Written() {
  278. return
  279. }
  280. defer headGitRepo.Close()
  281. // Check if another PR exists with the same targets
  282. existingPr, err := models.GetUnmergedPullRequest(headRepo.ID, ctx.Repo.Repository.ID, headBranch, baseBranch)
  283. if err != nil {
  284. if !models.IsErrPullRequestNotExist(err) {
  285. ctx.Error(http.StatusInternalServerError, "GetUnmergedPullRequest", err)
  286. return
  287. }
  288. } else {
  289. err = models.ErrPullRequestAlreadyExists{
  290. ID: existingPr.ID,
  291. IssueID: existingPr.Index,
  292. HeadRepoID: existingPr.HeadRepoID,
  293. BaseRepoID: existingPr.BaseRepoID,
  294. HeadBranch: existingPr.HeadBranch,
  295. BaseBranch: existingPr.BaseBranch,
  296. }
  297. ctx.Error(http.StatusConflict, "GetUnmergedPullRequest", err)
  298. return
  299. }
  300. if len(form.Labels) > 0 {
  301. labels, err := models.GetLabelsInRepoByIDs(ctx.Repo.Repository.ID, form.Labels)
  302. if err != nil {
  303. ctx.Error(http.StatusInternalServerError, "GetLabelsInRepoByIDs", err)
  304. return
  305. }
  306. labelIDs = make([]int64, len(form.Labels))
  307. orgLabelIDs := make([]int64, len(form.Labels))
  308. for i := range labels {
  309. labelIDs[i] = labels[i].ID
  310. }
  311. if ctx.Repo.Owner.IsOrganization() {
  312. orgLabels, err := models.GetLabelsInOrgByIDs(ctx.Repo.Owner.ID, form.Labels)
  313. if err != nil {
  314. ctx.Error(http.StatusInternalServerError, "GetLabelsInOrgByIDs", err)
  315. return
  316. }
  317. for i := range orgLabels {
  318. orgLabelIDs[i] = orgLabels[i].ID
  319. }
  320. }
  321. labelIDs = append(labelIDs, orgLabelIDs...)
  322. }
  323. if form.Milestone > 0 {
  324. milestone, err := models.GetMilestoneByRepoID(ctx.Repo.Repository.ID, milestoneID)
  325. if err != nil {
  326. if models.IsErrMilestoneNotExist(err) {
  327. ctx.NotFound()
  328. } else {
  329. ctx.Error(http.StatusInternalServerError, "GetMilestoneByRepoID", err)
  330. }
  331. return
  332. }
  333. milestoneID = milestone.ID
  334. }
  335. var deadlineUnix timeutil.TimeStamp
  336. if form.Deadline != nil {
  337. deadlineUnix = timeutil.TimeStamp(form.Deadline.Unix())
  338. }
  339. prIssue := &models.Issue{
  340. RepoID: repo.ID,
  341. Title: form.Title,
  342. PosterID: ctx.User.ID,
  343. Poster: ctx.User,
  344. MilestoneID: milestoneID,
  345. AssigneeID: assigneeID,
  346. IsPull: true,
  347. Content: form.Body,
  348. DeadlineUnix: deadlineUnix,
  349. }
  350. pr := &models.PullRequest{
  351. HeadRepoID: headRepo.ID,
  352. BaseRepoID: repo.ID,
  353. HeadBranch: headBranch,
  354. BaseBranch: baseBranch,
  355. HeadRepo: headRepo,
  356. BaseRepo: repo,
  357. MergeBase: compareInfo.MergeBase,
  358. Type: models.PullRequestGitea,
  359. }
  360. // Get all assignee IDs
  361. assigneeIDs, err := models.MakeIDsFromAPIAssigneesToAdd(form.Assignee, form.Assignees)
  362. if err != nil {
  363. if models.IsErrUserNotExist(err) {
  364. ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("Assignee does not exist: [name: %s]", err))
  365. } else {
  366. ctx.Error(http.StatusInternalServerError, "AddAssigneeByName", err)
  367. }
  368. return
  369. }
  370. // Check if the passed assignees is assignable
  371. for _, aID := range assigneeIDs {
  372. assignee, err := models.GetUserByID(aID)
  373. if err != nil {
  374. ctx.Error(http.StatusInternalServerError, "GetUserByID", err)
  375. return
  376. }
  377. valid, err := models.CanBeAssigned(assignee, repo, true)
  378. if err != nil {
  379. ctx.Error(http.StatusInternalServerError, "canBeAssigned", err)
  380. return
  381. }
  382. if !valid {
  383. ctx.Error(http.StatusUnprocessableEntity, "canBeAssigned", models.ErrUserDoesNotHaveAccessToRepo{UserID: aID, RepoName: repo.Name})
  384. return
  385. }
  386. }
  387. if err := pull_service.NewPullRequest(repo, prIssue, labelIDs, []string{}, pr, assigneeIDs); err != nil {
  388. if models.IsErrUserDoesNotHaveAccessToRepo(err) {
  389. ctx.Error(http.StatusBadRequest, "UserDoesNotHaveAccessToRepo", err)
  390. return
  391. }
  392. ctx.Error(http.StatusInternalServerError, "NewPullRequest", err)
  393. return
  394. }
  395. log.Trace("Pull request created: %d/%d", repo.ID, prIssue.ID)
  396. ctx.JSON(http.StatusCreated, convert.ToAPIPullRequest(pr))
  397. }
  398. // EditPullRequest does what it says
  399. func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) {
  400. // swagger:operation PATCH /repos/{owner}/{repo}/pulls/{index} repository repoEditPullRequest
  401. // ---
  402. // summary: Update a pull request. If using deadline only the date will be taken into account, and time of day ignored.
  403. // consumes:
  404. // - application/json
  405. // produces:
  406. // - application/json
  407. // parameters:
  408. // - name: owner
  409. // in: path
  410. // description: owner of the repo
  411. // type: string
  412. // required: true
  413. // - name: repo
  414. // in: path
  415. // description: name of the repo
  416. // type: string
  417. // required: true
  418. // - name: index
  419. // in: path
  420. // description: index of the pull request to edit
  421. // type: integer
  422. // format: int64
  423. // required: true
  424. // - name: body
  425. // in: body
  426. // schema:
  427. // "$ref": "#/definitions/EditPullRequestOption"
  428. // responses:
  429. // "201":
  430. // "$ref": "#/responses/PullRequest"
  431. // "403":
  432. // "$ref": "#/responses/forbidden"
  433. // "409":
  434. // "$ref": "#/responses/error"
  435. // "412":
  436. // "$ref": "#/responses/error"
  437. // "422":
  438. // "$ref": "#/responses/validationError"
  439. pr, err := models.GetPullRequestByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
  440. if err != nil {
  441. if models.IsErrPullRequestNotExist(err) {
  442. ctx.NotFound()
  443. } else {
  444. ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
  445. }
  446. return
  447. }
  448. err = pr.LoadIssue()
  449. if err != nil {
  450. ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
  451. return
  452. }
  453. issue := pr.Issue
  454. issue.Repo = ctx.Repo.Repository
  455. if !issue.IsPoster(ctx.User.ID) && !ctx.Repo.CanWrite(models.UnitTypePullRequests) {
  456. ctx.Status(http.StatusForbidden)
  457. return
  458. }
  459. oldTitle := issue.Title
  460. if len(form.Title) > 0 {
  461. issue.Title = form.Title
  462. }
  463. if len(form.Body) > 0 {
  464. issue.Content = form.Body
  465. }
  466. // Update or remove deadline if set
  467. if form.Deadline != nil || form.RemoveDeadline != nil {
  468. var deadlineUnix timeutil.TimeStamp
  469. if (form.RemoveDeadline == nil || !*form.RemoveDeadline) && !form.Deadline.IsZero() {
  470. deadline := time.Date(form.Deadline.Year(), form.Deadline.Month(), form.Deadline.Day(),
  471. 23, 59, 59, 0, form.Deadline.Location())
  472. deadlineUnix = timeutil.TimeStamp(deadline.Unix())
  473. }
  474. if err := models.UpdateIssueDeadline(issue, deadlineUnix, ctx.User); err != nil {
  475. ctx.Error(http.StatusInternalServerError, "UpdateIssueDeadline", err)
  476. return
  477. }
  478. issue.DeadlineUnix = deadlineUnix
  479. }
  480. // Add/delete assignees
  481. // Deleting is done the GitHub way (quote from their api documentation):
  482. // https://developer.github.com/v3/issues/#edit-an-issue
  483. // "assignees" (array): Logins for Users to assign to this issue.
  484. // Pass one or more user logins to replace the set of assignees on this Issue.
  485. // Send an empty array ([]) to clear all assignees from the Issue.
  486. if ctx.Repo.CanWrite(models.UnitTypePullRequests) && (form.Assignees != nil || len(form.Assignee) > 0) {
  487. err = issue_service.UpdateAssignees(issue, form.Assignee, form.Assignees, ctx.User)
  488. if err != nil {
  489. if models.IsErrUserNotExist(err) {
  490. ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("Assignee does not exist: [name: %s]", err))
  491. } else {
  492. ctx.Error(http.StatusInternalServerError, "UpdateAssignees", err)
  493. }
  494. return
  495. }
  496. }
  497. if ctx.Repo.CanWrite(models.UnitTypePullRequests) && form.Milestone != 0 &&
  498. issue.MilestoneID != form.Milestone {
  499. oldMilestoneID := issue.MilestoneID
  500. issue.MilestoneID = form.Milestone
  501. if err = issue_service.ChangeMilestoneAssign(issue, ctx.User, oldMilestoneID); err != nil {
  502. ctx.Error(http.StatusInternalServerError, "ChangeMilestoneAssign", err)
  503. return
  504. }
  505. }
  506. if ctx.Repo.CanWrite(models.UnitTypePullRequests) && form.Labels != nil {
  507. labels, err := models.GetLabelsInRepoByIDs(ctx.Repo.Repository.ID, form.Labels)
  508. if err != nil {
  509. ctx.Error(http.StatusInternalServerError, "GetLabelsInRepoByIDsError", err)
  510. return
  511. }
  512. if ctx.Repo.Owner.IsOrganization() {
  513. orgLabels, err := models.GetLabelsInOrgByIDs(ctx.Repo.Owner.ID, form.Labels)
  514. if err != nil {
  515. ctx.Error(http.StatusInternalServerError, "GetLabelsInOrgByIDs", err)
  516. return
  517. }
  518. labels = append(labels, orgLabels...)
  519. }
  520. if err = issue.ReplaceLabels(labels, ctx.User); err != nil {
  521. ctx.Error(http.StatusInternalServerError, "ReplaceLabelsError", err)
  522. return
  523. }
  524. }
  525. if form.State != nil {
  526. issue.IsClosed = (api.StateClosed == api.StateType(*form.State))
  527. }
  528. statusChangeComment, titleChanged, err := models.UpdateIssueByAPI(issue, ctx.User)
  529. if err != nil {
  530. if models.IsErrDependenciesLeft(err) {
  531. ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this pull request because it still has open dependencies")
  532. return
  533. }
  534. ctx.Error(http.StatusInternalServerError, "UpdateIssueByAPI", err)
  535. return
  536. }
  537. if titleChanged {
  538. notification.NotifyIssueChangeTitle(ctx.User, issue, oldTitle)
  539. }
  540. if statusChangeComment != nil {
  541. notification.NotifyIssueChangeStatus(ctx.User, issue, statusChangeComment, issue.IsClosed)
  542. }
  543. // change pull target branch
  544. if len(form.Base) != 0 && form.Base != pr.BaseBranch {
  545. if !ctx.Repo.GitRepo.IsBranchExist(form.Base) {
  546. ctx.Error(http.StatusNotFound, "NewBaseBranchNotExist", fmt.Errorf("new base '%s' not exist", form.Base))
  547. return
  548. }
  549. if err := pull_service.ChangeTargetBranch(pr, ctx.User, form.Base); err != nil {
  550. if models.IsErrPullRequestAlreadyExists(err) {
  551. ctx.Error(http.StatusConflict, "IsErrPullRequestAlreadyExists", err)
  552. return
  553. } else if models.IsErrIssueIsClosed(err) {
  554. ctx.Error(http.StatusUnprocessableEntity, "IsErrIssueIsClosed", err)
  555. return
  556. } else if models.IsErrPullRequestHasMerged(err) {
  557. ctx.Error(http.StatusConflict, "IsErrPullRequestHasMerged", err)
  558. return
  559. } else {
  560. ctx.InternalServerError(err)
  561. }
  562. return
  563. }
  564. notification.NotifyPullRequestChangeTargetBranch(ctx.User, pr, form.Base)
  565. }
  566. // Refetch from database
  567. pr, err = models.GetPullRequestByIndex(ctx.Repo.Repository.ID, pr.Index)
  568. if err != nil {
  569. if models.IsErrPullRequestNotExist(err) {
  570. ctx.NotFound()
  571. } else {
  572. ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
  573. }
  574. return
  575. }
  576. // TODO this should be 200, not 201
  577. ctx.JSON(http.StatusCreated, convert.ToAPIPullRequest(pr))
  578. }
  579. // IsPullRequestMerged checks if a PR exists given an index
  580. func IsPullRequestMerged(ctx *context.APIContext) {
  581. // swagger:operation GET /repos/{owner}/{repo}/pulls/{index}/merge repository repoPullRequestIsMerged
  582. // ---
  583. // summary: Check if a pull request has been merged
  584. // produces:
  585. // - application/json
  586. // parameters:
  587. // - name: owner
  588. // in: path
  589. // description: owner of the repo
  590. // type: string
  591. // required: true
  592. // - name: repo
  593. // in: path
  594. // description: name of the repo
  595. // type: string
  596. // required: true
  597. // - name: index
  598. // in: path
  599. // description: index of the pull request
  600. // type: integer
  601. // format: int64
  602. // required: true
  603. // responses:
  604. // "204":
  605. // description: pull request has been merged
  606. // "404":
  607. // description: pull request has not been merged
  608. pr, err := models.GetPullRequestByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
  609. if err != nil {
  610. if models.IsErrPullRequestNotExist(err) {
  611. ctx.NotFound()
  612. } else {
  613. ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
  614. }
  615. return
  616. }
  617. if pr.HasMerged {
  618. ctx.Status(http.StatusNoContent)
  619. }
  620. ctx.NotFound()
  621. }
  622. // MergePullRequest merges a PR given an index
  623. func MergePullRequest(ctx *context.APIContext, form auth.MergePullRequestForm) {
  624. // swagger:operation POST /repos/{owner}/{repo}/pulls/{index}/merge repository repoMergePullRequest
  625. // ---
  626. // summary: Merge a pull request
  627. // produces:
  628. // - application/json
  629. // parameters:
  630. // - name: owner
  631. // in: path
  632. // description: owner of the repo
  633. // type: string
  634. // required: true
  635. // - name: repo
  636. // in: path
  637. // description: name of the repo
  638. // type: string
  639. // required: true
  640. // - name: index
  641. // in: path
  642. // description: index of the pull request to merge
  643. // type: integer
  644. // format: int64
  645. // required: true
  646. // - name: body
  647. // in: body
  648. // schema:
  649. // $ref: "#/definitions/MergePullRequestOption"
  650. // responses:
  651. // "200":
  652. // "$ref": "#/responses/empty"
  653. // "405":
  654. // "$ref": "#/responses/empty"
  655. // "409":
  656. // "$ref": "#/responses/error"
  657. pr, err := models.GetPullRequestByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
  658. if err != nil {
  659. if models.IsErrPullRequestNotExist(err) {
  660. ctx.NotFound("GetPullRequestByIndex", err)
  661. } else {
  662. ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
  663. }
  664. return
  665. }
  666. if err = pr.LoadHeadRepo(); err != nil {
  667. ctx.ServerError("LoadHeadRepo", err)
  668. return
  669. }
  670. err = pr.LoadIssue()
  671. if err != nil {
  672. ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
  673. return
  674. }
  675. pr.Issue.Repo = ctx.Repo.Repository
  676. if ctx.IsSigned {
  677. // Update issue-user.
  678. if err = pr.Issue.ReadBy(ctx.User.ID); err != nil {
  679. ctx.Error(http.StatusInternalServerError, "ReadBy", err)
  680. return
  681. }
  682. }
  683. if pr.Issue.IsClosed {
  684. ctx.NotFound()
  685. return
  686. }
  687. allowedMerge, err := pull_service.IsUserAllowedToMerge(pr, ctx.Repo.Permission, ctx.User)
  688. if err != nil {
  689. ctx.Error(http.StatusInternalServerError, "IsUSerAllowedToMerge", err)
  690. return
  691. }
  692. if !allowedMerge {
  693. ctx.Error(http.StatusMethodNotAllowed, "Merge", "User not allowed to merge PR")
  694. return
  695. }
  696. if !pr.CanAutoMerge() || pr.HasMerged || pr.IsWorkInProgress() {
  697. ctx.Status(http.StatusMethodNotAllowed)
  698. return
  699. }
  700. if err := pull_service.CheckPRReadyToMerge(pr); err != nil {
  701. if !models.IsErrNotAllowedToMerge(err) {
  702. ctx.Error(http.StatusInternalServerError, "CheckPRReadyToMerge", err)
  703. return
  704. }
  705. if form.ForceMerge != nil && *form.ForceMerge {
  706. if isRepoAdmin, err := models.IsUserRepoAdmin(pr.BaseRepo, ctx.User); err != nil {
  707. ctx.Error(http.StatusInternalServerError, "IsUserRepoAdmin", err)
  708. return
  709. } else if !isRepoAdmin {
  710. ctx.Error(http.StatusMethodNotAllowed, "Merge", "Only repository admin can merge if not all checks are ok (force merge)")
  711. }
  712. } else {
  713. ctx.Error(http.StatusMethodNotAllowed, "PR is not ready to be merged", err)
  714. return
  715. }
  716. }
  717. if _, err := pull_service.IsSignedIfRequired(pr, ctx.User); err != nil {
  718. if !models.IsErrWontSign(err) {
  719. ctx.Error(http.StatusInternalServerError, "IsSignedIfRequired", err)
  720. return
  721. }
  722. ctx.Error(http.StatusMethodNotAllowed, fmt.Sprintf("Protected branch %s requires signed commits but this merge would not be signed", pr.BaseBranch), err)
  723. return
  724. }
  725. if len(form.Do) == 0 {
  726. form.Do = string(models.MergeStyleMerge)
  727. }
  728. message := strings.TrimSpace(form.MergeTitleField)
  729. if len(message) == 0 {
  730. if models.MergeStyle(form.Do) == models.MergeStyleMerge {
  731. message = pr.GetDefaultMergeMessage()
  732. }
  733. if models.MergeStyle(form.Do) == models.MergeStyleSquash {
  734. message = pr.GetDefaultSquashMessage()
  735. }
  736. }
  737. form.MergeMessageField = strings.TrimSpace(form.MergeMessageField)
  738. if len(form.MergeMessageField) > 0 {
  739. message += "\n\n" + form.MergeMessageField
  740. }
  741. if err := pull_service.Merge(pr, ctx.User, ctx.Repo.GitRepo, models.MergeStyle(form.Do), message); err != nil {
  742. if models.IsErrInvalidMergeStyle(err) {
  743. ctx.Status(http.StatusMethodNotAllowed)
  744. return
  745. } else if models.IsErrMergeConflicts(err) {
  746. conflictError := err.(models.ErrMergeConflicts)
  747. ctx.JSON(http.StatusConflict, conflictError)
  748. } else if models.IsErrRebaseConflicts(err) {
  749. conflictError := err.(models.ErrRebaseConflicts)
  750. ctx.JSON(http.StatusConflict, conflictError)
  751. } else if models.IsErrMergeUnrelatedHistories(err) {
  752. conflictError := err.(models.ErrMergeUnrelatedHistories)
  753. ctx.JSON(http.StatusConflict, conflictError)
  754. } else if git.IsErrPushOutOfDate(err) {
  755. ctx.Error(http.StatusConflict, "Merge", "merge push out of date")
  756. return
  757. } else if git.IsErrPushRejected(err) {
  758. errPushRej := err.(*git.ErrPushRejected)
  759. if len(errPushRej.Message) == 0 {
  760. ctx.Error(http.StatusConflict, "Merge", "PushRejected without remote error message")
  761. return
  762. }
  763. ctx.Error(http.StatusConflict, "Merge", "PushRejected with remote message: "+errPushRej.Message)
  764. return
  765. }
  766. ctx.Error(http.StatusInternalServerError, "Merge", err)
  767. return
  768. }
  769. log.Trace("Pull request merged: %d", pr.ID)
  770. ctx.Status(http.StatusOK)
  771. }
  772. func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) (*models.User, *models.Repository, *git.Repository, *git.CompareInfo, string, string) {
  773. baseRepo := ctx.Repo.Repository
  774. // Get compared branches information
  775. // format: <base branch>...[<head repo>:]<head branch>
  776. // base<-head: master...head:feature
  777. // same repo: master...feature
  778. // TODO: Validate form first?
  779. baseBranch := form.Base
  780. var (
  781. headUser *models.User
  782. headBranch string
  783. isSameRepo bool
  784. err error
  785. )
  786. // If there is no head repository, it means pull request between same repository.
  787. headInfos := strings.Split(form.Head, ":")
  788. if len(headInfos) == 1 {
  789. isSameRepo = true
  790. headUser = ctx.Repo.Owner
  791. headBranch = headInfos[0]
  792. } else if len(headInfos) == 2 {
  793. headUser, err = models.GetUserByName(headInfos[0])
  794. if err != nil {
  795. if models.IsErrUserNotExist(err) {
  796. ctx.NotFound("GetUserByName")
  797. } else {
  798. ctx.ServerError("GetUserByName", err)
  799. }
  800. return nil, nil, nil, nil, "", ""
  801. }
  802. headBranch = headInfos[1]
  803. } else {
  804. ctx.NotFound()
  805. return nil, nil, nil, nil, "", ""
  806. }
  807. ctx.Repo.PullRequest.SameRepo = isSameRepo
  808. log.Info("Base branch: %s", baseBranch)
  809. log.Info("Repo path: %s", ctx.Repo.GitRepo.Path)
  810. // Check if base branch is valid.
  811. if !ctx.Repo.GitRepo.IsBranchExist(baseBranch) {
  812. ctx.NotFound("IsBranchExist")
  813. return nil, nil, nil, nil, "", ""
  814. }
  815. // Check if current user has fork of repository or in the same repository.
  816. headRepo, has := models.HasForkedRepo(headUser.ID, baseRepo.ID)
  817. if !has && !isSameRepo {
  818. log.Trace("parseCompareInfo[%d]: does not have fork or in same repository", baseRepo.ID)
  819. ctx.NotFound("HasForkedRepo")
  820. return nil, nil, nil, nil, "", ""
  821. }
  822. var headGitRepo *git.Repository
  823. if isSameRepo {
  824. headRepo = ctx.Repo.Repository
  825. headGitRepo = ctx.Repo.GitRepo
  826. } else {
  827. headGitRepo, err = git.OpenRepository(models.RepoPath(headUser.Name, headRepo.Name))
  828. if err != nil {
  829. ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
  830. return nil, nil, nil, nil, "", ""
  831. }
  832. }
  833. // user should have permission to read baseRepo's codes and pulls, NOT headRepo's
  834. permBase, err := models.GetUserRepoPermission(baseRepo, ctx.User)
  835. if err != nil {
  836. headGitRepo.Close()
  837. ctx.ServerError("GetUserRepoPermission", err)
  838. return nil, nil, nil, nil, "", ""
  839. }
  840. if !permBase.CanReadIssuesOrPulls(true) || !permBase.CanRead(models.UnitTypeCode) {
  841. if log.IsTrace() {
  842. log.Trace("Permission Denied: User %-v cannot create/read pull requests or cannot read code in Repo %-v\nUser in baseRepo has Permissions: %-+v",
  843. ctx.User,
  844. baseRepo,
  845. permBase)
  846. }
  847. headGitRepo.Close()
  848. ctx.NotFound("Can't read pulls or can't read UnitTypeCode")
  849. return nil, nil, nil, nil, "", ""
  850. }
  851. // user should have permission to read headrepo's codes
  852. permHead, err := models.GetUserRepoPermission(headRepo, ctx.User)
  853. if err != nil {
  854. headGitRepo.Close()
  855. ctx.ServerError("GetUserRepoPermission", err)
  856. return nil, nil, nil, nil, "", ""
  857. }
  858. if !permHead.CanRead(models.UnitTypeCode) {
  859. if log.IsTrace() {
  860. log.Trace("Permission Denied: User: %-v cannot read code in Repo: %-v\nUser in headRepo has Permissions: %-+v",
  861. ctx.User,
  862. headRepo,
  863. permHead)
  864. }
  865. headGitRepo.Close()
  866. ctx.NotFound("Can't read headRepo UnitTypeCode")
  867. return nil, nil, nil, nil, "", ""
  868. }
  869. // Check if head branch is valid.
  870. if !headGitRepo.IsBranchExist(headBranch) {
  871. headGitRepo.Close()
  872. ctx.NotFound()
  873. return nil, nil, nil, nil, "", ""
  874. }
  875. compareInfo, err := headGitRepo.GetCompareInfo(models.RepoPath(baseRepo.Owner.Name, baseRepo.Name), baseBranch, headBranch)
  876. if err != nil {
  877. headGitRepo.Close()
  878. ctx.Error(http.StatusInternalServerError, "GetCompareInfo", err)
  879. return nil, nil, nil, nil, "", ""
  880. }
  881. return headUser, headRepo, headGitRepo, compareInfo, baseBranch, headBranch
  882. }
  883. // UpdatePullRequest merge PR's baseBranch into headBranch
  884. func UpdatePullRequest(ctx *context.APIContext) {
  885. // swagger:operation POST /repos/{owner}/{repo}/pulls/{index}/update repository repoUpdatePullRequest
  886. // ---
  887. // summary: Merge PR's baseBranch into headBranch
  888. // produces:
  889. // - application/json
  890. // parameters:
  891. // - name: owner
  892. // in: path
  893. // description: owner of the repo
  894. // type: string
  895. // required: true
  896. // - name: repo
  897. // in: path
  898. // description: name of the repo
  899. // type: string
  900. // required: true
  901. // - name: index
  902. // in: path
  903. // description: index of the pull request to get
  904. // type: integer
  905. // format: int64
  906. // required: true
  907. // responses:
  908. // "200":
  909. // "$ref": "#/responses/empty"
  910. // "403":
  911. // "$ref": "#/responses/forbidden"
  912. // "404":
  913. // "$ref": "#/responses/notFound"
  914. // "409":
  915. // "$ref": "#/responses/error"
  916. // "422":
  917. // "$ref": "#/responses/validationError"
  918. pr, err := models.GetPullRequestByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
  919. if err != nil {
  920. if models.IsErrPullRequestNotExist(err) {
  921. ctx.NotFound()
  922. } else {
  923. ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
  924. }
  925. return
  926. }
  927. if pr.HasMerged {
  928. ctx.Error(http.StatusUnprocessableEntity, "UpdatePullRequest", err)
  929. return
  930. }
  931. if err = pr.LoadIssue(); err != nil {
  932. ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
  933. return
  934. }
  935. if pr.Issue.IsClosed {
  936. ctx.Error(http.StatusUnprocessableEntity, "UpdatePullRequest", err)
  937. return
  938. }
  939. if err = pr.LoadBaseRepo(); err != nil {
  940. ctx.Error(http.StatusInternalServerError, "LoadBaseRepo", err)
  941. return
  942. }
  943. if err = pr.LoadHeadRepo(); err != nil {
  944. ctx.Error(http.StatusInternalServerError, "LoadHeadRepo", err)
  945. return
  946. }
  947. allowedUpdate, err := pull_service.IsUserAllowedToUpdate(pr, ctx.User)
  948. if err != nil {
  949. ctx.Error(http.StatusInternalServerError, "IsUserAllowedToMerge", err)
  950. return
  951. }
  952. if !allowedUpdate {
  953. ctx.Status(http.StatusForbidden)
  954. return
  955. }
  956. // default merge commit message
  957. message := fmt.Sprintf("Merge branch '%s' into %s", pr.BaseBranch, pr.HeadBranch)
  958. if err = pull_service.Update(pr, ctx.User, message); err != nil {
  959. if models.IsErrMergeConflicts(err) {
  960. ctx.Error(http.StatusConflict, "Update", "merge failed because of conflict")
  961. return
  962. }
  963. ctx.Error(http.StatusInternalServerError, "pull_service.Update", err)
  964. return
  965. }
  966. ctx.Status(http.StatusOK)
  967. }