初始提交: Gitea 项目代码
This commit is contained in:
@@ -0,0 +1,238 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package repo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
git_model "gitea.dev/models/git"
|
||||
"gitea.dev/models/organization"
|
||||
repo_model "gitea.dev/models/repo"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/optional"
|
||||
"gitea.dev/modules/repository"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/structs"
|
||||
"gitea.dev/modules/templates"
|
||||
"gitea.dev/modules/web"
|
||||
"gitea.dev/services/context"
|
||||
"gitea.dev/services/forms"
|
||||
repo_service "gitea.dev/services/repository"
|
||||
)
|
||||
|
||||
const (
|
||||
tplFork templates.TplName = "repo/pulls/fork"
|
||||
)
|
||||
|
||||
func getForkRepository(ctx *context.Context) *repo_model.Repository {
|
||||
forkRepo := ctx.Repo.Repository
|
||||
if ctx.Written() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if forkRepo.IsEmpty {
|
||||
log.Trace("Empty repository %-v", forkRepo)
|
||||
ctx.NotFound(nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := forkRepo.LoadOwner(ctx); err != nil {
|
||||
ctx.ServerError("LoadOwner", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
ctx.Data["repo_name"] = forkRepo.Name
|
||||
ctx.Data["description"] = forkRepo.Description
|
||||
ctx.Data["IsPrivate"] = forkRepo.IsPrivate || forkRepo.Owner.Visibility == structs.VisibleTypePrivate
|
||||
canForkToUser := repository.CanUserForkBetweenOwners(forkRepo.OwnerID, ctx.Doer.ID) && !repo_model.HasForkedRepo(ctx, ctx.Doer.ID, forkRepo.ID)
|
||||
|
||||
ctx.Data["ForkRepo"] = forkRepo
|
||||
|
||||
ownedOrgs, err := organization.GetOrgsCanCreateRepoByUserID(ctx, ctx.Doer.ID)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetOrgsCanCreateRepoByUserID", err)
|
||||
return nil
|
||||
}
|
||||
var orgs []*organization.Organization
|
||||
for _, org := range ownedOrgs {
|
||||
if forkRepo.OwnerID != org.ID && !repo_model.HasForkedRepo(ctx, org.ID, forkRepo.ID) {
|
||||
orgs = append(orgs, org)
|
||||
}
|
||||
}
|
||||
|
||||
traverseParentRepo := forkRepo
|
||||
for {
|
||||
if !repository.CanUserForkBetweenOwners(ctx.Doer.ID, traverseParentRepo.OwnerID) {
|
||||
canForkToUser = false
|
||||
} else {
|
||||
for i, org := range orgs {
|
||||
if org.ID == traverseParentRepo.OwnerID {
|
||||
orgs = append(orgs[:i], orgs[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !traverseParentRepo.IsFork {
|
||||
break
|
||||
}
|
||||
traverseParentRepo, err = repo_model.GetRepositoryByID(ctx, traverseParentRepo.ForkID)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetRepositoryByID", err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Data["CanForkToUser"] = canForkToUser
|
||||
ctx.Data["Orgs"] = orgs
|
||||
|
||||
// TODO: this message should only be shown for the "current doer" when it is selected, just like the "new repo" page.
|
||||
// msg := ctx.TrN(maxCreationLimit, "repo.form.reach_limit_of_creation_1", "repo.form.reach_limit_of_creation_n", ctx.Doer.MaxCreationLimit())
|
||||
|
||||
if canForkToUser {
|
||||
ctx.Data["ContextUser"] = ctx.Doer
|
||||
ctx.Data["CanForkRepoInNewOwner"] = true
|
||||
} else if len(orgs) > 0 {
|
||||
ctx.Data["ContextUser"] = orgs[0]
|
||||
ctx.Data["CanForkRepoInNewOwner"] = true
|
||||
} else {
|
||||
ctx.Data["CanForkRepoInNewOwner"] = false
|
||||
ctx.Flash.Error(ctx.Tr("repo.fork_no_valid_owners"), true)
|
||||
return nil
|
||||
}
|
||||
|
||||
branches, err := git_model.FindBranchNames(ctx, git_model.FindBranchOptions{
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
ListOptions: db.ListOptionsAll,
|
||||
IsDeletedBranch: optional.Some(false),
|
||||
// Add it as the first option
|
||||
ExcludeBranchNames: []string{ctx.Repo.Repository.DefaultBranch},
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("FindBranchNames", err)
|
||||
return nil
|
||||
}
|
||||
ctx.Data["Branches"] = append([]string{ctx.Repo.Repository.DefaultBranch}, branches...)
|
||||
|
||||
return forkRepo
|
||||
}
|
||||
|
||||
// Fork render repository fork page
|
||||
func Fork(ctx *context.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("new_fork")
|
||||
getForkRepository(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.HTML(http.StatusOK, tplFork)
|
||||
}
|
||||
|
||||
// ForkPost response for forking a repository
|
||||
func ForkPost(ctx *context.Context) {
|
||||
form := web.GetForm(ctx).(*forms.CreateRepoForm)
|
||||
ctx.Data["Title"] = ctx.Tr("new_fork")
|
||||
|
||||
ctxUser := checkContextUser(ctx, form.UID)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
forkRepo := getForkRepository(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["ContextUser"] = ctxUser
|
||||
|
||||
if ctx.HasError() {
|
||||
ctx.JSONError(ctx.GetErrMsg())
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
traverseParentRepo := forkRepo
|
||||
for {
|
||||
if !repository.CanUserForkBetweenOwners(ctxUser.ID, traverseParentRepo.OwnerID) {
|
||||
ctx.JSONError(ctx.Tr("repo.settings.new_owner_has_same_repo"))
|
||||
return
|
||||
}
|
||||
repo := repo_model.GetForkedRepo(ctx, ctxUser.ID, traverseParentRepo.ID)
|
||||
if repo != nil {
|
||||
ctx.JSONRedirect(ctxUser.HomeLink() + "/" + url.PathEscape(repo.Name))
|
||||
return
|
||||
}
|
||||
if !traverseParentRepo.IsFork {
|
||||
break
|
||||
}
|
||||
traverseParentRepo, err = repo_model.GetRepositoryByID(ctx, traverseParentRepo.ForkID)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetRepositoryByID", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Check if user is allowed to create repo's on the organization.
|
||||
if ctxUser.IsOrganization() {
|
||||
isAllowedToFork, err := organization.OrgFromUser(ctxUser).CanCreateOrgRepo(ctx, ctx.Doer.ID)
|
||||
if err != nil {
|
||||
ctx.ServerError("CanCreateOrgRepo", err)
|
||||
return
|
||||
} else if !isAllowedToFork {
|
||||
ctx.HTTPError(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
repo := ForkRepoTo(ctx, ctxUser, repo_service.ForkRepoOptions{
|
||||
BaseRepo: forkRepo,
|
||||
Name: form.RepoName,
|
||||
Description: form.Description,
|
||||
SingleBranch: form.ForkSingleBranch,
|
||||
})
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
ctx.JSONRedirect(ctxUser.HomeLink() + "/" + url.PathEscape(repo.Name))
|
||||
}
|
||||
|
||||
func ForkRepoTo(ctx *context.Context, owner *user_model.User, forkOpts repo_service.ForkRepoOptions) *repo_model.Repository {
|
||||
repo, err := repo_service.ForkRepository(ctx, ctx.Doer, owner, forkOpts)
|
||||
if err != nil {
|
||||
ctx.Data["Err_RepoName"] = true
|
||||
switch {
|
||||
case repo_model.IsErrReachLimitOfRepo(err):
|
||||
maxCreationLimit := owner.MaxCreationLimit()
|
||||
msg := ctx.TrN(maxCreationLimit, "repo.form.reach_limit_of_creation_1", "repo.form.reach_limit_of_creation_n", maxCreationLimit)
|
||||
ctx.JSONError(msg)
|
||||
case repo_model.IsErrRepoAlreadyExist(err):
|
||||
ctx.JSONError(ctx.Tr("repo.settings.new_owner_has_same_repo"))
|
||||
case repo_model.IsErrRepoFilesAlreadyExist(err):
|
||||
switch {
|
||||
case ctx.IsUserSiteAdmin() || (setting.Repository.AllowAdoptionOfUnadoptedRepositories && setting.Repository.AllowDeleteOfUnadoptedRepositories):
|
||||
ctx.JSONError(ctx.Tr("form.repository_files_already_exist.adopt_or_delete"))
|
||||
case setting.Repository.AllowAdoptionOfUnadoptedRepositories:
|
||||
ctx.JSONError(ctx.Tr("form.repository_files_already_exist.adopt"))
|
||||
case setting.Repository.AllowDeleteOfUnadoptedRepositories:
|
||||
ctx.JSONError(ctx.Tr("form.repository_files_already_exist.delete"))
|
||||
default:
|
||||
ctx.JSONError(ctx.Tr("form.repository_files_already_exist"))
|
||||
}
|
||||
case db.IsErrNameReserved(err):
|
||||
ctx.JSONError(ctx.Tr("repo.form.name_reserved", err.(db.ErrNameReserved).Name))
|
||||
case db.IsErrNamePatternNotAllowed(err):
|
||||
ctx.JSONError(ctx.Tr("repo.form.name_pattern_not_allowed", err.(db.ErrNamePatternNotAllowed).Pattern))
|
||||
case errors.Is(err, user_model.ErrBlockedUser):
|
||||
ctx.JSONError(ctx.Tr("repo.fork.blocked_user"))
|
||||
default:
|
||||
ctx.ServerError("ForkPost", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return repo
|
||||
}
|
||||
Reference in New Issue
Block a user