您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

branches.go 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597
  1. // Copyright 2016 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package git
  4. import (
  5. "context"
  6. "fmt"
  7. "strings"
  8. "time"
  9. "code.gitea.io/gitea/models/db"
  10. "code.gitea.io/gitea/models/organization"
  11. "code.gitea.io/gitea/models/perm"
  12. access_model "code.gitea.io/gitea/models/perm/access"
  13. repo_model "code.gitea.io/gitea/models/repo"
  14. "code.gitea.io/gitea/models/unit"
  15. user_model "code.gitea.io/gitea/models/user"
  16. "code.gitea.io/gitea/modules/base"
  17. "code.gitea.io/gitea/modules/log"
  18. "code.gitea.io/gitea/modules/timeutil"
  19. "code.gitea.io/gitea/modules/util"
  20. "github.com/gobwas/glob"
  21. )
  22. // ProtectedBranch struct
  23. type ProtectedBranch struct {
  24. ID int64 `xorm:"pk autoincr"`
  25. RepoID int64 `xorm:"UNIQUE(s)"`
  26. BranchName string `xorm:"UNIQUE(s)"`
  27. CanPush bool `xorm:"NOT NULL DEFAULT false"`
  28. EnableWhitelist bool
  29. WhitelistUserIDs []int64 `xorm:"JSON TEXT"`
  30. WhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
  31. EnableMergeWhitelist bool `xorm:"NOT NULL DEFAULT false"`
  32. WhitelistDeployKeys bool `xorm:"NOT NULL DEFAULT false"`
  33. MergeWhitelistUserIDs []int64 `xorm:"JSON TEXT"`
  34. MergeWhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
  35. EnableStatusCheck bool `xorm:"NOT NULL DEFAULT false"`
  36. StatusCheckContexts []string `xorm:"JSON TEXT"`
  37. EnableApprovalsWhitelist bool `xorm:"NOT NULL DEFAULT false"`
  38. ApprovalsWhitelistUserIDs []int64 `xorm:"JSON TEXT"`
  39. ApprovalsWhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
  40. RequiredApprovals int64 `xorm:"NOT NULL DEFAULT 0"`
  41. BlockOnRejectedReviews bool `xorm:"NOT NULL DEFAULT false"`
  42. BlockOnOfficialReviewRequests bool `xorm:"NOT NULL DEFAULT false"`
  43. BlockOnOutdatedBranch bool `xorm:"NOT NULL DEFAULT false"`
  44. DismissStaleApprovals bool `xorm:"NOT NULL DEFAULT false"`
  45. RequireSignedCommits bool `xorm:"NOT NULL DEFAULT false"`
  46. ProtectedFilePatterns string `xorm:"TEXT"`
  47. UnprotectedFilePatterns string `xorm:"TEXT"`
  48. CreatedUnix timeutil.TimeStamp `xorm:"created"`
  49. UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
  50. }
  51. func init() {
  52. db.RegisterModel(new(ProtectedBranch))
  53. db.RegisterModel(new(DeletedBranch))
  54. db.RegisterModel(new(RenamedBranch))
  55. }
  56. // IsProtected returns if the branch is protected
  57. func (protectBranch *ProtectedBranch) IsProtected() bool {
  58. return protectBranch.ID > 0
  59. }
  60. // CanUserPush returns if some user could push to this protected branch
  61. func (protectBranch *ProtectedBranch) CanUserPush(ctx context.Context, userID int64) bool {
  62. if !protectBranch.CanPush {
  63. return false
  64. }
  65. if !protectBranch.EnableWhitelist {
  66. if user, err := user_model.GetUserByID(ctx, userID); err != nil {
  67. log.Error("GetUserByID: %v", err)
  68. return false
  69. } else if repo, err := repo_model.GetRepositoryByID(ctx, protectBranch.RepoID); err != nil {
  70. log.Error("repo_model.GetRepositoryByID: %v", err)
  71. return false
  72. } else if writeAccess, err := access_model.HasAccessUnit(ctx, user, repo, unit.TypeCode, perm.AccessModeWrite); err != nil {
  73. log.Error("HasAccessUnit: %v", err)
  74. return false
  75. } else {
  76. return writeAccess
  77. }
  78. }
  79. if base.Int64sContains(protectBranch.WhitelistUserIDs, userID) {
  80. return true
  81. }
  82. if len(protectBranch.WhitelistTeamIDs) == 0 {
  83. return false
  84. }
  85. in, err := organization.IsUserInTeams(ctx, userID, protectBranch.WhitelistTeamIDs)
  86. if err != nil {
  87. log.Error("IsUserInTeams: %v", err)
  88. return false
  89. }
  90. return in
  91. }
  92. // IsUserMergeWhitelisted checks if some user is whitelisted to merge to this branch
  93. func IsUserMergeWhitelisted(ctx context.Context, protectBranch *ProtectedBranch, userID int64, permissionInRepo access_model.Permission) bool {
  94. if !protectBranch.EnableMergeWhitelist {
  95. // Then we need to fall back on whether the user has write permission
  96. return permissionInRepo.CanWrite(unit.TypeCode)
  97. }
  98. if base.Int64sContains(protectBranch.MergeWhitelistUserIDs, userID) {
  99. return true
  100. }
  101. if len(protectBranch.MergeWhitelistTeamIDs) == 0 {
  102. return false
  103. }
  104. in, err := organization.IsUserInTeams(ctx, userID, protectBranch.MergeWhitelistTeamIDs)
  105. if err != nil {
  106. log.Error("IsUserInTeams: %v", err)
  107. return false
  108. }
  109. return in
  110. }
  111. // IsUserOfficialReviewer check if user is official reviewer for the branch (counts towards required approvals)
  112. func IsUserOfficialReviewer(ctx context.Context, protectBranch *ProtectedBranch, user *user_model.User) (bool, error) {
  113. repo, err := repo_model.GetRepositoryByID(ctx, protectBranch.RepoID)
  114. if err != nil {
  115. return false, err
  116. }
  117. if !protectBranch.EnableApprovalsWhitelist {
  118. // Anyone with write access is considered official reviewer
  119. writeAccess, err := access_model.HasAccessUnit(ctx, user, repo, unit.TypeCode, perm.AccessModeWrite)
  120. if err != nil {
  121. return false, err
  122. }
  123. return writeAccess, nil
  124. }
  125. if base.Int64sContains(protectBranch.ApprovalsWhitelistUserIDs, user.ID) {
  126. return true, nil
  127. }
  128. inTeam, err := organization.IsUserInTeams(ctx, user.ID, protectBranch.ApprovalsWhitelistTeamIDs)
  129. if err != nil {
  130. return false, err
  131. }
  132. return inTeam, nil
  133. }
  134. // GetProtectedFilePatterns parses a semicolon separated list of protected file patterns and returns a glob.Glob slice
  135. func (protectBranch *ProtectedBranch) GetProtectedFilePatterns() []glob.Glob {
  136. return getFilePatterns(protectBranch.ProtectedFilePatterns)
  137. }
  138. // GetUnprotectedFilePatterns parses a semicolon separated list of unprotected file patterns and returns a glob.Glob slice
  139. func (protectBranch *ProtectedBranch) GetUnprotectedFilePatterns() []glob.Glob {
  140. return getFilePatterns(protectBranch.UnprotectedFilePatterns)
  141. }
  142. func getFilePatterns(filePatterns string) []glob.Glob {
  143. extarr := make([]glob.Glob, 0, 10)
  144. for _, expr := range strings.Split(strings.ToLower(filePatterns), ";") {
  145. expr = strings.TrimSpace(expr)
  146. if expr != "" {
  147. if g, err := glob.Compile(expr, '.', '/'); err != nil {
  148. log.Info("Invalid glob expression '%s' (skipped): %v", expr, err)
  149. } else {
  150. extarr = append(extarr, g)
  151. }
  152. }
  153. }
  154. return extarr
  155. }
  156. // MergeBlockedByProtectedFiles returns true if merge is blocked by protected files change
  157. func (protectBranch *ProtectedBranch) MergeBlockedByProtectedFiles(changedProtectedFiles []string) bool {
  158. glob := protectBranch.GetProtectedFilePatterns()
  159. if len(glob) == 0 {
  160. return false
  161. }
  162. return len(changedProtectedFiles) > 0
  163. }
  164. // IsProtectedFile return if path is protected
  165. func (protectBranch *ProtectedBranch) IsProtectedFile(patterns []glob.Glob, path string) bool {
  166. if len(patterns) == 0 {
  167. patterns = protectBranch.GetProtectedFilePatterns()
  168. if len(patterns) == 0 {
  169. return false
  170. }
  171. }
  172. lpath := strings.ToLower(strings.TrimSpace(path))
  173. r := false
  174. for _, pat := range patterns {
  175. if pat.Match(lpath) {
  176. r = true
  177. break
  178. }
  179. }
  180. return r
  181. }
  182. // IsUnprotectedFile return if path is unprotected
  183. func (protectBranch *ProtectedBranch) IsUnprotectedFile(patterns []glob.Glob, path string) bool {
  184. if len(patterns) == 0 {
  185. patterns = protectBranch.GetUnprotectedFilePatterns()
  186. if len(patterns) == 0 {
  187. return false
  188. }
  189. }
  190. lpath := strings.ToLower(strings.TrimSpace(path))
  191. r := false
  192. for _, pat := range patterns {
  193. if pat.Match(lpath) {
  194. r = true
  195. break
  196. }
  197. }
  198. return r
  199. }
  200. // GetProtectedBranchBy getting protected branch by ID/Name
  201. func GetProtectedBranchBy(ctx context.Context, repoID int64, branchName string) (*ProtectedBranch, error) {
  202. rel := &ProtectedBranch{RepoID: repoID, BranchName: branchName}
  203. has, err := db.GetByBean(ctx, rel)
  204. if err != nil {
  205. return nil, err
  206. }
  207. if !has {
  208. return nil, nil
  209. }
  210. return rel, nil
  211. }
  212. // WhitelistOptions represent all sorts of whitelists used for protected branches
  213. type WhitelistOptions struct {
  214. UserIDs []int64
  215. TeamIDs []int64
  216. MergeUserIDs []int64
  217. MergeTeamIDs []int64
  218. ApprovalsUserIDs []int64
  219. ApprovalsTeamIDs []int64
  220. }
  221. // UpdateProtectBranch saves branch protection options of repository.
  222. // If ID is 0, it creates a new record. Otherwise, updates existing record.
  223. // This function also performs check if whitelist user and team's IDs have been changed
  224. // to avoid unnecessary whitelist delete and regenerate.
  225. func UpdateProtectBranch(ctx context.Context, repo *repo_model.Repository, protectBranch *ProtectedBranch, opts WhitelistOptions) (err error) {
  226. if err = repo.GetOwner(ctx); err != nil {
  227. return fmt.Errorf("GetOwner: %w", err)
  228. }
  229. whitelist, err := updateUserWhitelist(ctx, repo, protectBranch.WhitelistUserIDs, opts.UserIDs)
  230. if err != nil {
  231. return err
  232. }
  233. protectBranch.WhitelistUserIDs = whitelist
  234. whitelist, err = updateUserWhitelist(ctx, repo, protectBranch.MergeWhitelistUserIDs, opts.MergeUserIDs)
  235. if err != nil {
  236. return err
  237. }
  238. protectBranch.MergeWhitelistUserIDs = whitelist
  239. whitelist, err = updateApprovalWhitelist(ctx, repo, protectBranch.ApprovalsWhitelistUserIDs, opts.ApprovalsUserIDs)
  240. if err != nil {
  241. return err
  242. }
  243. protectBranch.ApprovalsWhitelistUserIDs = whitelist
  244. // if the repo is in an organization
  245. whitelist, err = updateTeamWhitelist(ctx, repo, protectBranch.WhitelistTeamIDs, opts.TeamIDs)
  246. if err != nil {
  247. return err
  248. }
  249. protectBranch.WhitelistTeamIDs = whitelist
  250. whitelist, err = updateTeamWhitelist(ctx, repo, protectBranch.MergeWhitelistTeamIDs, opts.MergeTeamIDs)
  251. if err != nil {
  252. return err
  253. }
  254. protectBranch.MergeWhitelistTeamIDs = whitelist
  255. whitelist, err = updateTeamWhitelist(ctx, repo, protectBranch.ApprovalsWhitelistTeamIDs, opts.ApprovalsTeamIDs)
  256. if err != nil {
  257. return err
  258. }
  259. protectBranch.ApprovalsWhitelistTeamIDs = whitelist
  260. // Make sure protectBranch.ID is not 0 for whitelists
  261. if protectBranch.ID == 0 {
  262. if _, err = db.GetEngine(ctx).Insert(protectBranch); err != nil {
  263. return fmt.Errorf("Insert: %w", err)
  264. }
  265. return nil
  266. }
  267. if _, err = db.GetEngine(ctx).ID(protectBranch.ID).AllCols().Update(protectBranch); err != nil {
  268. return fmt.Errorf("Update: %w", err)
  269. }
  270. return nil
  271. }
  272. // GetProtectedBranches get all protected branches
  273. func GetProtectedBranches(ctx context.Context, repoID int64) ([]*ProtectedBranch, error) {
  274. protectedBranches := make([]*ProtectedBranch, 0)
  275. return protectedBranches, db.GetEngine(ctx).Find(&protectedBranches, &ProtectedBranch{RepoID: repoID})
  276. }
  277. // IsProtectedBranch checks if branch is protected
  278. func IsProtectedBranch(ctx context.Context, repoID int64, branchName string) (bool, error) {
  279. protectedBranch := &ProtectedBranch{
  280. RepoID: repoID,
  281. BranchName: branchName,
  282. }
  283. has, err := db.GetEngine(ctx).Exist(protectedBranch)
  284. if err != nil {
  285. return true, err
  286. }
  287. return has, nil
  288. }
  289. // updateApprovalWhitelist checks whether the user whitelist changed and returns a whitelist with
  290. // the users from newWhitelist which have explicit read or write access to the repo.
  291. func updateApprovalWhitelist(ctx context.Context, repo *repo_model.Repository, currentWhitelist, newWhitelist []int64) (whitelist []int64, err error) {
  292. hasUsersChanged := !util.SliceSortedEqual(currentWhitelist, newWhitelist)
  293. if !hasUsersChanged {
  294. return currentWhitelist, nil
  295. }
  296. whitelist = make([]int64, 0, len(newWhitelist))
  297. for _, userID := range newWhitelist {
  298. if reader, err := access_model.IsRepoReader(ctx, repo, userID); err != nil {
  299. return nil, err
  300. } else if !reader {
  301. continue
  302. }
  303. whitelist = append(whitelist, userID)
  304. }
  305. return whitelist, err
  306. }
  307. // updateUserWhitelist checks whether the user whitelist changed and returns a whitelist with
  308. // the users from newWhitelist which have write access to the repo.
  309. func updateUserWhitelist(ctx context.Context, repo *repo_model.Repository, currentWhitelist, newWhitelist []int64) (whitelist []int64, err error) {
  310. hasUsersChanged := !util.SliceSortedEqual(currentWhitelist, newWhitelist)
  311. if !hasUsersChanged {
  312. return currentWhitelist, nil
  313. }
  314. whitelist = make([]int64, 0, len(newWhitelist))
  315. for _, userID := range newWhitelist {
  316. user, err := user_model.GetUserByID(ctx, userID)
  317. if err != nil {
  318. return nil, fmt.Errorf("GetUserByID [user_id: %d, repo_id: %d]: %w", userID, repo.ID, err)
  319. }
  320. perm, err := access_model.GetUserRepoPermission(ctx, repo, user)
  321. if err != nil {
  322. return nil, fmt.Errorf("GetUserRepoPermission [user_id: %d, repo_id: %d]: %w", userID, repo.ID, err)
  323. }
  324. if !perm.CanWrite(unit.TypeCode) {
  325. continue // Drop invalid user ID
  326. }
  327. whitelist = append(whitelist, userID)
  328. }
  329. return whitelist, err
  330. }
  331. // updateTeamWhitelist checks whether the team whitelist changed and returns a whitelist with
  332. // the teams from newWhitelist which have write access to the repo.
  333. func updateTeamWhitelist(ctx context.Context, repo *repo_model.Repository, currentWhitelist, newWhitelist []int64) (whitelist []int64, err error) {
  334. hasTeamsChanged := !util.SliceSortedEqual(currentWhitelist, newWhitelist)
  335. if !hasTeamsChanged {
  336. return currentWhitelist, nil
  337. }
  338. teams, err := organization.GetTeamsWithAccessToRepo(ctx, repo.OwnerID, repo.ID, perm.AccessModeRead)
  339. if err != nil {
  340. return nil, fmt.Errorf("GetTeamsWithAccessToRepo [org_id: %d, repo_id: %d]: %w", repo.OwnerID, repo.ID, err)
  341. }
  342. whitelist = make([]int64, 0, len(teams))
  343. for i := range teams {
  344. if util.SliceContains(newWhitelist, teams[i].ID) {
  345. whitelist = append(whitelist, teams[i].ID)
  346. }
  347. }
  348. return whitelist, err
  349. }
  350. // DeleteProtectedBranch removes ProtectedBranch relation between the user and repository.
  351. func DeleteProtectedBranch(ctx context.Context, repoID, id int64) (err error) {
  352. protectedBranch := &ProtectedBranch{
  353. RepoID: repoID,
  354. ID: id,
  355. }
  356. if affected, err := db.GetEngine(ctx).Delete(protectedBranch); err != nil {
  357. return err
  358. } else if affected != 1 {
  359. return fmt.Errorf("delete protected branch ID(%v) failed", id)
  360. }
  361. return nil
  362. }
  363. // DeletedBranch struct
  364. type DeletedBranch struct {
  365. ID int64 `xorm:"pk autoincr"`
  366. RepoID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
  367. Name string `xorm:"UNIQUE(s) NOT NULL"`
  368. Commit string `xorm:"UNIQUE(s) NOT NULL"`
  369. DeletedByID int64 `xorm:"INDEX"`
  370. DeletedBy *user_model.User `xorm:"-"`
  371. DeletedUnix timeutil.TimeStamp `xorm:"INDEX created"`
  372. }
  373. // AddDeletedBranch adds a deleted branch to the database
  374. func AddDeletedBranch(ctx context.Context, repoID int64, branchName, commit string, deletedByID int64) error {
  375. deletedBranch := &DeletedBranch{
  376. RepoID: repoID,
  377. Name: branchName,
  378. Commit: commit,
  379. DeletedByID: deletedByID,
  380. }
  381. _, err := db.GetEngine(ctx).Insert(deletedBranch)
  382. return err
  383. }
  384. // GetDeletedBranches returns all the deleted branches
  385. func GetDeletedBranches(ctx context.Context, repoID int64) ([]*DeletedBranch, error) {
  386. deletedBranches := make([]*DeletedBranch, 0)
  387. return deletedBranches, db.GetEngine(ctx).Where("repo_id = ?", repoID).Desc("deleted_unix").Find(&deletedBranches)
  388. }
  389. // GetDeletedBranchByID get a deleted branch by its ID
  390. func GetDeletedBranchByID(ctx context.Context, repoID, id int64) (*DeletedBranch, error) {
  391. deletedBranch := &DeletedBranch{}
  392. has, err := db.GetEngine(ctx).Where("repo_id = ?", repoID).And("id = ?", id).Get(deletedBranch)
  393. if err != nil {
  394. return nil, err
  395. }
  396. if !has {
  397. return nil, nil
  398. }
  399. return deletedBranch, nil
  400. }
  401. // RemoveDeletedBranchByID removes a deleted branch from the database
  402. func RemoveDeletedBranchByID(ctx context.Context, repoID, id int64) (err error) {
  403. deletedBranch := &DeletedBranch{
  404. RepoID: repoID,
  405. ID: id,
  406. }
  407. if affected, err := db.GetEngine(ctx).Delete(deletedBranch); err != nil {
  408. return err
  409. } else if affected != 1 {
  410. return fmt.Errorf("remove deleted branch ID(%v) failed", id)
  411. }
  412. return nil
  413. }
  414. // LoadUser loads the user that deleted the branch
  415. // When there's no user found it returns a user_model.NewGhostUser
  416. func (deletedBranch *DeletedBranch) LoadUser(ctx context.Context) {
  417. user, err := user_model.GetUserByID(ctx, deletedBranch.DeletedByID)
  418. if err != nil {
  419. user = user_model.NewGhostUser()
  420. }
  421. deletedBranch.DeletedBy = user
  422. }
  423. // RemoveDeletedBranchByName removes all deleted branches
  424. func RemoveDeletedBranchByName(ctx context.Context, repoID int64, branch string) error {
  425. _, err := db.GetEngine(ctx).Where("repo_id=? AND name=?", repoID, branch).Delete(new(DeletedBranch))
  426. return err
  427. }
  428. // RemoveOldDeletedBranches removes old deleted branches
  429. func RemoveOldDeletedBranches(ctx context.Context, olderThan time.Duration) {
  430. // Nothing to do for shutdown or terminate
  431. log.Trace("Doing: DeletedBranchesCleanup")
  432. deleteBefore := time.Now().Add(-olderThan)
  433. _, err := db.GetEngine(ctx).Where("deleted_unix < ?", deleteBefore.Unix()).Delete(new(DeletedBranch))
  434. if err != nil {
  435. log.Error("DeletedBranchesCleanup: %v", err)
  436. }
  437. }
  438. // RenamedBranch provide renamed branch log
  439. // will check it when a branch can't be found
  440. type RenamedBranch struct {
  441. ID int64 `xorm:"pk autoincr"`
  442. RepoID int64 `xorm:"INDEX NOT NULL"`
  443. From string
  444. To string
  445. CreatedUnix timeutil.TimeStamp `xorm:"created"`
  446. }
  447. // FindRenamedBranch check if a branch was renamed
  448. func FindRenamedBranch(ctx context.Context, repoID int64, from string) (branch *RenamedBranch, exist bool, err error) {
  449. branch = &RenamedBranch{
  450. RepoID: repoID,
  451. From: from,
  452. }
  453. exist, err = db.GetEngine(ctx).Get(branch)
  454. return branch, exist, err
  455. }
  456. // RenameBranch rename a branch
  457. func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to string, gitAction func(isDefault bool) error) (err error) {
  458. ctx, committer, err := db.TxContext(ctx)
  459. if err != nil {
  460. return err
  461. }
  462. defer committer.Close()
  463. sess := db.GetEngine(ctx)
  464. // 1. update default branch if needed
  465. isDefault := repo.DefaultBranch == from
  466. if isDefault {
  467. repo.DefaultBranch = to
  468. _, err = sess.ID(repo.ID).Cols("default_branch").Update(repo)
  469. if err != nil {
  470. return err
  471. }
  472. }
  473. // 2. Update protected branch if needed
  474. protectedBranch, err := GetProtectedBranchBy(ctx, repo.ID, from)
  475. if err != nil {
  476. return err
  477. }
  478. if protectedBranch != nil {
  479. protectedBranch.BranchName = to
  480. _, err = sess.ID(protectedBranch.ID).Cols("branch_name").Update(protectedBranch)
  481. if err != nil {
  482. return err
  483. }
  484. }
  485. // 3. Update all not merged pull request base branch name
  486. _, err = sess.Table("pull_request").Where("base_repo_id=? AND base_branch=? AND has_merged=?",
  487. repo.ID, from, false).
  488. Update(map[string]interface{}{"base_branch": to})
  489. if err != nil {
  490. return err
  491. }
  492. // 4. do git action
  493. if err = gitAction(isDefault); err != nil {
  494. return err
  495. }
  496. // 5. insert renamed branch record
  497. renamedBranch := &RenamedBranch{
  498. RepoID: repo.ID,
  499. From: from,
  500. To: to,
  501. }
  502. err = db.Insert(ctx, renamedBranch)
  503. if err != nil {
  504. return err
  505. }
  506. return committer.Commit()
  507. }