mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-25 03:19:50 +02: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:
		
							parent
							
								
									5bc030efa2
								
							
						
					
					
						commit
						a66c16dc1b
					
				| @ -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) | ||||||
|  | |||||||
							
								
								
									
										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 | 		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 | ||||||
| 		} | 		} | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user