mirror of
				https://github.com/go-gitea/gitea
				synced 2025-10-31 11:28:24 +00:00 
			
		
		
		
	Add support for forking single branch (#25821)
Fixes #25117 Add UI for choosing branch to fork Change default branch on single-branch forks  --------- Co-authored-by: Denys Konovalov <kontakt@denyskon.de> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
		
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							e8840e7e2b
						
					
				
				
					commit
					5e02e3b7ee
				
			| @@ -943,6 +943,8 @@ fork_from = Fork From | |||||||
| already_forked = You've already forked %s | already_forked = You've already forked %s | ||||||
| fork_to_different_account = Fork to a different account | fork_to_different_account = Fork to a different account | ||||||
| fork_visibility_helper = The visibility of a forked repository cannot be changed. | fork_visibility_helper = The visibility of a forked repository cannot be changed. | ||||||
|  | fork_branch = Branch to be cloned to the fork | ||||||
|  | all_branches = All branches | ||||||
| fork_no_valid_owners = This repository can not be forked because there are no valid owners. | fork_no_valid_owners = This repository can not be forked because there are no valid owners. | ||||||
| use_template = Use this template | use_template = Use this template | ||||||
| clone_in_vsc = Clone in VS Code | clone_in_vsc = Clone in VS Code | ||||||
|   | |||||||
| @@ -180,6 +180,21 @@ func getForkRepository(ctx *context.Context) *repo_model.Repository { | |||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	branches, err := git_model.FindBranchNames(ctx, git_model.FindBranchOptions{ | ||||||
|  | 		RepoID: ctx.Repo.Repository.ID, | ||||||
|  | 		ListOptions: db.ListOptions{ | ||||||
|  | 			ListAll: true, | ||||||
|  | 		}, | ||||||
|  | 		IsDeletedBranch: util.OptionalBoolFalse, | ||||||
|  | 		// 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 | 	return forkRepo | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -261,9 +276,10 @@ func ForkPost(ctx *context.Context) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	repo, err := repo_service.ForkRepository(ctx, ctx.Doer, ctxUser, repo_service.ForkRepoOptions{ | 	repo, err := repo_service.ForkRepository(ctx, ctx.Doer, ctxUser, repo_service.ForkRepoOptions{ | ||||||
| 		BaseRepo:    forkRepo, | 		BaseRepo:     forkRepo, | ||||||
| 		Name:        form.RepoName, | 		Name:         form.RepoName, | ||||||
| 		Description: form.Description, | 		Description:  form.Description, | ||||||
|  | 		SingleBranch: form.ForkSingleBranch, | ||||||
| 	}) | 	}) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		ctx.Data["Err_RepoName"] = true | 		ctx.Data["Err_RepoName"] = true | ||||||
|   | |||||||
| @@ -51,6 +51,8 @@ type CreateRepoForm struct { | |||||||
| 	Labels          bool | 	Labels          bool | ||||||
| 	ProtectedBranch bool | 	ProtectedBranch bool | ||||||
| 	TrustModel      string | 	TrustModel      string | ||||||
|  |  | ||||||
|  | 	ForkSingleBranch string | ||||||
| } | } | ||||||
|  |  | ||||||
| // Validate validates the fields | // Validate validates the fields | ||||||
|   | |||||||
| @@ -44,9 +44,10 @@ func (err ErrForkAlreadyExist) Unwrap() error { | |||||||
|  |  | ||||||
| // ForkRepoOptions contains the fork repository options | // ForkRepoOptions contains the fork repository options | ||||||
| type ForkRepoOptions struct { | type ForkRepoOptions struct { | ||||||
| 	BaseRepo    *repo_model.Repository | 	BaseRepo     *repo_model.Repository | ||||||
| 	Name        string | 	Name         string | ||||||
| 	Description string | 	Description  string | ||||||
|  | 	SingleBranch string | ||||||
| } | } | ||||||
|  |  | ||||||
| // ForkRepository forks a repository | // ForkRepository forks a repository | ||||||
| @@ -70,6 +71,10 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	defaultBranch := opts.BaseRepo.DefaultBranch | ||||||
|  | 	if opts.SingleBranch != "" { | ||||||
|  | 		defaultBranch = opts.SingleBranch | ||||||
|  | 	} | ||||||
| 	repo := &repo_model.Repository{ | 	repo := &repo_model.Repository{ | ||||||
| 		OwnerID:       owner.ID, | 		OwnerID:       owner.ID, | ||||||
| 		Owner:         owner, | 		Owner:         owner, | ||||||
| @@ -77,7 +82,7 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork | |||||||
| 		Name:          opts.Name, | 		Name:          opts.Name, | ||||||
| 		LowerName:     strings.ToLower(opts.Name), | 		LowerName:     strings.ToLower(opts.Name), | ||||||
| 		Description:   opts.Description, | 		Description:   opts.Description, | ||||||
| 		DefaultBranch: opts.BaseRepo.DefaultBranch, | 		DefaultBranch: defaultBranch, | ||||||
| 		IsPrivate:     opts.BaseRepo.IsPrivate || opts.BaseRepo.Owner.Visibility == structs.VisibleTypePrivate, | 		IsPrivate:     opts.BaseRepo.IsPrivate || opts.BaseRepo.Owner.Visibility == structs.VisibleTypePrivate, | ||||||
| 		IsEmpty:       opts.BaseRepo.IsEmpty, | 		IsEmpty:       opts.BaseRepo.IsEmpty, | ||||||
| 		IsFork:        true, | 		IsFork:        true, | ||||||
| @@ -134,9 +139,12 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork | |||||||
|  |  | ||||||
| 		needsRollback = true | 		needsRollback = true | ||||||
|  |  | ||||||
|  | 		cloneCmd := git.NewCommand(txCtx, "clone", "--bare") | ||||||
|  | 		if opts.SingleBranch != "" { | ||||||
|  | 			cloneCmd.AddArguments("--single-branch", "--branch").AddDynamicArguments(opts.SingleBranch) | ||||||
|  | 		} | ||||||
| 		repoPath := repo_model.RepoPath(owner.Name, repo.Name) | 		repoPath := repo_model.RepoPath(owner.Name, repo.Name) | ||||||
| 		if stdout, _, err := git.NewCommand(txCtx, | 		if stdout, _, err := cloneCmd.AddDynamicArguments(oldRepoPath, repoPath). | ||||||
| 			"clone", "--bare").AddDynamicArguments(oldRepoPath, repoPath). |  | ||||||
| 			SetDescription(fmt.Sprintf("ForkRepository(git clone): %s to %s", opts.BaseRepo.FullName(), repo.FullName())). | 			SetDescription(fmt.Sprintf("ForkRepository(git clone): %s to %s", opts.BaseRepo.FullName(), repo.FullName())). | ||||||
| 			RunStdBytes(&git.RunOpts{Timeout: 10 * time.Minute}); err != nil { | 			RunStdBytes(&git.RunOpts{Timeout: 10 * time.Minute}); err != nil { | ||||||
| 			log.Error("Fork Repository (git clone) Failed for %v (from %v):\nStdout: %s\nError: %v", repo, opts.BaseRepo, stdout, err) | 			log.Error("Fork Repository (git clone) Failed for %v (from %v):\nStdout: %s\nError: %v", repo, opts.BaseRepo, stdout, err) | ||||||
|   | |||||||
| @@ -51,6 +51,26 @@ | |||||||
| 						</div> | 						</div> | ||||||
| 						<span class="help">{{ctx.Locale.Tr "repo.fork_visibility_helper"}}</span> | 						<span class="help">{{ctx.Locale.Tr "repo.fork_visibility_helper"}}</span> | ||||||
| 					</div> | 					</div> | ||||||
|  | 					<div class="inline field"> | ||||||
|  | 						<label>{{ctx.Locale.Tr "repo.fork_branch"}}</label> | ||||||
|  | 						<div class="ui selection dropdown"> | ||||||
|  | 							<input type="hidden" id="fork_single_branch" name="fork_single_branch" value="" required> | ||||||
|  | 							<span class="text truncated-item-container" data-value="" title="{{ctx.Locale.Tr "repo.all_branches"}}"> | ||||||
|  | 								<span class="truncated-item-name">{{ctx.Locale.Tr "repo.all_branches"}}</span> | ||||||
|  | 							</span> | ||||||
|  | 							{{svg "octicon-triangle-down" 14 "dropdown icon"}} | ||||||
|  | 							<div class="menu"> | ||||||
|  | 								<div class="item truncated-item-container" data-value="" title="{{ctx.Locale.Tr "repo.all_branches"}}"> | ||||||
|  | 									<span class="truncated-item-name">{{ctx.Locale.Tr "repo.all_branches"}}</span> | ||||||
|  | 								</div> | ||||||
|  | 								{{range .Branches}} | ||||||
|  | 									<div class="item truncated-item-container" data-value="{{.}}" title="{{.}}"> | ||||||
|  | 										<span class="truncated-item-name">{{.}}</span> | ||||||
|  | 									</div> | ||||||
|  | 								{{end}} | ||||||
|  | 							</div> | ||||||
|  | 						</div> | ||||||
|  | 					</div> | ||||||
| 					<div class="inline field {{if .Err_Description}}error{{end}}"> | 					<div class="inline field {{if .Err_Description}}error{{end}}"> | ||||||
| 						<label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label> | 						<label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label> | ||||||
| 						<textarea id="description" name="description">{{.description}}</textarea> | 						<textarea id="description" name="description">{{.description}}</textarea> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user