1
0
mirror of https://github.com/go-gitea/gitea.git synced 2025-04-18 00:47:48 -04:00

Allow to fork repository into the same owner ()

This feature is experimental, not fully tested, and may be changed in
the future.

It is only designed for users who really need it: set
`[repository].ALLOW_FORK_INTO_SAME_OWNER=true` in your app.ini

Doc: https://gitea.com/gitea/docs/pulls/122


![image](https://github.com/user-attachments/assets/38d08c23-9cfc-49d8-9321-ff81edf65395)
This commit is contained in:
wxiaoguang 2024-12-14 09:39:05 +08:00 committed by GitHub
parent 5bc030efa2
commit a66c16dc1b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 44 additions and 5 deletions
custom/conf
modules
routers/web/repo

@ -1040,9 +1040,13 @@ LEVEL = Info
;; Don't allow download source archive files from UI ;; Don't allow download source archive files from UI
;DISABLE_DOWNLOAD_SOURCE_ARCHIVES = false ;DISABLE_DOWNLOAD_SOURCE_ARCHIVES = false
;; Allow fork repositories without maximum number limit ;; Allow to fork repositories without maximum number limit
;ALLOW_FORK_WITHOUT_MAXIMUM_LIMIT = true ;ALLOW_FORK_WITHOUT_MAXIMUM_LIMIT = true
;; Allow to fork repositories into the same owner (user or organization)
;; This feature is experimental, not fully tested, and may be changed in the future
;ALLOW_FORK_INTO_SAME_OWNER = false
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;[repository.editor] ;[repository.editor]

@ -9,14 +9,22 @@ import (
"code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/organization"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
) )
func CanUserForkBetweenOwners(id1, id2 int64) bool {
if id1 != id2 {
return true
}
return setting.Repository.AllowForkIntoSameOwner
}
// CanUserForkRepo returns true if specified user can fork repository. // CanUserForkRepo returns true if specified user can fork repository.
func CanUserForkRepo(ctx context.Context, user *user_model.User, repo *repo_model.Repository) (bool, error) { func CanUserForkRepo(ctx context.Context, user *user_model.User, repo *repo_model.Repository) (bool, error) {
if user == nil { if user == nil {
return false, nil return false, nil
} }
if repo.OwnerID != user.ID && !repo_model.HasForkedRepo(ctx, user.ID, repo.ID) { if CanUserForkBetweenOwners(repo.OwnerID, user.ID) && !repo_model.HasForkedRepo(ctx, user.ID, repo.ID) {
return true, nil return true, nil
} }
ownedOrgs, err := organization.GetOrgsCanCreateRepoByUserID(ctx, user.ID) ownedOrgs, err := organization.GetOrgsCanCreateRepoByUserID(ctx, user.ID)

@ -0,0 +1,25 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repository
import (
"testing"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
"github.com/stretchr/testify/assert"
)
func TestCanUserForkBetweenOwners(t *testing.T) {
defer test.MockVariableValue(&setting.Repository.AllowForkIntoSameOwner)
setting.Repository.AllowForkIntoSameOwner = true
assert.True(t, CanUserForkBetweenOwners(1, 1))
assert.True(t, CanUserForkBetweenOwners(1, 2))
setting.Repository.AllowForkIntoSameOwner = false
assert.False(t, CanUserForkBetweenOwners(1, 1))
assert.True(t, CanUserForkBetweenOwners(1, 2))
}

@ -53,6 +53,7 @@ var (
AllowDeleteOfUnadoptedRepositories bool AllowDeleteOfUnadoptedRepositories bool
DisableDownloadSourceArchives bool DisableDownloadSourceArchives bool
AllowForkWithoutMaximumLimit bool AllowForkWithoutMaximumLimit bool
AllowForkIntoSameOwner bool
// Repository editor settings // Repository editor settings
Editor struct { Editor struct {

@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/modules/web"
@ -48,7 +49,7 @@ func getForkRepository(ctx *context.Context) *repo_model.Repository {
ctx.Data["repo_name"] = forkRepo.Name ctx.Data["repo_name"] = forkRepo.Name
ctx.Data["description"] = forkRepo.Description ctx.Data["description"] = forkRepo.Description
ctx.Data["IsPrivate"] = forkRepo.IsPrivate || forkRepo.Owner.Visibility == structs.VisibleTypePrivate ctx.Data["IsPrivate"] = forkRepo.IsPrivate || forkRepo.Owner.Visibility == structs.VisibleTypePrivate
canForkToUser := forkRepo.OwnerID != ctx.Doer.ID && !repo_model.HasForkedRepo(ctx, ctx.Doer.ID, forkRepo.ID) canForkToUser := repository.CanUserForkBetweenOwners(forkRepo.OwnerID, ctx.Doer.ID) && !repo_model.HasForkedRepo(ctx, ctx.Doer.ID, forkRepo.ID)
ctx.Data["ForkRepo"] = forkRepo ctx.Data["ForkRepo"] = forkRepo
@ -66,7 +67,7 @@ func getForkRepository(ctx *context.Context) *repo_model.Repository {
traverseParentRepo := forkRepo traverseParentRepo := forkRepo
for { for {
if ctx.Doer.ID == traverseParentRepo.OwnerID { if !repository.CanUserForkBetweenOwners(ctx.Doer.ID, traverseParentRepo.OwnerID) {
canForkToUser = false canForkToUser = false
} else { } else {
for i, org := range orgs { for i, org := range orgs {
@ -162,7 +163,7 @@ func ForkPost(ctx *context.Context) {
var err error var err error
traverseParentRepo := forkRepo traverseParentRepo := forkRepo
for { for {
if ctxUser.ID == traverseParentRepo.OwnerID { if !repository.CanUserForkBetweenOwners(ctxUser.ID, traverseParentRepo.OwnerID) {
ctx.RenderWithErr(ctx.Tr("repo.settings.new_owner_has_same_repo"), tplFork, &form) ctx.RenderWithErr(ctx.Tr("repo.settings.new_owner_has_same_repo"), tplFork, &form)
return return
} }