gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. // Copyright 2024 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package repo
  4. import (
  5. "errors"
  6. "net/http"
  7. "net/url"
  8. "code.gitea.io/gitea/models/db"
  9. git_model "code.gitea.io/gitea/models/git"
  10. "code.gitea.io/gitea/models/organization"
  11. repo_model "code.gitea.io/gitea/models/repo"
  12. user_model "code.gitea.io/gitea/models/user"
  13. "code.gitea.io/gitea/modules/log"
  14. "code.gitea.io/gitea/modules/optional"
  15. "code.gitea.io/gitea/modules/repository"
  16. "code.gitea.io/gitea/modules/setting"
  17. "code.gitea.io/gitea/modules/structs"
  18. "code.gitea.io/gitea/modules/templates"
  19. "code.gitea.io/gitea/modules/web"
  20. "code.gitea.io/gitea/services/context"
  21. "code.gitea.io/gitea/services/forms"
  22. repo_service "code.gitea.io/gitea/services/repository"
  23. )
  24. const (
  25. tplFork templates.TplName = "repo/pulls/fork"
  26. )
  27. func getForkRepository(ctx *context.Context) *repo_model.Repository {
  28. forkRepo := ctx.Repo.Repository
  29. if ctx.Written() {
  30. return nil
  31. }
  32. if forkRepo.IsEmpty {
  33. log.Trace("Empty repository %-v", forkRepo)
  34. ctx.NotFound(nil)
  35. return nil
  36. }
  37. if err := forkRepo.LoadOwner(ctx); err != nil {
  38. ctx.ServerError("LoadOwner", err)
  39. return nil
  40. }
  41. ctx.Data["repo_name"] = forkRepo.Name
  42. ctx.Data["description"] = forkRepo.Description
  43. ctx.Data["IsPrivate"] = forkRepo.IsPrivate || forkRepo.Owner.Visibility == structs.VisibleTypePrivate
  44. canForkToUser := repository.CanUserForkBetweenOwners(forkRepo.OwnerID, ctx.Doer.ID) && !repo_model.HasForkedRepo(ctx, ctx.Doer.ID, forkRepo.ID)
  45. ctx.Data["ForkRepo"] = forkRepo
  46. ownedOrgs, err := organization.GetOrgsCanCreateRepoByUserID(ctx, ctx.Doer.ID)
  47. if err != nil {
  48. ctx.ServerError("GetOrgsCanCreateRepoByUserID", err)
  49. return nil
  50. }
  51. var orgs []*organization.Organization
  52. for _, org := range ownedOrgs {
  53. if forkRepo.OwnerID != org.ID && !repo_model.HasForkedRepo(ctx, org.ID, forkRepo.ID) {
  54. orgs = append(orgs, org)
  55. }
  56. }
  57. traverseParentRepo := forkRepo
  58. for {
  59. if !repository.CanUserForkBetweenOwners(ctx.Doer.ID, traverseParentRepo.OwnerID) {
  60. canForkToUser = false
  61. } else {
  62. for i, org := range orgs {
  63. if org.ID == traverseParentRepo.OwnerID {
  64. orgs = append(orgs[:i], orgs[i+1:]...)
  65. break
  66. }
  67. }
  68. }
  69. if !traverseParentRepo.IsFork {
  70. break
  71. }
  72. traverseParentRepo, err = repo_model.GetRepositoryByID(ctx, traverseParentRepo.ForkID)
  73. if err != nil {
  74. ctx.ServerError("GetRepositoryByID", err)
  75. return nil
  76. }
  77. }
  78. ctx.Data["CanForkToUser"] = canForkToUser
  79. ctx.Data["Orgs"] = orgs
  80. // TODO: this message should only be shown for the "current doer" when it is selected, just like the "new repo" page.
  81. // msg := ctx.TrN(maxCreationLimit, "repo.form.reach_limit_of_creation_1", "repo.form.reach_limit_of_creation_n", ctx.Doer.MaxCreationLimit())
  82. if canForkToUser {
  83. ctx.Data["ContextUser"] = ctx.Doer
  84. ctx.Data["CanForkRepoInNewOwner"] = true
  85. } else if len(orgs) > 0 {
  86. ctx.Data["ContextUser"] = orgs[0]
  87. ctx.Data["CanForkRepoInNewOwner"] = true
  88. } else {
  89. ctx.Data["CanForkRepoInNewOwner"] = false
  90. ctx.Flash.Error(ctx.Tr("repo.fork_no_valid_owners"), true)
  91. return nil
  92. }
  93. branches, err := git_model.FindBranchNames(ctx, git_model.FindBranchOptions{
  94. RepoID: ctx.Repo.Repository.ID,
  95. ListOptions: db.ListOptionsAll,
  96. IsDeletedBranch: optional.Some(false),
  97. // Add it as the first option
  98. ExcludeBranchNames: []string{ctx.Repo.Repository.DefaultBranch},
  99. })
  100. if err != nil {
  101. ctx.ServerError("FindBranchNames", err)
  102. return nil
  103. }
  104. ctx.Data["Branches"] = append([]string{ctx.Repo.Repository.DefaultBranch}, branches...)
  105. return forkRepo
  106. }
  107. // Fork render repository fork page
  108. func Fork(ctx *context.Context) {
  109. ctx.Data["Title"] = ctx.Tr("new_fork")
  110. getForkRepository(ctx)
  111. if ctx.Written() {
  112. return
  113. }
  114. ctx.HTML(http.StatusOK, tplFork)
  115. }
  116. // ForkPost response for forking a repository
  117. func ForkPost(ctx *context.Context) {
  118. form := web.GetForm(ctx).(*forms.CreateRepoForm)
  119. ctx.Data["Title"] = ctx.Tr("new_fork")
  120. ctxUser := checkContextUser(ctx, form.UID)
  121. if ctx.Written() {
  122. return
  123. }
  124. forkRepo := getForkRepository(ctx)
  125. if ctx.Written() {
  126. return
  127. }
  128. ctx.Data["ContextUser"] = ctxUser
  129. if ctx.HasError() {
  130. ctx.JSONError(ctx.GetErrMsg())
  131. return
  132. }
  133. var err error
  134. traverseParentRepo := forkRepo
  135. for {
  136. if !repository.CanUserForkBetweenOwners(ctxUser.ID, traverseParentRepo.OwnerID) {
  137. ctx.JSONError(ctx.Tr("repo.settings.new_owner_has_same_repo"))
  138. return
  139. }
  140. repo := repo_model.GetForkedRepo(ctx, ctxUser.ID, traverseParentRepo.ID)
  141. if repo != nil {
  142. ctx.JSONRedirect(ctxUser.HomeLink() + "/" + url.PathEscape(repo.Name))
  143. return
  144. }
  145. if !traverseParentRepo.IsFork {
  146. break
  147. }
  148. traverseParentRepo, err = repo_model.GetRepositoryByID(ctx, traverseParentRepo.ForkID)
  149. if err != nil {
  150. ctx.ServerError("GetRepositoryByID", err)
  151. return
  152. }
  153. }
  154. // Check if user is allowed to create repo's on the organization.
  155. if ctxUser.IsOrganization() {
  156. isAllowedToFork, err := organization.OrgFromUser(ctxUser).CanCreateOrgRepo(ctx, ctx.Doer.ID)
  157. if err != nil {
  158. ctx.ServerError("CanCreateOrgRepo", err)
  159. return
  160. } else if !isAllowedToFork {
  161. ctx.HTTPError(http.StatusForbidden)
  162. return
  163. }
  164. }
  165. repo := ForkRepoTo(ctx, ctxUser, repo_service.ForkRepoOptions{
  166. BaseRepo: forkRepo,
  167. Name: form.RepoName,
  168. Description: form.Description,
  169. SingleBranch: form.ForkSingleBranch,
  170. })
  171. if ctx.Written() {
  172. return
  173. }
  174. ctx.JSONRedirect(ctxUser.HomeLink() + "/" + url.PathEscape(repo.Name))
  175. }
  176. func ForkRepoTo(ctx *context.Context, owner *user_model.User, forkOpts repo_service.ForkRepoOptions) *repo_model.Repository {
  177. repo, err := repo_service.ForkRepository(ctx, ctx.Doer, owner, forkOpts)
  178. if err != nil {
  179. ctx.Data["Err_RepoName"] = true
  180. switch {
  181. case repo_model.IsErrReachLimitOfRepo(err):
  182. maxCreationLimit := owner.MaxCreationLimit()
  183. msg := ctx.TrN(maxCreationLimit, "repo.form.reach_limit_of_creation_1", "repo.form.reach_limit_of_creation_n", maxCreationLimit)
  184. ctx.JSONError(msg)
  185. case repo_model.IsErrRepoAlreadyExist(err):
  186. ctx.JSONError(ctx.Tr("repo.settings.new_owner_has_same_repo"))
  187. case repo_model.IsErrRepoFilesAlreadyExist(err):
  188. switch {
  189. case ctx.IsUserSiteAdmin() || (setting.Repository.AllowAdoptionOfUnadoptedRepositories && setting.Repository.AllowDeleteOfUnadoptedRepositories):
  190. ctx.JSONError(ctx.Tr("form.repository_files_already_exist.adopt_or_delete"))
  191. case setting.Repository.AllowAdoptionOfUnadoptedRepositories:
  192. ctx.JSONError(ctx.Tr("form.repository_files_already_exist.adopt"))
  193. case setting.Repository.AllowDeleteOfUnadoptedRepositories:
  194. ctx.JSONError(ctx.Tr("form.repository_files_already_exist.delete"))
  195. default:
  196. ctx.JSONError(ctx.Tr("form.repository_files_already_exist"))
  197. }
  198. case db.IsErrNameReserved(err):
  199. ctx.JSONError(ctx.Tr("repo.form.name_reserved", err.(db.ErrNameReserved).Name))
  200. case db.IsErrNamePatternNotAllowed(err):
  201. ctx.JSONError(ctx.Tr("repo.form.name_pattern_not_allowed", err.(db.ErrNamePatternNotAllowed).Pattern))
  202. case errors.Is(err, user_model.ErrBlockedUser):
  203. ctx.JSONError(ctx.Tr("repo.fork.blocked_user"))
  204. default:
  205. ctx.ServerError("ForkPost", err)
  206. }
  207. return nil
  208. }
  209. return repo
  210. }