Fixes #25117 Add UI for choosing branch to fork Change default branch on single-branch forks ![image](https://github.com/go-gitea/gitea/assets/19504461/28505f69-a9a2-43a8-8b19-a0cdac3ddc5a) --------- Co-authored-by: Denys Konovalov <kontakt@denyskon.de> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>tags/v1.22.0-rc0
@@ -943,6 +943,8 @@ fork_from = Fork From | |||
already_forked = You've already forked %s | |||
fork_to_different_account = Fork to a different account | |||
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. | |||
use_template = Use this template | |||
clone_in_vsc = Clone in VS Code |
@@ -180,6 +180,21 @@ func getForkRepository(ctx *context.Context) *repo_model.Repository { | |||
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 | |||
} | |||
@@ -261,9 +276,10 @@ func ForkPost(ctx *context.Context) { | |||
} | |||
repo, err := repo_service.ForkRepository(ctx, ctx.Doer, ctxUser, repo_service.ForkRepoOptions{ | |||
BaseRepo: forkRepo, | |||
Name: form.RepoName, | |||
Description: form.Description, | |||
BaseRepo: forkRepo, | |||
Name: form.RepoName, | |||
Description: form.Description, | |||
SingleBranch: form.ForkSingleBranch, | |||
}) | |||
if err != nil { | |||
ctx.Data["Err_RepoName"] = true |
@@ -51,6 +51,8 @@ type CreateRepoForm struct { | |||
Labels bool | |||
ProtectedBranch bool | |||
TrustModel string | |||
ForkSingleBranch string | |||
} | |||
// Validate validates the fields |
@@ -44,9 +44,10 @@ func (err ErrForkAlreadyExist) Unwrap() error { | |||
// ForkRepoOptions contains the fork repository options | |||
type ForkRepoOptions struct { | |||
BaseRepo *repo_model.Repository | |||
Name string | |||
Description string | |||
BaseRepo *repo_model.Repository | |||
Name string | |||
Description string | |||
SingleBranch string | |||
} | |||
// 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{ | |||
OwnerID: owner.ID, | |||
Owner: owner, | |||
@@ -77,7 +82,7 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork | |||
Name: opts.Name, | |||
LowerName: strings.ToLower(opts.Name), | |||
Description: opts.Description, | |||
DefaultBranch: opts.BaseRepo.DefaultBranch, | |||
DefaultBranch: defaultBranch, | |||
IsPrivate: opts.BaseRepo.IsPrivate || opts.BaseRepo.Owner.Visibility == structs.VisibleTypePrivate, | |||
IsEmpty: opts.BaseRepo.IsEmpty, | |||
IsFork: true, | |||
@@ -134,9 +139,12 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork | |||
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) | |||
if stdout, _, err := git.NewCommand(txCtx, | |||
"clone", "--bare").AddDynamicArguments(oldRepoPath, repoPath). | |||
if stdout, _, err := cloneCmd.AddDynamicArguments(oldRepoPath, repoPath). | |||
SetDescription(fmt.Sprintf("ForkRepository(git clone): %s to %s", opts.BaseRepo.FullName(), repo.FullName())). | |||
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) |
@@ -51,6 +51,26 @@ | |||
</div> | |||
<span class="help">{{ctx.Locale.Tr "repo.fork_visibility_helper"}}</span> | |||
</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}}"> | |||
<label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label> | |||
<textarea id="description" name="description">{{.description}}</textarea> |