Add support for forking single branch (#25821)
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>
This commit is contained in:
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>
|
||||||
|
|
Loading…
Reference in a new issue