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
9 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
9 years ago
9 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
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  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. ctx.JSON(http.StatusOK, convert.ToBranch(ctx.Repo.Repository, branch, c, branchProtection, ctx.User, ctx.Repo.IsAdmin()))
  68. }
  69. // ListBranches list all the branches of a repository
  70. func ListBranches(ctx *context.APIContext) {
  71. // swagger:operation GET /repos/{owner}/{repo}/branches repository repoListBranches
  72. // ---
  73. // summary: List a repository's branches
  74. // produces:
  75. // - application/json
  76. // parameters:
  77. // - name: owner
  78. // in: path
  79. // description: owner of the repo
  80. // type: string
  81. // required: true
  82. // - name: repo
  83. // in: path
  84. // description: name of the repo
  85. // type: string
  86. // required: true
  87. // responses:
  88. // "200":
  89. // "$ref": "#/responses/BranchList"
  90. branches, err := repo_module.GetBranches(ctx.Repo.Repository)
  91. if err != nil {
  92. ctx.Error(http.StatusInternalServerError, "GetBranches", err)
  93. return
  94. }
  95. apiBranches := make([]*api.Branch, len(branches))
  96. for i := range branches {
  97. c, err := branches[i].GetCommit()
  98. if err != nil {
  99. ctx.Error(http.StatusInternalServerError, "GetCommit", err)
  100. return
  101. }
  102. branchProtection, err := ctx.Repo.Repository.GetBranchProtection(branches[i].Name)
  103. if err != nil {
  104. ctx.Error(http.StatusInternalServerError, "GetBranchProtection", err)
  105. return
  106. }
  107. apiBranches[i] = convert.ToBranch(ctx.Repo.Repository, branches[i], c, branchProtection, ctx.User, ctx.Repo.IsAdmin())
  108. }
  109. ctx.JSON(http.StatusOK, &apiBranches)
  110. }
  111. // GetBranchProtection gets a branch protection
  112. func GetBranchProtection(ctx *context.APIContext) {
  113. // swagger:operation GET /repos/{owner}/{repo}/branch_protections/{name} repository repoGetBranchProtection
  114. // ---
  115. // summary: Get a specific branch protection for the repository
  116. // produces:
  117. // - application/json
  118. // parameters:
  119. // - name: owner
  120. // in: path
  121. // description: owner of the repo
  122. // type: string
  123. // required: true
  124. // - name: repo
  125. // in: path
  126. // description: name of the repo
  127. // type: string
  128. // required: true
  129. // - name: name
  130. // in: path
  131. // description: name of protected branch
  132. // type: string
  133. // required: true
  134. // responses:
  135. // "200":
  136. // "$ref": "#/responses/BranchProtection"
  137. // "404":
  138. // "$ref": "#/responses/notFound"
  139. repo := ctx.Repo.Repository
  140. bpName := ctx.Params(":name")
  141. bp, err := models.GetProtectedBranchBy(repo.ID, bpName)
  142. if err != nil {
  143. ctx.Error(http.StatusInternalServerError, "GetProtectedBranchByID", err)
  144. return
  145. }
  146. if bp == nil || bp.RepoID != repo.ID {
  147. ctx.NotFound()
  148. return
  149. }
  150. ctx.JSON(http.StatusOK, convert.ToBranchProtection(bp))
  151. }
  152. // ListBranchProtections list branch protections for a repo
  153. func ListBranchProtections(ctx *context.APIContext) {
  154. // swagger:operation GET /repos/{owner}/{repo}/branch_protections repository repoListBranchProtection
  155. // ---
  156. // summary: List branch protections for a repository
  157. // produces:
  158. // - application/json
  159. // parameters:
  160. // - name: owner
  161. // in: path
  162. // description: owner of the repo
  163. // type: string
  164. // required: true
  165. // - name: repo
  166. // in: path
  167. // description: name of the repo
  168. // type: string
  169. // required: true
  170. // responses:
  171. // "200":
  172. // "$ref": "#/responses/BranchProtectionList"
  173. repo := ctx.Repo.Repository
  174. bps, err := repo.GetProtectedBranches()
  175. if err != nil {
  176. ctx.Error(http.StatusInternalServerError, "GetProtectedBranches", err)
  177. return
  178. }
  179. apiBps := make([]*api.BranchProtection, len(bps))
  180. for i := range bps {
  181. apiBps[i] = convert.ToBranchProtection(bps[i])
  182. }
  183. ctx.JSON(http.StatusOK, apiBps)
  184. }
  185. // CreateBranchProtection creates a branch protection for a repo
  186. func CreateBranchProtection(ctx *context.APIContext, form api.CreateBranchProtectionOption) {
  187. // swagger:operation POST /repos/{owner}/{repo}/branch_protections repository repoCreateBranchProtection
  188. // ---
  189. // summary: Create a branch protections for a repository
  190. // consumes:
  191. // - application/json
  192. // produces:
  193. // - application/json
  194. // parameters:
  195. // - name: owner
  196. // in: path
  197. // description: owner of the repo
  198. // type: string
  199. // required: true
  200. // - name: repo
  201. // in: path
  202. // description: name of the repo
  203. // type: string
  204. // required: true
  205. // - name: body
  206. // in: body
  207. // schema:
  208. // "$ref": "#/definitions/CreateBranchProtectionOption"
  209. // responses:
  210. // "201":
  211. // "$ref": "#/responses/BranchProtection"
  212. // "403":
  213. // "$ref": "#/responses/forbidden"
  214. // "404":
  215. // "$ref": "#/responses/notFound"
  216. // "422":
  217. // "$ref": "#/responses/validationError"
  218. repo := ctx.Repo.Repository
  219. // Currently protection must match an actual branch
  220. if !git.IsBranchExist(ctx.Repo.Repository.RepoPath(), form.BranchName) {
  221. ctx.NotFound()
  222. return
  223. }
  224. protectBranch, err := models.GetProtectedBranchBy(repo.ID, form.BranchName)
  225. if err != nil {
  226. ctx.Error(http.StatusInternalServerError, "GetProtectBranchOfRepoByName", err)
  227. return
  228. } else if protectBranch != nil {
  229. ctx.Error(http.StatusForbidden, "Create branch protection", "Branch protection already exist")
  230. return
  231. }
  232. var requiredApprovals int64
  233. if form.RequiredApprovals > 0 {
  234. requiredApprovals = form.RequiredApprovals
  235. }
  236. whitelistUsers, err := models.GetUserIDsByNames(form.PushWhitelistUsernames, false)
  237. if err != nil {
  238. if models.IsErrUserNotExist(err) {
  239. ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
  240. return
  241. }
  242. ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
  243. return
  244. }
  245. mergeWhitelistUsers, err := models.GetUserIDsByNames(form.MergeWhitelistUsernames, 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. approvalsWhitelistUsers, err := models.GetUserIDsByNames(form.ApprovalsWhitelistUsernames, 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. var whitelistTeams, mergeWhitelistTeams, approvalsWhitelistTeams []int64
  264. if repo.Owner.IsOrganization() {
  265. whitelistTeams, err = models.GetTeamIDsByNames(repo.OwnerID, form.PushWhitelistTeams, false)
  266. if err != nil {
  267. if models.IsErrTeamNotExist(err) {
  268. ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
  269. return
  270. }
  271. ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
  272. return
  273. }
  274. mergeWhitelistTeams, err = models.GetTeamIDsByNames(repo.OwnerID, form.MergeWhitelistTeams, 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. approvalsWhitelistTeams, err = models.GetTeamIDsByNames(repo.OwnerID, form.ApprovalsWhitelistTeams, 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. }
  293. protectBranch = &models.ProtectedBranch{
  294. RepoID: ctx.Repo.Repository.ID,
  295. BranchName: form.BranchName,
  296. CanPush: form.EnablePush,
  297. EnableWhitelist: form.EnablePush && form.EnablePushWhitelist,
  298. EnableMergeWhitelist: form.EnableMergeWhitelist,
  299. WhitelistDeployKeys: form.EnablePush && form.EnablePushWhitelist && form.PushWhitelistDeployKeys,
  300. EnableStatusCheck: form.EnableStatusCheck,
  301. StatusCheckContexts: form.StatusCheckContexts,
  302. EnableApprovalsWhitelist: form.EnableApprovalsWhitelist,
  303. RequiredApprovals: requiredApprovals,
  304. BlockOnRejectedReviews: form.BlockOnRejectedReviews,
  305. DismissStaleApprovals: form.DismissStaleApprovals,
  306. RequireSignedCommits: form.RequireSignedCommits,
  307. }
  308. err = models.UpdateProtectBranch(ctx.Repo.Repository, protectBranch, models.WhitelistOptions{
  309. UserIDs: whitelistUsers,
  310. TeamIDs: whitelistTeams,
  311. MergeUserIDs: mergeWhitelistUsers,
  312. MergeTeamIDs: mergeWhitelistTeams,
  313. ApprovalsUserIDs: approvalsWhitelistUsers,
  314. ApprovalsTeamIDs: approvalsWhitelistTeams,
  315. })
  316. if err != nil {
  317. ctx.Error(http.StatusInternalServerError, "UpdateProtectBranch", err)
  318. return
  319. }
  320. // Reload from db to get all whitelists
  321. bp, err := models.GetProtectedBranchBy(ctx.Repo.Repository.ID, form.BranchName)
  322. if err != nil {
  323. ctx.Error(http.StatusInternalServerError, "GetProtectedBranchByID", err)
  324. return
  325. }
  326. if bp == nil || bp.RepoID != ctx.Repo.Repository.ID {
  327. ctx.Error(http.StatusInternalServerError, "New branch protection not found", err)
  328. return
  329. }
  330. ctx.JSON(http.StatusCreated, convert.ToBranchProtection(bp))
  331. }
  332. // EditBranchProtection edits a branch protection for a repo
  333. func EditBranchProtection(ctx *context.APIContext, form api.EditBranchProtectionOption) {
  334. // swagger:operation PATCH /repos/{owner}/{repo}/branch_protections/{name} repository repoEditBranchProtection
  335. // ---
  336. // summary: Edit a branch protections for a repository. Only fields that are set will be changed
  337. // consumes:
  338. // - application/json
  339. // produces:
  340. // - application/json
  341. // parameters:
  342. // - name: owner
  343. // in: path
  344. // description: owner of the repo
  345. // type: string
  346. // required: true
  347. // - name: repo
  348. // in: path
  349. // description: name of the repo
  350. // type: string
  351. // required: true
  352. // - name: name
  353. // in: path
  354. // description: name of protected branch
  355. // type: string
  356. // required: true
  357. // - name: body
  358. // in: body
  359. // schema:
  360. // "$ref": "#/definitions/EditBranchProtectionOption"
  361. // responses:
  362. // "200":
  363. // "$ref": "#/responses/BranchProtection"
  364. // "404":
  365. // "$ref": "#/responses/notFound"
  366. // "422":
  367. // "$ref": "#/responses/validationError"
  368. repo := ctx.Repo.Repository
  369. bpName := ctx.Params(":name")
  370. protectBranch, err := models.GetProtectedBranchBy(repo.ID, bpName)
  371. if err != nil {
  372. ctx.Error(http.StatusInternalServerError, "GetProtectedBranchByID", err)
  373. return
  374. }
  375. if protectBranch == nil || protectBranch.RepoID != repo.ID {
  376. ctx.NotFound()
  377. return
  378. }
  379. if form.EnablePush != nil {
  380. if !*form.EnablePush {
  381. protectBranch.CanPush = false
  382. protectBranch.EnableWhitelist = false
  383. protectBranch.WhitelistDeployKeys = false
  384. } else {
  385. protectBranch.CanPush = true
  386. if form.EnablePushWhitelist != nil {
  387. if !*form.EnablePushWhitelist {
  388. protectBranch.EnableWhitelist = false
  389. protectBranch.WhitelistDeployKeys = false
  390. } else {
  391. protectBranch.EnableWhitelist = true
  392. if form.PushWhitelistDeployKeys != nil {
  393. protectBranch.WhitelistDeployKeys = *form.PushWhitelistDeployKeys
  394. }
  395. }
  396. }
  397. }
  398. }
  399. if form.EnableMergeWhitelist != nil {
  400. protectBranch.EnableMergeWhitelist = *form.EnableMergeWhitelist
  401. }
  402. if form.EnableStatusCheck != nil {
  403. protectBranch.EnableStatusCheck = *form.EnableStatusCheck
  404. }
  405. if protectBranch.EnableStatusCheck {
  406. protectBranch.StatusCheckContexts = form.StatusCheckContexts
  407. }
  408. if form.RequiredApprovals != nil && *form.RequiredApprovals >= 0 {
  409. protectBranch.RequiredApprovals = *form.RequiredApprovals
  410. }
  411. if form.EnableApprovalsWhitelist != nil {
  412. protectBranch.EnableApprovalsWhitelist = *form.EnableApprovalsWhitelist
  413. }
  414. if form.BlockOnRejectedReviews != nil {
  415. protectBranch.BlockOnRejectedReviews = *form.BlockOnRejectedReviews
  416. }
  417. if form.DismissStaleApprovals != nil {
  418. protectBranch.DismissStaleApprovals = *form.DismissStaleApprovals
  419. }
  420. if form.RequireSignedCommits != nil {
  421. protectBranch.RequireSignedCommits = *form.RequireSignedCommits
  422. }
  423. var whitelistUsers []int64
  424. if form.PushWhitelistUsernames != nil {
  425. whitelistUsers, err = models.GetUserIDsByNames(form.PushWhitelistUsernames, false)
  426. if err != nil {
  427. if models.IsErrUserNotExist(err) {
  428. ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
  429. return
  430. }
  431. ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
  432. return
  433. }
  434. } else {
  435. whitelistUsers = protectBranch.WhitelistUserIDs
  436. }
  437. var mergeWhitelistUsers []int64
  438. if form.MergeWhitelistUsernames != nil {
  439. mergeWhitelistUsers, err = models.GetUserIDsByNames(form.MergeWhitelistUsernames, false)
  440. if err != nil {
  441. if models.IsErrUserNotExist(err) {
  442. ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
  443. return
  444. }
  445. ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
  446. return
  447. }
  448. } else {
  449. mergeWhitelistUsers = protectBranch.MergeWhitelistUserIDs
  450. }
  451. var approvalsWhitelistUsers []int64
  452. if form.ApprovalsWhitelistUsernames != nil {
  453. approvalsWhitelistUsers, err = models.GetUserIDsByNames(form.ApprovalsWhitelistUsernames, false)
  454. if err != nil {
  455. if models.IsErrUserNotExist(err) {
  456. ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
  457. return
  458. }
  459. ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
  460. return
  461. }
  462. } else {
  463. approvalsWhitelistUsers = protectBranch.ApprovalsWhitelistUserIDs
  464. }
  465. var whitelistTeams, mergeWhitelistTeams, approvalsWhitelistTeams []int64
  466. if repo.Owner.IsOrganization() {
  467. if form.PushWhitelistTeams != nil {
  468. whitelistTeams, err = models.GetTeamIDsByNames(repo.OwnerID, form.PushWhitelistTeams, false)
  469. if err != nil {
  470. if models.IsErrTeamNotExist(err) {
  471. ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
  472. return
  473. }
  474. ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
  475. return
  476. }
  477. } else {
  478. whitelistTeams = protectBranch.WhitelistTeamIDs
  479. }
  480. if form.MergeWhitelistTeams != nil {
  481. mergeWhitelistTeams, err = models.GetTeamIDsByNames(repo.OwnerID, form.MergeWhitelistTeams, false)
  482. if err != nil {
  483. if models.IsErrTeamNotExist(err) {
  484. ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
  485. return
  486. }
  487. ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
  488. return
  489. }
  490. } else {
  491. mergeWhitelistTeams = protectBranch.MergeWhitelistTeamIDs
  492. }
  493. if form.ApprovalsWhitelistTeams != nil {
  494. approvalsWhitelistTeams, err = models.GetTeamIDsByNames(repo.OwnerID, form.ApprovalsWhitelistTeams, false)
  495. if err != nil {
  496. if models.IsErrTeamNotExist(err) {
  497. ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
  498. return
  499. }
  500. ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
  501. return
  502. }
  503. } else {
  504. approvalsWhitelistTeams = protectBranch.ApprovalsWhitelistTeamIDs
  505. }
  506. }
  507. err = models.UpdateProtectBranch(ctx.Repo.Repository, protectBranch, models.WhitelistOptions{
  508. UserIDs: whitelistUsers,
  509. TeamIDs: whitelistTeams,
  510. MergeUserIDs: mergeWhitelistUsers,
  511. MergeTeamIDs: mergeWhitelistTeams,
  512. ApprovalsUserIDs: approvalsWhitelistUsers,
  513. ApprovalsTeamIDs: approvalsWhitelistTeams,
  514. })
  515. if err != nil {
  516. ctx.Error(http.StatusInternalServerError, "UpdateProtectBranch", err)
  517. return
  518. }
  519. // Reload from db to ensure get all whitelists
  520. bp, err := models.GetProtectedBranchBy(repo.ID, bpName)
  521. if err != nil {
  522. ctx.Error(http.StatusInternalServerError, "GetProtectedBranchBy", err)
  523. return
  524. }
  525. if bp == nil || bp.RepoID != ctx.Repo.Repository.ID {
  526. ctx.Error(http.StatusInternalServerError, "New branch protection not found", err)
  527. return
  528. }
  529. ctx.JSON(http.StatusOK, convert.ToBranchProtection(bp))
  530. }
  531. // DeleteBranchProtection deletes a branch protection for a repo
  532. func DeleteBranchProtection(ctx *context.APIContext) {
  533. // swagger:operation DELETE /repos/{owner}/{repo}/branch_protections/{name} repository repoDeleteBranchProtection
  534. // ---
  535. // summary: Delete a specific branch protection for the repository
  536. // produces:
  537. // - application/json
  538. // parameters:
  539. // - name: owner
  540. // in: path
  541. // description: owner of the repo
  542. // type: string
  543. // required: true
  544. // - name: repo
  545. // in: path
  546. // description: name of the repo
  547. // type: string
  548. // required: true
  549. // - name: name
  550. // in: path
  551. // description: name of protected branch
  552. // type: string
  553. // required: true
  554. // responses:
  555. // "204":
  556. // "$ref": "#/responses/empty"
  557. // "404":
  558. // "$ref": "#/responses/notFound"
  559. repo := ctx.Repo.Repository
  560. bpName := ctx.Params(":name")
  561. bp, err := models.GetProtectedBranchBy(repo.ID, bpName)
  562. if err != nil {
  563. ctx.Error(http.StatusInternalServerError, "GetProtectedBranchByID", err)
  564. return
  565. }
  566. if bp == nil || bp.RepoID != repo.ID {
  567. ctx.NotFound()
  568. return
  569. }
  570. if err := ctx.Repo.Repository.DeleteProtectedBranch(bp.ID); err != nil {
  571. ctx.Error(http.StatusInternalServerError, "DeleteProtectedBranch", err)
  572. return
  573. }
  574. ctx.Status(http.StatusNoContent)
  575. }