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
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. }