aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--custom/conf/app.example.ini6
-rw-r--r--modules/repository/fork.go10
-rw-r--r--modules/repository/fork_test.go25
-rw-r--r--modules/setting/repository.go1
-rw-r--r--routers/web/repo/fork.go7
5 files changed, 44 insertions, 5 deletions
diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini
index 5c23f70d7c..6377ebf9d2 100644
--- a/custom/conf/app.example.ini
+++ b/custom/conf/app.example.ini
@@ -1040,9 +1040,13 @@ LEVEL = Info
;; Don't allow download source archive files from UI
;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 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]
diff --git a/modules/repository/fork.go b/modules/repository/fork.go
index fbf0008716..d530634071 100644
--- a/modules/repository/fork.go
+++ b/modules/repository/fork.go
@@ -9,14 +9,22 @@ import (
"code.gitea.io/gitea/models/organization"
repo_model "code.gitea.io/gitea/models/repo"
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.
func CanUserForkRepo(ctx context.Context, user *user_model.User, repo *repo_model.Repository) (bool, error) {
if user == 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
}
ownedOrgs, err := organization.GetOrgsCanCreateRepoByUserID(ctx, user.ID)
diff --git a/modules/repository/fork_test.go b/modules/repository/fork_test.go
new file mode 100644
index 0000000000..f8c76d942d
--- /dev/null
+++ b/modules/repository/fork_test.go
@@ -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))
+}
diff --git a/modules/setting/repository.go b/modules/setting/repository.go
index 14cf5805c0..c5619d0f04 100644
--- a/modules/setting/repository.go
+++ b/modules/setting/repository.go
@@ -53,6 +53,7 @@ var (
AllowDeleteOfUnadoptedRepositories bool
DisableDownloadSourceArchives bool
AllowForkWithoutMaximumLimit bool
+ AllowForkIntoSameOwner bool
// Repository editor settings
Editor struct {
diff --git a/routers/web/repo/fork.go b/routers/web/repo/fork.go
index 27e42a8f98..86af705617 100644
--- a/routers/web/repo/fork.go
+++ b/routers/web/repo/fork.go
@@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
+ "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"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["description"] = forkRepo.Description
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
@@ -66,7 +67,7 @@ func getForkRepository(ctx *context.Context) *repo_model.Repository {
traverseParentRepo := forkRepo
for {
- if ctx.Doer.ID == traverseParentRepo.OwnerID {
+ if !repository.CanUserForkBetweenOwners(ctx.Doer.ID, traverseParentRepo.OwnerID) {
canForkToUser = false
} else {
for i, org := range orgs {
@@ -162,7 +163,7 @@ func ForkPost(ctx *context.Context) {
var err error
traverseParentRepo := forkRepo
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)
return
}