mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09:00 
			
		
		
		
	Allow to fork repository into the same owner (#32819)
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 
This commit is contained in:
		| @@ -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] | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
							
								
								
									
										25
									
								
								modules/repository/fork_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								modules/repository/fork_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| 		DisableDownloadSourceArchives           bool | ||||
| 		AllowForkWithoutMaximumLimit            bool | ||||
| 		AllowForkIntoSameOwner                  bool | ||||
|  | ||||
| 		// Repository editor settings | ||||
| 		Editor struct { | ||||
|   | ||||
| @@ -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 | ||||
| 		} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user