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.

branch.go 19 kB

Improve listing performance by using go-git (#6478) * Use go-git for tree reading and commit info lookup. Signed-off-by: Filip Navara <navara@emclient.com> * Use TreeEntry.IsRegular() instead of ObjectType that was removed. Signed-off-by: Filip Navara <navara@emclient.com> * Use the treePath to optimize commit info search. Signed-off-by: Filip Navara <navara@emclient.com> * Extract the latest commit at treePath along with the other commits. Signed-off-by: Filip Navara <navara@emclient.com> * Fix listing commit info for a directory that was created in one commit and never modified after. Signed-off-by: Filip Navara <navara@emclient.com> * Avoid nearly all external 'git' invocations when doing directory listing (.editorconfig code path is still hit). Signed-off-by: Filip Navara <navara@emclient.com> * Use go-git for reading blobs. Signed-off-by: Filip Navara <navara@emclient.com> * Make SHA1 type alias for plumbing.Hash in go-git. Signed-off-by: Filip Navara <navara@emclient.com> * Make Signature type alias for object.Signature in go-git. Signed-off-by: Filip Navara <navara@emclient.com> * Fix GetCommitsInfo for repository with only one commit. Signed-off-by: Filip Navara <navara@emclient.com> * Fix PGP signature verification. Signed-off-by: Filip Navara <navara@emclient.com> * Fix issues with walking commit graph across merges. Signed-off-by: Filip Navara <navara@emclient.com> * Fix typo in condition. Signed-off-by: Filip Navara <navara@emclient.com> * Speed up loading branch list by keeping the repository reference (and thus all the loaded packfile indexes). Signed-off-by: Filip Navara <navara@emclient.com> * Fix lising submodules. Signed-off-by: Filip Navara <navara@emclient.com> * Fix build Signed-off-by: Filip Navara <navara@emclient.com> * Add back commit cache because of name-rev Signed-off-by: Filip Navara <navara@emclient.com> * Fix tests Signed-off-by: Filip Navara <navara@emclient.com> * Fix code style * Fix spelling * Address PR feedback Signed-off-by: Filip Navara <navara@emclient.com> * Update vendor module list Signed-off-by: Filip Navara <navara@emclient.com> * Fix getting trees by commit id Signed-off-by: Filip Navara <navara@emclient.com> * Fix remaining unit test failures * Fix GetTreeBySHA * Avoid running `git name-rev` if not necessary Signed-off-by: Filip Navara <navara@emclient.com> * Move Branch code to git module * Clean up GPG signature verification and fix it for tagged commits * Address PR feedback (import formatting, copyright headers) * Make blob lookup by SHA working * Update tests to use public API * Allow getting content from any type of object through the blob interface * Change test to actually expect the object content that is in the GIT repository * Change one more test to actually expect the object content that is in the GIT repository * Add comments
6 years ago
10 years ago
Improve listing performance by using go-git (#6478) * Use go-git for tree reading and commit info lookup. Signed-off-by: Filip Navara <navara@emclient.com> * Use TreeEntry.IsRegular() instead of ObjectType that was removed. Signed-off-by: Filip Navara <navara@emclient.com> * Use the treePath to optimize commit info search. Signed-off-by: Filip Navara <navara@emclient.com> * Extract the latest commit at treePath along with the other commits. Signed-off-by: Filip Navara <navara@emclient.com> * Fix listing commit info for a directory that was created in one commit and never modified after. Signed-off-by: Filip Navara <navara@emclient.com> * Avoid nearly all external 'git' invocations when doing directory listing (.editorconfig code path is still hit). Signed-off-by: Filip Navara <navara@emclient.com> * Use go-git for reading blobs. Signed-off-by: Filip Navara <navara@emclient.com> * Make SHA1 type alias for plumbing.Hash in go-git. Signed-off-by: Filip Navara <navara@emclient.com> * Make Signature type alias for object.Signature in go-git. Signed-off-by: Filip Navara <navara@emclient.com> * Fix GetCommitsInfo for repository with only one commit. Signed-off-by: Filip Navara <navara@emclient.com> * Fix PGP signature verification. Signed-off-by: Filip Navara <navara@emclient.com> * Fix issues with walking commit graph across merges. Signed-off-by: Filip Navara <navara@emclient.com> * Fix typo in condition. Signed-off-by: Filip Navara <navara@emclient.com> * Speed up loading branch list by keeping the repository reference (and thus all the loaded packfile indexes). Signed-off-by: Filip Navara <navara@emclient.com> * Fix lising submodules. Signed-off-by: Filip Navara <navara@emclient.com> * Fix build Signed-off-by: Filip Navara <navara@emclient.com> * Add back commit cache because of name-rev Signed-off-by: Filip Navara <navara@emclient.com> * Fix tests Signed-off-by: Filip Navara <navara@emclient.com> * Fix code style * Fix spelling * Address PR feedback Signed-off-by: Filip Navara <navara@emclient.com> * Update vendor module list Signed-off-by: Filip Navara <navara@emclient.com> * Fix getting trees by commit id Signed-off-by: Filip Navara <navara@emclient.com> * Fix remaining unit test failures * Fix GetTreeBySHA * Avoid running `git name-rev` if not necessary Signed-off-by: Filip Navara <navara@emclient.com> * Move Branch code to git module * Clean up GPG signature verification and fix it for tagged commits * Address PR feedback (import formatting, copyright headers) * Make blob lookup by SHA working * Update tests to use public API * Allow getting content from any type of object through the blob interface * Change test to actually expect the object content that is in the GIT repository * Change one more test to actually expect the object content that is in the GIT repository * Add comments
6 years ago
10 years ago
10 years ago
Improve listing performance by using go-git (#6478) * Use go-git for tree reading and commit info lookup. Signed-off-by: Filip Navara <navara@emclient.com> * Use TreeEntry.IsRegular() instead of ObjectType that was removed. Signed-off-by: Filip Navara <navara@emclient.com> * Use the treePath to optimize commit info search. Signed-off-by: Filip Navara <navara@emclient.com> * Extract the latest commit at treePath along with the other commits. Signed-off-by: Filip Navara <navara@emclient.com> * Fix listing commit info for a directory that was created in one commit and never modified after. Signed-off-by: Filip Navara <navara@emclient.com> * Avoid nearly all external 'git' invocations when doing directory listing (.editorconfig code path is still hit). Signed-off-by: Filip Navara <navara@emclient.com> * Use go-git for reading blobs. Signed-off-by: Filip Navara <navara@emclient.com> * Make SHA1 type alias for plumbing.Hash in go-git. Signed-off-by: Filip Navara <navara@emclient.com> * Make Signature type alias for object.Signature in go-git. Signed-off-by: Filip Navara <navara@emclient.com> * Fix GetCommitsInfo for repository with only one commit. Signed-off-by: Filip Navara <navara@emclient.com> * Fix PGP signature verification. Signed-off-by: Filip Navara <navara@emclient.com> * Fix issues with walking commit graph across merges. Signed-off-by: Filip Navara <navara@emclient.com> * Fix typo in condition. Signed-off-by: Filip Navara <navara@emclient.com> * Speed up loading branch list by keeping the repository reference (and thus all the loaded packfile indexes). Signed-off-by: Filip Navara <navara@emclient.com> * Fix lising submodules. Signed-off-by: Filip Navara <navara@emclient.com> * Fix build Signed-off-by: Filip Navara <navara@emclient.com> * Add back commit cache because of name-rev Signed-off-by: Filip Navara <navara@emclient.com> * Fix tests Signed-off-by: Filip Navara <navara@emclient.com> * Fix code style * Fix spelling * Address PR feedback Signed-off-by: Filip Navara <navara@emclient.com> * Update vendor module list Signed-off-by: Filip Navara <navara@emclient.com> * Fix getting trees by commit id Signed-off-by: Filip Navara <navara@emclient.com> * Fix remaining unit test failures * Fix GetTreeBySHA * Avoid running `git name-rev` if not necessary Signed-off-by: Filip Navara <navara@emclient.com> * Move Branch code to git module * Clean up GPG signature verification and fix it for tagged commits * Address PR feedback (import formatting, copyright headers) * Make blob lookup by SHA working * Update tests to use public API * Allow getting content from any type of object through the blob interface * Change test to actually expect the object content that is in the GIT repository * Change one more test to actually expect the object content that is in the GIT repository * Add comments
6 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. // Copyright 2016 The Gogs Authors. All rights reserved.
  2. // Copyright 2019 The Gitea Authors. All rights reserved.
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. package repo
  6. import (
  7. "net/http"
  8. "code.gitea.io/gitea/models"
  9. "code.gitea.io/gitea/modules/context"
  10. "code.gitea.io/gitea/modules/convert"
  11. "code.gitea.io/gitea/modules/git"
  12. repo_module "code.gitea.io/gitea/modules/repository"
  13. api "code.gitea.io/gitea/modules/structs"
  14. )
  15. // GetBranch get a branch of a repository
  16. func GetBranch(ctx *context.APIContext) {
  17. // swagger:operation GET /repos/{owner}/{repo}/branches/{branch} repository repoGetBranch
  18. // ---
  19. // summary: Retrieve a specific branch from a repository, including its effective branch protection
  20. // produces:
  21. // - application/json
  22. // parameters:
  23. // - name: owner
  24. // in: path
  25. // description: owner of the repo
  26. // type: string
  27. // required: true
  28. // - name: repo
  29. // in: path
  30. // description: name of the repo
  31. // type: string
  32. // required: true
  33. // - name: branch
  34. // in: path
  35. // description: branch to get
  36. // type: string
  37. // required: true
  38. // responses:
  39. // "200":
  40. // "$ref": "#/responses/Branch"
  41. if ctx.Repo.TreePath != "" {
  42. // if TreePath != "", then URL contained extra slashes
  43. // (i.e. "master/subbranch" instead of "master"), so branch does
  44. // not exist
  45. ctx.NotFound()
  46. return
  47. }
  48. branch, err := repo_module.GetBranch(ctx.Repo.Repository, ctx.Repo.BranchName)
  49. if err != nil {
  50. if git.IsErrBranchNotExist(err) {
  51. ctx.NotFound(err)
  52. } else {
  53. ctx.Error(http.StatusInternalServerError, "GetBranch", err)
  54. }
  55. return
  56. }
  57. c, err := branch.GetCommit()
  58. if err != nil {
  59. ctx.Error(http.StatusInternalServerError, "GetCommit", err)
  60. return
  61. }
  62. branchProtection, err := ctx.Repo.Repository.GetBranchProtection(ctx.Repo.BranchName)
  63. if err != nil {
  64. ctx.Error(http.StatusInternalServerError, "GetBranchProtection", err)
  65. return
  66. }
  67. br, err := convert.ToBranch(ctx.Repo.Repository, branch, c, branchProtection, ctx.User, ctx.Repo.IsAdmin())
  68. if err != nil {
  69. ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
  70. return
  71. }
  72. ctx.JSON(http.StatusOK, br)
  73. }
  74. // ListBranches list all the branches of a repository
  75. func ListBranches(ctx *context.APIContext) {
  76. // swagger:operation GET /repos/{owner}/{repo}/branches repository repoListBranches
  77. // ---
  78. // summary: List a repository's branches
  79. // produces:
  80. // - application/json
  81. // parameters:
  82. // - name: owner
  83. // in: path
  84. // description: owner of the repo
  85. // type: string
  86. // required: true
  87. // - name: repo
  88. // in: path
  89. // description: name of the repo
  90. // type: string
  91. // required: true
  92. // responses:
  93. // "200":
  94. // "$ref": "#/responses/BranchList"
  95. branches, err := repo_module.GetBranches(ctx.Repo.Repository)
  96. if err != nil {
  97. ctx.Error(http.StatusInternalServerError, "GetBranches", err)
  98. return
  99. }
  100. apiBranches := make([]*api.Branch, len(branches))
  101. for i := range branches {
  102. c, err := branches[i].GetCommit()
  103. if err != nil {
  104. ctx.Error(http.StatusInternalServerError, "GetCommit", err)
  105. return
  106. }
  107. branchProtection, err := ctx.Repo.Repository.GetBranchProtection(branches[i].Name)
  108. if err != nil {
  109. ctx.Error(http.StatusInternalServerError, "GetBranchProtection", err)
  110. return
  111. }
  112. apiBranches[i], err = convert.ToBranch(ctx.Repo.Repository, branches[i], c, branchProtection, ctx.User, ctx.Repo.IsAdmin())
  113. if err != nil {
  114. ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
  115. return
  116. }
  117. }
  118. ctx.JSON(http.StatusOK, &apiBranches)
  119. }
  120. // GetBranchProtection gets a branch protection
  121. func GetBranchProtection(ctx *context.APIContext) {
  122. // swagger:operation GET /repos/{owner}/{repo}/branch_protections/{name} repository repoGetBranchProtection
  123. // ---
  124. // summary: Get a specific branch protection for the repository
  125. // produces:
  126. // - application/json
  127. // parameters:
  128. // - name: owner
  129. // in: path
  130. // description: owner of the repo
  131. // type: string
  132. // required: true
  133. // - name: repo
  134. // in: path
  135. // description: name of the repo
  136. // type: string
  137. // required: true
  138. // - name: name
  139. // in: path
  140. // description: name of protected branch
  141. // type: string
  142. // required: true
  143. // responses:
  144. // "200":
  145. // "$ref": "#/responses/BranchProtection"
  146. // "404":
  147. // "$ref": "#/responses/notFound"
  148. repo := ctx.Repo.Repository
  149. bpName := ctx.Params(":name")
  150. bp, err := models.GetProtectedBranchBy(repo.ID, bpName)
  151. if err != nil {
  152. ctx.Error(http.StatusInternalServerError, "GetProtectedBranchByID", err)
  153. return
  154. }
  155. if bp == nil || bp.RepoID != repo.ID {
  156. ctx.NotFound()
  157. return
  158. }
  159. ctx.JSON(http.StatusOK, convert.ToBranchProtection(bp))
  160. }
  161. // ListBranchProtections list branch protections for a repo
  162. func ListBranchProtections(ctx *context.APIContext) {
  163. // swagger:operation GET /repos/{owner}/{repo}/branch_protections repository repoListBranchProtection
  164. // ---
  165. // summary: List branch protections for a repository
  166. // produces:
  167. // - application/json
  168. // parameters:
  169. // - name: owner
  170. // in: path
  171. // description: owner of the repo
  172. // type: string
  173. // required: true
  174. // - name: repo
  175. // in: path
  176. // description: name of the repo
  177. // type: string
  178. // required: true
  179. // responses:
  180. // "200":
  181. // "$ref": "#/responses/BranchProtectionList"
  182. repo := ctx.Repo.Repository
  183. bps, err := repo.GetProtectedBranches()
  184. if err != nil {
  185. ctx.Error(http.StatusInternalServerError, "GetProtectedBranches", err)
  186. return
  187. }
  188. apiBps := make([]*api.BranchProtection, len(bps))
  189. for i := range bps {
  190. apiBps[i] = convert.ToBranchProtection(bps[i])
  191. }
  192. ctx.JSON(http.StatusOK, apiBps)
  193. }
  194. // CreateBranchProtection creates a branch protection for a repo
  195. func CreateBranchProtection(ctx *context.APIContext, form api.CreateBranchProtectionOption) {
  196. // swagger:operation POST /repos/{owner}/{repo}/branch_protections repository repoCreateBranchProtection
  197. // ---
  198. // summary: Create a branch protections for a repository
  199. // consumes:
  200. // - application/json
  201. // produces:
  202. // - application/json
  203. // parameters:
  204. // - name: owner
  205. // in: path
  206. // description: owner of the repo
  207. // type: string
  208. // required: true
  209. // - name: repo
  210. // in: path
  211. // description: name of the repo
  212. // type: string
  213. // required: true
  214. // - name: body
  215. // in: body
  216. // schema:
  217. // "$ref": "#/definitions/CreateBranchProtectionOption"
  218. // responses:
  219. // "201":
  220. // "$ref": "#/responses/BranchProtection"
  221. // "403":
  222. // "$ref": "#/responses/forbidden"
  223. // "404":
  224. // "$ref": "#/responses/notFound"
  225. // "422":
  226. // "$ref": "#/responses/validationError"
  227. repo := ctx.Repo.Repository
  228. // Currently protection must match an actual branch
  229. if !git.IsBranchExist(ctx.Repo.Repository.RepoPath(), form.BranchName) {
  230. ctx.NotFound()
  231. return
  232. }
  233. protectBranch, err := models.GetProtectedBranchBy(repo.ID, form.BranchName)
  234. if err != nil {
  235. ctx.Error(http.StatusInternalServerError, "GetProtectBranchOfRepoByName", err)
  236. return
  237. } else if protectBranch != nil {
  238. ctx.Error(http.StatusForbidden, "Create branch protection", "Branch protection already exist")
  239. return
  240. }
  241. var requiredApprovals int64
  242. if form.RequiredApprovals > 0 {
  243. requiredApprovals = form.RequiredApprovals
  244. }
  245. whitelistUsers, err := models.GetUserIDsByNames(form.PushWhitelistUsernames, false)
  246. if err != nil {
  247. if models.IsErrUserNotExist(err) {
  248. ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
  249. return
  250. }
  251. ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
  252. return
  253. }
  254. mergeWhitelistUsers, err := models.GetUserIDsByNames(form.MergeWhitelistUsernames, false)
  255. if err != nil {
  256. if models.IsErrUserNotExist(err) {
  257. ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
  258. return
  259. }
  260. ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
  261. return
  262. }
  263. approvalsWhitelistUsers, err := models.GetUserIDsByNames(form.ApprovalsWhitelistUsernames, false)
  264. if err != nil {
  265. if models.IsErrUserNotExist(err) {
  266. ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
  267. return
  268. }
  269. ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
  270. return
  271. }
  272. var whitelistTeams, mergeWhitelistTeams, approvalsWhitelistTeams []int64
  273. if repo.Owner.IsOrganization() {
  274. whitelistTeams, err = models.GetTeamIDsByNames(repo.OwnerID, form.PushWhitelistTeams, false)
  275. if err != nil {
  276. if models.IsErrTeamNotExist(err) {
  277. ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
  278. return
  279. }
  280. ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
  281. return
  282. }
  283. mergeWhitelistTeams, err = models.GetTeamIDsByNames(repo.OwnerID, form.MergeWhitelistTeams, false)
  284. if err != nil {
  285. if models.IsErrTeamNotExist(err) {
  286. ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
  287. return
  288. }
  289. ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
  290. return
  291. }
  292. approvalsWhitelistTeams, err = models.GetTeamIDsByNames(repo.OwnerID, form.ApprovalsWhitelistTeams, false)
  293. if err != nil {
  294. if models.IsErrTeamNotExist(err) {
  295. ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
  296. return
  297. }
  298. ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
  299. return
  300. }
  301. }
  302. protectBranch = &models.ProtectedBranch{
  303. RepoID: ctx.Repo.Repository.ID,
  304. BranchName: form.BranchName,
  305. CanPush: form.EnablePush,
  306. EnableWhitelist: form.EnablePush && form.EnablePushWhitelist,
  307. EnableMergeWhitelist: form.EnableMergeWhitelist,
  308. WhitelistDeployKeys: form.EnablePush && form.EnablePushWhitelist && form.PushWhitelistDeployKeys,
  309. EnableStatusCheck: form.EnableStatusCheck,
  310. StatusCheckContexts: form.StatusCheckContexts,
  311. EnableApprovalsWhitelist: form.EnableApprovalsWhitelist,
  312. RequiredApprovals: requiredApprovals,
  313. BlockOnRejectedReviews: form.BlockOnRejectedReviews,
  314. DismissStaleApprovals: form.DismissStaleApprovals,
  315. RequireSignedCommits: form.RequireSignedCommits,
  316. }
  317. err = models.UpdateProtectBranch(ctx.Repo.Repository, protectBranch, models.WhitelistOptions{
  318. UserIDs: whitelistUsers,
  319. TeamIDs: whitelistTeams,
  320. MergeUserIDs: mergeWhitelistUsers,
  321. MergeTeamIDs: mergeWhitelistTeams,
  322. ApprovalsUserIDs: approvalsWhitelistUsers,
  323. ApprovalsTeamIDs: approvalsWhitelistTeams,
  324. })
  325. if err != nil {
  326. ctx.Error(http.StatusInternalServerError, "UpdateProtectBranch", err)
  327. return
  328. }
  329. // Reload from db to get all whitelists
  330. bp, err := models.GetProtectedBranchBy(ctx.Repo.Repository.ID, form.BranchName)
  331. if err != nil {
  332. ctx.Error(http.StatusInternalServerError, "GetProtectedBranchByID", err)
  333. return
  334. }
  335. if bp == nil || bp.RepoID != ctx.Repo.Repository.ID {
  336. ctx.Error(http.StatusInternalServerError, "New branch protection not found", err)
  337. return
  338. }
  339. ctx.JSON(http.StatusCreated, convert.ToBranchProtection(bp))
  340. }
  341. // EditBranchProtection edits a branch protection for a repo
  342. func EditBranchProtection(ctx *context.APIContext, form api.EditBranchProtectionOption) {
  343. // swagger:operation PATCH /repos/{owner}/{repo}/branch_protections/{name} repository repoEditBranchProtection
  344. // ---
  345. // summary: Edit a branch protections for a repository. Only fields that are set will be changed
  346. // consumes:
  347. // - application/json
  348. // produces:
  349. // - application/json
  350. // parameters:
  351. // - name: owner
  352. // in: path
  353. // description: owner of the repo
  354. // type: string
  355. // required: true
  356. // - name: repo
  357. // in: path
  358. // description: name of the repo
  359. // type: string
  360. // required: true
  361. // - name: name
  362. // in: path
  363. // description: name of protected branch
  364. // type: string
  365. // required: true
  366. // - name: body
  367. // in: body
  368. // schema:
  369. // "$ref": "#/definitions/EditBranchProtectionOption"
  370. // responses:
  371. // "200":
  372. // "$ref": "#/responses/BranchProtection"
  373. // "404":
  374. // "$ref": "#/responses/notFound"
  375. // "422":
  376. // "$ref": "#/responses/validationError"
  377. repo := ctx.Repo.Repository
  378. bpName := ctx.Params(":name")
  379. protectBranch, err := models.GetProtectedBranchBy(repo.ID, bpName)
  380. if err != nil {
  381. ctx.Error(http.StatusInternalServerError, "GetProtectedBranchByID", err)
  382. return
  383. }
  384. if protectBranch == nil || protectBranch.RepoID != repo.ID {
  385. ctx.NotFound()
  386. return
  387. }
  388. if form.EnablePush != nil {
  389. if !*form.EnablePush {
  390. protectBranch.CanPush = false
  391. protectBranch.EnableWhitelist = false
  392. protectBranch.WhitelistDeployKeys = false
  393. } else {
  394. protectBranch.CanPush = true
  395. if form.EnablePushWhitelist != nil {
  396. if !*form.EnablePushWhitelist {
  397. protectBranch.EnableWhitelist = false
  398. protectBranch.WhitelistDeployKeys = false
  399. } else {
  400. protectBranch.EnableWhitelist = true
  401. if form.PushWhitelistDeployKeys != nil {
  402. protectBranch.WhitelistDeployKeys = *form.PushWhitelistDeployKeys
  403. }
  404. }
  405. }
  406. }
  407. }
  408. if form.EnableMergeWhitelist != nil {
  409. protectBranch.EnableMergeWhitelist = *form.EnableMergeWhitelist
  410. }
  411. if form.EnableStatusCheck != nil {
  412. protectBranch.EnableStatusCheck = *form.EnableStatusCheck
  413. }
  414. if protectBranch.EnableStatusCheck {
  415. protectBranch.StatusCheckContexts = form.StatusCheckContexts
  416. }
  417. if form.RequiredApprovals != nil && *form.RequiredApprovals >= 0 {
  418. protectBranch.RequiredApprovals = *form.RequiredApprovals
  419. }
  420. if form.EnableApprovalsWhitelist != nil {
  421. protectBranch.EnableApprovalsWhitelist = *form.EnableApprovalsWhitelist
  422. }
  423. if form.BlockOnRejectedReviews != nil {
  424. protectBranch.BlockOnRejectedReviews = *form.BlockOnRejectedReviews
  425. }
  426. if form.DismissStaleApprovals != nil {
  427. protectBranch.DismissStaleApprovals = *form.DismissStaleApprovals
  428. }
  429. if form.RequireSignedCommits != nil {
  430. protectBranch.RequireSignedCommits = *form.RequireSignedCommits
  431. }
  432. var whitelistUsers []int64
  433. if form.PushWhitelistUsernames != nil {
  434. whitelistUsers, err = models.GetUserIDsByNames(form.PushWhitelistUsernames, false)
  435. if err != nil {
  436. if models.IsErrUserNotExist(err) {
  437. ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
  438. return
  439. }
  440. ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
  441. return
  442. }
  443. } else {
  444. whitelistUsers = protectBranch.WhitelistUserIDs
  445. }
  446. var mergeWhitelistUsers []int64
  447. if form.MergeWhitelistUsernames != nil {
  448. mergeWhitelistUsers, err = models.GetUserIDsByNames(form.MergeWhitelistUsernames, false)
  449. if err != nil {
  450. if models.IsErrUserNotExist(err) {
  451. ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
  452. return
  453. }
  454. ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
  455. return
  456. }
  457. } else {
  458. mergeWhitelistUsers = protectBranch.MergeWhitelistUserIDs
  459. }
  460. var approvalsWhitelistUsers []int64
  461. if form.ApprovalsWhitelistUsernames != nil {
  462. approvalsWhitelistUsers, err = models.GetUserIDsByNames(form.ApprovalsWhitelistUsernames, false)
  463. if err != nil {
  464. if models.IsErrUserNotExist(err) {
  465. ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
  466. return
  467. }
  468. ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
  469. return
  470. }
  471. } else {
  472. approvalsWhitelistUsers = protectBranch.ApprovalsWhitelistUserIDs
  473. }
  474. var whitelistTeams, mergeWhitelistTeams, approvalsWhitelistTeams []int64
  475. if repo.Owner.IsOrganization() {
  476. if form.PushWhitelistTeams != nil {
  477. whitelistTeams, err = models.GetTeamIDsByNames(repo.OwnerID, form.PushWhitelistTeams, false)
  478. if err != nil {
  479. if models.IsErrTeamNotExist(err) {
  480. ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
  481. return
  482. }
  483. ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
  484. return
  485. }
  486. } else {
  487. whitelistTeams = protectBranch.WhitelistTeamIDs
  488. }
  489. if form.MergeWhitelistTeams != nil {
  490. mergeWhitelistTeams, err = models.GetTeamIDsByNames(repo.OwnerID, form.MergeWhitelistTeams, false)
  491. if err != nil {
  492. if models.IsErrTeamNotExist(err) {
  493. ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
  494. return
  495. }
  496. ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
  497. return
  498. }
  499. } else {
  500. mergeWhitelistTeams = protectBranch.MergeWhitelistTeamIDs
  501. }
  502. if form.ApprovalsWhitelistTeams != nil {
  503. approvalsWhitelistTeams, err = models.GetTeamIDsByNames(repo.OwnerID, form.ApprovalsWhitelistTeams, false)
  504. if err != nil {
  505. if models.IsErrTeamNotExist(err) {
  506. ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
  507. return
  508. }
  509. ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
  510. return
  511. }
  512. } else {
  513. approvalsWhitelistTeams = protectBranch.ApprovalsWhitelistTeamIDs
  514. }
  515. }
  516. err = models.UpdateProtectBranch(ctx.Repo.Repository, protectBranch, models.WhitelistOptions{
  517. UserIDs: whitelistUsers,
  518. TeamIDs: whitelistTeams,
  519. MergeUserIDs: mergeWhitelistUsers,
  520. MergeTeamIDs: mergeWhitelistTeams,
  521. ApprovalsUserIDs: approvalsWhitelistUsers,
  522. ApprovalsTeamIDs: approvalsWhitelistTeams,
  523. })
  524. if err != nil {
  525. ctx.Error(http.StatusInternalServerError, "UpdateProtectBranch", err)
  526. return
  527. }
  528. // Reload from db to ensure get all whitelists
  529. bp, err := models.GetProtectedBranchBy(repo.ID, bpName)
  530. if err != nil {
  531. ctx.Error(http.StatusInternalServerError, "GetProtectedBranchBy", err)
  532. return
  533. }
  534. if bp == nil || bp.RepoID != ctx.Repo.Repository.ID {
  535. ctx.Error(http.StatusInternalServerError, "New branch protection not found", err)
  536. return
  537. }
  538. ctx.JSON(http.StatusOK, convert.ToBranchProtection(bp))
  539. }
  540. // DeleteBranchProtection deletes a branch protection for a repo
  541. func DeleteBranchProtection(ctx *context.APIContext) {
  542. // swagger:operation DELETE /repos/{owner}/{repo}/branch_protections/{name} repository repoDeleteBranchProtection
  543. // ---
  544. // summary: Delete a specific branch protection for the repository
  545. // produces:
  546. // - application/json
  547. // parameters:
  548. // - name: owner
  549. // in: path
  550. // description: owner of the repo
  551. // type: string
  552. // required: true
  553. // - name: repo
  554. // in: path
  555. // description: name of the repo
  556. // type: string
  557. // required: true
  558. // - name: name
  559. // in: path
  560. // description: name of protected branch
  561. // type: string
  562. // required: true
  563. // responses:
  564. // "204":
  565. // "$ref": "#/responses/empty"
  566. // "404":
  567. // "$ref": "#/responses/notFound"
  568. repo := ctx.Repo.Repository
  569. bpName := ctx.Params(":name")
  570. bp, err := models.GetProtectedBranchBy(repo.ID, bpName)
  571. if err != nil {
  572. ctx.Error(http.StatusInternalServerError, "GetProtectedBranchByID", err)
  573. return
  574. }
  575. if bp == nil || bp.RepoID != repo.ID {
  576. ctx.NotFound()
  577. return
  578. }
  579. if err := ctx.Repo.Repository.DeleteProtectedBranch(bp.ID); err != nil {
  580. ctx.Error(http.StatusInternalServerError, "DeleteProtectedBranch", err)
  581. return
  582. }
  583. ctx.Status(http.StatusNoContent)
  584. }