mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08: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
					
				
			
			
				
	
			
			
			
						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
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -264,6 +279,7 @@ func ForkPost(ctx *context.Context) {
 | 
				
			|||||||
		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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -47,6 +47,7 @@ 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