A fork of Gitea (see branch `mj`) adding Majority Judgment Polls 𐄷 over Issues and Merge Requests. https://git.mieuxvoter.fr
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

241 lines
7.7 KiB

  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 repository
  5. import (
  6. "container/list"
  7. "fmt"
  8. "time"
  9. "code.gitea.io/gitea/models"
  10. "code.gitea.io/gitea/modules/cache"
  11. "code.gitea.io/gitea/modules/git"
  12. "code.gitea.io/gitea/modules/graceful"
  13. "code.gitea.io/gitea/modules/log"
  14. "code.gitea.io/gitea/modules/notification"
  15. "code.gitea.io/gitea/modules/queue"
  16. "code.gitea.io/gitea/modules/repofiles"
  17. repo_module "code.gitea.io/gitea/modules/repository"
  18. "code.gitea.io/gitea/modules/setting"
  19. pull_service "code.gitea.io/gitea/services/pull"
  20. )
  21. // pushQueue represents a queue to handle update pull request tests
  22. var pushQueue queue.Queue
  23. // handle passed PR IDs and test the PRs
  24. func handle(data ...queue.Data) {
  25. for _, datum := range data {
  26. opts := datum.([]*repo_module.PushUpdateOptions)
  27. if err := pushUpdates(opts); err != nil {
  28. log.Error("pushUpdate failed: %v", err)
  29. }
  30. }
  31. }
  32. func initPushQueue() error {
  33. pushQueue = queue.CreateQueue("push_update", handle, []*repo_module.PushUpdateOptions{}).(queue.Queue)
  34. if pushQueue == nil {
  35. return fmt.Errorf("Unable to create push_update Queue")
  36. }
  37. go graceful.GetManager().RunWithShutdownFns(pushQueue.Run)
  38. return nil
  39. }
  40. // PushUpdate is an alias of PushUpdates for single push update options
  41. func PushUpdate(opts *repo_module.PushUpdateOptions) error {
  42. return PushUpdates([]*repo_module.PushUpdateOptions{opts})
  43. }
  44. // PushUpdates adds a push update to push queue
  45. func PushUpdates(opts []*repo_module.PushUpdateOptions) error {
  46. if len(opts) == 0 {
  47. return nil
  48. }
  49. for _, opt := range opts {
  50. if opt.IsNewRef() && opt.IsDelRef() {
  51. return fmt.Errorf("Old and new revisions are both %s", git.EmptySHA)
  52. }
  53. }
  54. return pushQueue.Push(opts)
  55. }
  56. // pushUpdates generates push action history feeds for push updating multiple refs
  57. func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
  58. if len(optsList) == 0 {
  59. return nil
  60. }
  61. repo, err := models.GetRepositoryByOwnerAndName(optsList[0].RepoUserName, optsList[0].RepoName)
  62. if err != nil {
  63. return fmt.Errorf("GetRepositoryByOwnerAndName failed: %v", err)
  64. }
  65. repoPath := repo.RepoPath()
  66. gitRepo, err := git.OpenRepository(repoPath)
  67. if err != nil {
  68. return fmt.Errorf("OpenRepository: %v", err)
  69. }
  70. defer gitRepo.Close()
  71. if err = repo.UpdateSize(models.DefaultDBContext()); err != nil {
  72. log.Error("Failed to update size for repository: %v", err)
  73. }
  74. addTags := make([]string, 0, len(optsList))
  75. delTags := make([]string, 0, len(optsList))
  76. var pusher *models.User
  77. for _, opts := range optsList {
  78. if opts.IsNewRef() && opts.IsDelRef() {
  79. return fmt.Errorf("Old and new revisions are both %s", git.EmptySHA)
  80. }
  81. var commits = &repo_module.PushCommits{}
  82. if opts.IsTag() { // If is tag reference
  83. if pusher == nil || pusher.ID != opts.PusherID {
  84. var err error
  85. if pusher, err = models.GetUserByID(opts.PusherID); err != nil {
  86. return err
  87. }
  88. }
  89. tagName := opts.TagName()
  90. if opts.IsDelRef() {
  91. notification.NotifyPushCommits(
  92. pusher, repo,
  93. &repo_module.PushUpdateOptions{
  94. RefFullName: git.TagPrefix + tagName,
  95. OldCommitID: opts.OldCommitID,
  96. NewCommitID: git.EmptySHA,
  97. }, repo_module.NewPushCommits())
  98. delTags = append(delTags, tagName)
  99. notification.NotifyDeleteRef(pusher, repo, "tag", opts.RefFullName)
  100. } else { // is new tag
  101. notification.NotifyPushCommits(
  102. pusher, repo,
  103. &repo_module.PushUpdateOptions{
  104. RefFullName: git.TagPrefix + tagName,
  105. OldCommitID: git.EmptySHA,
  106. NewCommitID: opts.NewCommitID,
  107. }, repo_module.NewPushCommits())
  108. addTags = append(addTags, tagName)
  109. notification.NotifyCreateRef(pusher, repo, "tag", opts.RefFullName)
  110. }
  111. } else if opts.IsBranch() { // If is branch reference
  112. if pusher == nil || pusher.ID != opts.PusherID {
  113. var err error
  114. if pusher, err = models.GetUserByID(opts.PusherID); err != nil {
  115. return err
  116. }
  117. }
  118. branch := opts.BranchName()
  119. if !opts.IsDelRef() {
  120. log.Trace("TriggerTask '%s/%s' by %s", repo.Name, branch, pusher.Name)
  121. go pull_service.AddTestPullRequestTask(pusher, repo.ID, branch, true, opts.OldCommitID, opts.NewCommitID)
  122. newCommit, err := gitRepo.GetCommit(opts.NewCommitID)
  123. if err != nil {
  124. return fmt.Errorf("gitRepo.GetCommit: %v", err)
  125. }
  126. refName := opts.RefName()
  127. // Push new branch.
  128. var l *list.List
  129. if opts.IsNewRef() {
  130. if repo.IsEmpty { // Change default branch and empty status only if pushed ref is non-empty branch.
  131. repo.DefaultBranch = refName
  132. repo.IsEmpty = false
  133. if repo.DefaultBranch != setting.Repository.DefaultBranch {
  134. if err := gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil {
  135. if !git.IsErrUnsupportedVersion(err) {
  136. return err
  137. }
  138. }
  139. }
  140. // Update the is empty and default_branch columns
  141. if err := models.UpdateRepositoryCols(repo, "default_branch", "is_empty"); err != nil {
  142. return fmt.Errorf("UpdateRepositoryCols: %v", err)
  143. }
  144. }
  145. l, err = newCommit.CommitsBeforeLimit(10)
  146. if err != nil {
  147. return fmt.Errorf("newCommit.CommitsBeforeLimit: %v", err)
  148. }
  149. notification.NotifyCreateRef(pusher, repo, "branch", opts.RefFullName)
  150. } else {
  151. l, err = newCommit.CommitsBeforeUntil(opts.OldCommitID)
  152. if err != nil {
  153. return fmt.Errorf("newCommit.CommitsBeforeUntil: %v", err)
  154. }
  155. isForce, err := repo_module.IsForcePush(opts)
  156. if err != nil {
  157. log.Error("isForcePush %s:%s failed: %v", repo.FullName(), branch, err)
  158. }
  159. if isForce {
  160. log.Trace("Push %s is a force push", opts.NewCommitID)
  161. cache.Remove(repo.GetCommitsCountCacheKey(opts.RefName(), true))
  162. } else {
  163. // TODO: increment update the commit count cache but not remove
  164. cache.Remove(repo.GetCommitsCountCacheKey(opts.RefName(), true))
  165. }
  166. }
  167. commits = repo_module.ListToPushCommits(l)
  168. if err := repofiles.UpdateIssuesCommit(pusher, repo, commits.Commits, refName); err != nil {
  169. log.Error("updateIssuesCommit: %v", err)
  170. }
  171. if len(commits.Commits) > setting.UI.FeedMaxCommitNum {
  172. commits.Commits = commits.Commits[:setting.UI.FeedMaxCommitNum]
  173. }
  174. commits.CompareURL = repo.ComposeCompareURL(opts.OldCommitID, opts.NewCommitID)
  175. notification.NotifyPushCommits(pusher, repo, opts, commits)
  176. if err = models.RemoveDeletedBranch(repo.ID, branch); err != nil {
  177. log.Error("models.RemoveDeletedBranch %s/%s failed: %v", repo.ID, branch, err)
  178. }
  179. // Cache for big repository
  180. if err := repo_module.CacheRef(graceful.GetManager().HammerContext(), repo, gitRepo, opts.RefFullName); err != nil {
  181. log.Error("repo_module.CacheRef %s/%s failed: %v", repo.ID, branch, err)
  182. }
  183. } else {
  184. notification.NotifyDeleteRef(pusher, repo, "branch", opts.RefFullName)
  185. if err = pull_service.CloseBranchPulls(pusher, repo.ID, branch); err != nil {
  186. // close all related pulls
  187. log.Error("close related pull request failed: %v", err)
  188. }
  189. }
  190. // Even if user delete a branch on a repository which he didn't watch, he will be watch that.
  191. if err = models.WatchIfAuto(opts.PusherID, repo.ID, true); err != nil {
  192. log.Warn("Fail to perform auto watch on user %v for repo %v: %v", opts.PusherID, repo.ID, err)
  193. }
  194. } else {
  195. log.Trace("Non-tag and non-branch commits pushed.")
  196. }
  197. }
  198. if err := repo_module.PushUpdateAddDeleteTags(repo, gitRepo, addTags, delTags); err != nil {
  199. return fmt.Errorf("PushUpdateAddDeleteTags: %v", err)
  200. }
  201. // Change repository last updated time.
  202. if err := models.UpdateRepositoryUpdatedTime(repo.ID, time.Now()); err != nil {
  203. return fmt.Errorf("UpdateRepositoryUpdatedTime: %v", err)
  204. }
  205. return nil
  206. }