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.

update.go 4.0 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. // Copyright 2020 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package pull
  5. import (
  6. "fmt"
  7. "strconv"
  8. "strings"
  9. "code.gitea.io/gitea/models"
  10. "code.gitea.io/gitea/modules/git"
  11. "code.gitea.io/gitea/modules/log"
  12. )
  13. // Update updates pull request with base branch.
  14. func Update(pull *models.PullRequest, doer *models.User, message string) error {
  15. //use merge functions but switch repo's and branch's
  16. pr := &models.PullRequest{
  17. HeadRepoID: pull.BaseRepoID,
  18. BaseRepoID: pull.HeadRepoID,
  19. HeadBranch: pull.BaseBranch,
  20. BaseBranch: pull.HeadBranch,
  21. }
  22. if err := pr.LoadHeadRepo(); err != nil {
  23. log.Error("LoadHeadRepo: %v", err)
  24. return fmt.Errorf("LoadHeadRepo: %v", err)
  25. } else if err = pr.LoadBaseRepo(); err != nil {
  26. log.Error("LoadBaseRepo: %v", err)
  27. return fmt.Errorf("LoadBaseRepo: %v", err)
  28. }
  29. diffCount, err := GetDiverging(pull)
  30. if err != nil {
  31. return err
  32. } else if diffCount.Behind == 0 {
  33. return fmt.Errorf("HeadBranch of PR %d is up to date", pull.Index)
  34. }
  35. defer func() {
  36. go AddTestPullRequestTask(doer, pr.HeadRepo.ID, pr.HeadBranch, false, "", "")
  37. }()
  38. return rawMerge(pr, doer, models.MergeStyleMerge, message)
  39. }
  40. // IsUserAllowedToUpdate check if user is allowed to update PR with given permissions and branch protections
  41. func IsUserAllowedToUpdate(pull *models.PullRequest, user *models.User) (bool, error) {
  42. headRepoPerm, err := models.GetUserRepoPermission(pull.HeadRepo, user)
  43. if err != nil {
  44. return false, err
  45. }
  46. pr := &models.PullRequest{
  47. HeadRepoID: pull.BaseRepoID,
  48. BaseRepoID: pull.HeadRepoID,
  49. HeadBranch: pull.BaseBranch,
  50. BaseBranch: pull.HeadBranch,
  51. }
  52. return IsUserAllowedToMerge(pr, headRepoPerm, user)
  53. }
  54. // GetDiverging determines how many commits a PR is ahead or behind the PR base branch
  55. func GetDiverging(pr *models.PullRequest) (*git.DivergeObject, error) {
  56. log.Trace("PushToBaseRepo[%d]: pushing commits to base repo '%s'", pr.BaseRepoID, pr.GetGitRefName())
  57. if err := pr.LoadBaseRepo(); err != nil {
  58. return nil, err
  59. }
  60. if err := pr.LoadHeadRepo(); err != nil {
  61. return nil, err
  62. }
  63. headRepoPath := pr.HeadRepo.RepoPath()
  64. headGitRepo, err := git.OpenRepository(headRepoPath)
  65. if err != nil {
  66. return nil, fmt.Errorf("OpenRepository: %v", err)
  67. }
  68. defer headGitRepo.Close()
  69. if pr.IsSameRepo() {
  70. diff, err := git.GetDivergingCommits(pr.HeadRepo.RepoPath(), pr.BaseBranch, pr.HeadBranch)
  71. return &diff, err
  72. }
  73. tmpRemoteName := fmt.Sprintf("tmp-pull-%d-base", pr.ID)
  74. if err = headGitRepo.AddRemote(tmpRemoteName, pr.BaseRepo.RepoPath(), true); err != nil {
  75. return nil, fmt.Errorf("headGitRepo.AddRemote: %v", err)
  76. }
  77. // Make sure to remove the remote even if the push fails
  78. defer func() {
  79. if err := headGitRepo.RemoveRemote(tmpRemoteName); err != nil {
  80. log.Error("CountDiverging: RemoveRemote: %s", err)
  81. }
  82. }()
  83. // $(git rev-list --count tmp-pull-1-base/master..feature) commits ahead of master
  84. ahead, errorAhead := checkDivergence(headRepoPath, fmt.Sprintf("%s/%s", tmpRemoteName, pr.BaseBranch), pr.HeadBranch)
  85. if errorAhead != nil {
  86. return &git.DivergeObject{}, errorAhead
  87. }
  88. // $(git rev-list --count feature..tmp-pull-1-base/master) commits behind master
  89. behind, errorBehind := checkDivergence(headRepoPath, pr.HeadBranch, fmt.Sprintf("%s/%s", tmpRemoteName, pr.BaseBranch))
  90. if errorBehind != nil {
  91. return &git.DivergeObject{}, errorBehind
  92. }
  93. return &git.DivergeObject{Ahead: ahead, Behind: behind}, nil
  94. }
  95. func checkDivergence(repoPath string, baseBranch string, targetBranch string) (int, error) {
  96. branches := fmt.Sprintf("%s..%s", baseBranch, targetBranch)
  97. cmd := git.NewCommand("rev-list", "--count", branches)
  98. stdout, err := cmd.RunInDir(repoPath)
  99. if err != nil {
  100. return -1, err
  101. }
  102. outInteger, errInteger := strconv.Atoi(strings.Trim(stdout, "\n"))
  103. if errInteger != nil {
  104. return -1, errInteger
  105. }
  106. return outInteger, nil
  107. }