gitea源码

update.go 5.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. // Copyright 2020 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package pull
  4. import (
  5. "context"
  6. "errors"
  7. "fmt"
  8. git_model "code.gitea.io/gitea/models/git"
  9. issues_model "code.gitea.io/gitea/models/issues"
  10. access_model "code.gitea.io/gitea/models/perm/access"
  11. repo_model "code.gitea.io/gitea/models/repo"
  12. "code.gitea.io/gitea/models/unit"
  13. user_model "code.gitea.io/gitea/models/user"
  14. "code.gitea.io/gitea/modules/git"
  15. "code.gitea.io/gitea/modules/globallock"
  16. "code.gitea.io/gitea/modules/log"
  17. "code.gitea.io/gitea/modules/repository"
  18. )
  19. // Update updates pull request with base branch.
  20. func Update(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, message string, rebase bool) error {
  21. if pr.Flow == issues_model.PullRequestFlowAGit {
  22. // TODO: update of agit flow pull request's head branch is unsupported
  23. return errors.New("update of agit flow pull request's head branch is unsupported")
  24. }
  25. releaser, err := globallock.Lock(ctx, getPullWorkingLockKey(pr.ID))
  26. if err != nil {
  27. log.Error("lock.Lock(): %v", err)
  28. return fmt.Errorf("lock.Lock: %w", err)
  29. }
  30. defer releaser()
  31. diffCount, err := GetDiverging(ctx, pr)
  32. if err != nil {
  33. return err
  34. } else if diffCount.Behind == 0 {
  35. return fmt.Errorf("HeadBranch of PR %d is up to date", pr.Index)
  36. }
  37. if err := pr.LoadBaseRepo(ctx); err != nil {
  38. log.Error("unable to load BaseRepo for %-v during update-by-merge: %v", pr, err)
  39. return fmt.Errorf("unable to load BaseRepo for PR[%d] during update-by-merge: %w", pr.ID, err)
  40. }
  41. if err := pr.LoadHeadRepo(ctx); err != nil {
  42. log.Error("unable to load HeadRepo for PR %-v during update-by-merge: %v", pr, err)
  43. return fmt.Errorf("unable to load HeadRepo for PR[%d] during update-by-merge: %w", pr.ID, err)
  44. }
  45. if pr.HeadRepo == nil {
  46. // LoadHeadRepo will swallow ErrRepoNotExist so if pr.HeadRepo is still nil recreate the error
  47. err := repo_model.ErrRepoNotExist{
  48. ID: pr.HeadRepoID,
  49. }
  50. log.Error("unable to load HeadRepo for PR %-v during update-by-merge: %v", pr, err)
  51. return fmt.Errorf("unable to load HeadRepo for PR[%d] during update-by-merge: %w", pr.ID, err)
  52. }
  53. defer func() {
  54. go AddTestPullRequestTask(TestPullRequestOptions{
  55. RepoID: pr.BaseRepo.ID,
  56. Doer: doer,
  57. Branch: pr.BaseBranch,
  58. IsSync: false,
  59. IsForcePush: false,
  60. OldCommitID: "",
  61. NewCommitID: "",
  62. })
  63. }()
  64. if rebase {
  65. return updateHeadByRebaseOnToBase(ctx, pr, doer)
  66. }
  67. // TODO: FakePR: it is somewhat hacky, but it is the only way to "merge" at the moment
  68. // ideally in the future the "merge" functions should be refactored to decouple from the PullRequest
  69. // now use a fake reverse PR to switch head&base repos/branches
  70. reversePR := &issues_model.PullRequest{
  71. ID: pr.ID,
  72. HeadRepoID: pr.BaseRepoID,
  73. HeadRepo: pr.BaseRepo,
  74. HeadBranch: pr.BaseBranch,
  75. BaseRepoID: pr.HeadRepoID,
  76. BaseRepo: pr.HeadRepo,
  77. BaseBranch: pr.HeadBranch,
  78. }
  79. _, err = doMergeAndPush(ctx, reversePR, doer, repo_model.MergeStyleMerge, "", message, repository.PushTriggerPRUpdateWithBase)
  80. return err
  81. }
  82. // IsUserAllowedToUpdate check if user is allowed to update PR with given permissions and branch protections
  83. func IsUserAllowedToUpdate(ctx context.Context, pull *issues_model.PullRequest, user *user_model.User) (mergeAllowed, rebaseAllowed bool, err error) {
  84. if pull.Flow == issues_model.PullRequestFlowAGit {
  85. return false, false, nil
  86. }
  87. if user == nil {
  88. return false, false, nil
  89. }
  90. headRepoPerm, err := access_model.GetUserRepoPermission(ctx, pull.HeadRepo, user)
  91. if err != nil {
  92. if repo_model.IsErrUnitTypeNotExist(err) {
  93. return false, false, nil
  94. }
  95. return false, false, err
  96. }
  97. if err := pull.LoadBaseRepo(ctx); err != nil {
  98. return false, false, err
  99. }
  100. pr := &issues_model.PullRequest{
  101. HeadRepoID: pull.BaseRepoID,
  102. HeadRepo: pull.BaseRepo,
  103. BaseRepoID: pull.HeadRepoID,
  104. BaseRepo: pull.HeadRepo,
  105. HeadBranch: pull.BaseBranch,
  106. BaseBranch: pull.HeadBranch,
  107. }
  108. pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch)
  109. if err != nil {
  110. return false, false, err
  111. }
  112. if err := pr.LoadBaseRepo(ctx); err != nil {
  113. return false, false, err
  114. }
  115. prUnit, err := pr.BaseRepo.GetUnit(ctx, unit.TypePullRequests)
  116. if err != nil {
  117. if repo_model.IsErrUnitTypeNotExist(err) {
  118. return false, false, nil
  119. }
  120. log.Error("pr.BaseRepo.GetUnit(unit.TypePullRequests): %v", err)
  121. return false, false, err
  122. }
  123. rebaseAllowed = prUnit.PullRequestsConfig().AllowRebaseUpdate
  124. // If branch protected, disable rebase unless user is whitelisted to force push (which extends regular push)
  125. if pb != nil {
  126. pb.Repo = pull.BaseRepo
  127. if !pb.CanUserForcePush(ctx, user) {
  128. rebaseAllowed = false
  129. }
  130. }
  131. baseRepoPerm, err := access_model.GetUserRepoPermission(ctx, pull.BaseRepo, user)
  132. if err != nil {
  133. return false, false, err
  134. }
  135. mergeAllowed, err = IsUserAllowedToMerge(ctx, pr, headRepoPerm, user)
  136. if err != nil {
  137. return false, false, err
  138. }
  139. if pull.AllowMaintainerEdit {
  140. mergeAllowedMaintainer, err := IsUserAllowedToMerge(ctx, pr, baseRepoPerm, user)
  141. if err != nil {
  142. return false, false, err
  143. }
  144. mergeAllowed = mergeAllowed || mergeAllowedMaintainer
  145. }
  146. return mergeAllowed, rebaseAllowed, nil
  147. }
  148. // GetDiverging determines how many commits a PR is ahead or behind the PR base branch
  149. func GetDiverging(ctx context.Context, pr *issues_model.PullRequest) (*git.DivergeObject, error) {
  150. log.Trace("GetDiverging[%-v]: compare commits", pr)
  151. prCtx, cancel, err := createTemporaryRepoForPR(ctx, pr)
  152. if err != nil {
  153. if !git_model.IsErrBranchNotExist(err) {
  154. log.Error("CreateTemporaryRepoForPR %-v: %v", pr, err)
  155. }
  156. return nil, err
  157. }
  158. defer cancel()
  159. diff, err := git.GetDivergingCommits(ctx, prCtx.tmpBasePath, baseBranch, trackingBranch)
  160. return &diff, err
  161. }