mirror of https://github.com/go-gitea/gitea.git
sync
This commit is contained in:
parent
4fc626daa1
commit
21dd561dec
|
|
@ -653,7 +653,7 @@ func (repo *Repository) AllowsPulls(ctx context.Context) bool {
|
|||
|
||||
// CanEnableEditor returns true if repository meets the requirements of web editor.
|
||||
func (repo *Repository) CanEnableEditor() bool {
|
||||
return !repo.IsMirror
|
||||
return !repo.IsMirror && !repo.IsArchived
|
||||
}
|
||||
|
||||
// DescriptionHTML does special handles to description and return HTML string.
|
||||
|
|
|
|||
|
|
@ -1398,6 +1398,12 @@ editor.revert = Revert %s onto:
|
|||
editor.failed_to_commit = Failed to commit changes.
|
||||
editor.failed_to_commit_summary = Error Message:
|
||||
|
||||
editor.fork_create = Fork Repository to Propose Changes
|
||||
editor.fork_create_description = You can not edit this repository directly. Instead you can create a fork, make edits and create a pull request.
|
||||
editor.fork_edit_description = You can not edit this repository directly. The changes will be written to your fork <b>%s</b>, so you can create a pull request.
|
||||
editor.fork_not_editable = You have forked this repository but your fork is not editable.
|
||||
editor.fork_failed_to_push_branch = Failed to push branch %s to your repository.
|
||||
|
||||
commits.desc = Browse source code change history.
|
||||
commits.commits = Commits
|
||||
commits.no_commits = No commits in common. "%s" and "%s" have entirely different histories.
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/charset"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/httplib"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/templates"
|
||||
|
|
@ -39,7 +40,7 @@ const (
|
|||
editorCommitChoiceNewBranch string = "commit-to-new-branch"
|
||||
)
|
||||
|
||||
func prepareEditorCommitFormOptions(ctx *context.Context, editorAction string) {
|
||||
func prepareEditorCommitFormOptions(ctx *context.Context, editorAction string) *context.CommitFormOptions {
|
||||
cleanedTreePath := files_service.CleanGitTreePath(ctx.Repo.TreePath)
|
||||
if cleanedTreePath != ctx.Repo.TreePath {
|
||||
redirectTo := fmt.Sprintf("%s/%s/%s/%s", ctx.Repo.RepoLink, editorAction, util.PathEscapeSegments(ctx.Repo.BranchName), util.PathEscapeSegments(cleanedTreePath))
|
||||
|
|
@ -47,18 +48,28 @@ func prepareEditorCommitFormOptions(ctx *context.Context, editorAction string) {
|
|||
redirectTo += "?" + ctx.Req.URL.RawQuery
|
||||
}
|
||||
ctx.Redirect(redirectTo)
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
commitFormBehaviors, err := ctx.Repo.PrepareCommitFormBehaviors(ctx, ctx.Doer)
|
||||
commitFormOptions, err := context.PrepareCommitFormOptions(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.Permission, ctx.Repo.RefFullName)
|
||||
if err != nil {
|
||||
ctx.ServerError("PrepareCommitFormBehaviors", err)
|
||||
return
|
||||
ctx.ServerError("PrepareCommitFormOptions", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if commitFormOptions.NeedFork {
|
||||
ForkToEdit(ctx)
|
||||
return nil
|
||||
}
|
||||
|
||||
if commitFormOptions.WillSubmitToFork && !commitFormOptions.TargetRepo.CanEnableEditor() {
|
||||
ctx.Data["NotFoundPrompt"] = ctx.Locale.Tr("repo.editor.fork_not_editable")
|
||||
ctx.NotFound(nil)
|
||||
}
|
||||
|
||||
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
|
||||
ctx.Data["TreePath"] = ctx.Repo.TreePath
|
||||
ctx.Data["CommitFormBehaviors"] = commitFormBehaviors
|
||||
ctx.Data["CommitFormOptions"] = commitFormOptions
|
||||
|
||||
// for online editor
|
||||
ctx.Data["PreviewableExtensions"] = strings.Join(markup.PreviewableExtensions(), ",")
|
||||
|
|
@ -69,9 +80,10 @@ func prepareEditorCommitFormOptions(ctx *context.Context, editorAction string) {
|
|||
// form fields
|
||||
ctx.Data["commit_summary"] = ""
|
||||
ctx.Data["commit_message"] = ""
|
||||
ctx.Data["commit_choice"] = util.Iif(commitFormBehaviors.CanCommitToBranch, editorCommitChoiceDirect, editorCommitChoiceNewBranch)
|
||||
ctx.Data["new_branch_name"] = getUniquePatchBranchName(ctx, ctx.Doer.LowerName, ctx.Repo.Repository)
|
||||
ctx.Data["commit_choice"] = util.Iif(commitFormOptions.CanCommitToBranch, editorCommitChoiceDirect, editorCommitChoiceNewBranch)
|
||||
ctx.Data["new_branch_name"] = getUniquePatchBranchName(ctx, ctx.Doer.LowerName, commitFormOptions.TargetRepo)
|
||||
ctx.Data["last_commit"] = ctx.Repo.CommitID
|
||||
return commitFormOptions
|
||||
}
|
||||
|
||||
func prepareTreePathFieldsAndPaths(ctx *context.Context, treePath string) {
|
||||
|
|
@ -79,15 +91,15 @@ func prepareTreePathFieldsAndPaths(ctx *context.Context, treePath string) {
|
|||
ctx.Data["TreeNames"], ctx.Data["TreePaths"] = getParentTreeFields(treePath)
|
||||
}
|
||||
|
||||
type parsedEditorCommitForm[T any] struct {
|
||||
form T
|
||||
commonForm *forms.CommitCommonForm
|
||||
CommitFormBehaviors *context.CommitFormBehaviors
|
||||
TargetBranchName string
|
||||
GitCommitter *files_service.IdentityOptions
|
||||
type preparedEditorCommitForm[T any] struct {
|
||||
form T
|
||||
commonForm *forms.CommitCommonForm
|
||||
CommitFormOptions *context.CommitFormOptions
|
||||
TargetBranchName string
|
||||
GitCommitter *files_service.IdentityOptions
|
||||
}
|
||||
|
||||
func (f *parsedEditorCommitForm[T]) GetCommitMessage(defaultCommitMessage string) string {
|
||||
func (f *preparedEditorCommitForm[T]) GetCommitMessage(defaultCommitMessage string) string {
|
||||
commitMessage := util.IfZero(strings.TrimSpace(f.commonForm.CommitSummary), defaultCommitMessage)
|
||||
if body := strings.TrimSpace(f.commonForm.CommitMessage); body != "" {
|
||||
commitMessage += "\n\n" + body
|
||||
|
|
@ -95,7 +107,7 @@ func (f *parsedEditorCommitForm[T]) GetCommitMessage(defaultCommitMessage string
|
|||
return commitMessage
|
||||
}
|
||||
|
||||
func parseEditorCommitSubmittedForm[T forms.CommitCommonFormInterface](ctx *context.Context) *parsedEditorCommitForm[T] {
|
||||
func prepareEditorCommitSubmittedForm[T forms.CommitCommonFormInterface](ctx *context.Context) *preparedEditorCommitForm[T] {
|
||||
form := web.GetForm(ctx).(T)
|
||||
if ctx.HasError() {
|
||||
ctx.JSONError(ctx.GetErrMsg())
|
||||
|
|
@ -105,15 +117,20 @@ func parseEditorCommitSubmittedForm[T forms.CommitCommonFormInterface](ctx *cont
|
|||
commonForm := form.GetCommitCommonForm()
|
||||
commonForm.TreePath = files_service.CleanGitTreePath(commonForm.TreePath)
|
||||
|
||||
commitFormBehaviors, err := ctx.Repo.PrepareCommitFormBehaviors(ctx, ctx.Doer)
|
||||
commitFormOptions, err := context.PrepareCommitFormOptions(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.Permission, ctx.Repo.RefFullName)
|
||||
if err != nil {
|
||||
ctx.ServerError("PrepareCommitFormBehaviors", err)
|
||||
ctx.ServerError("PrepareCommitFormOptions", err)
|
||||
return nil
|
||||
}
|
||||
if commitFormOptions.NeedFork {
|
||||
// It shouldn't happen, because we should have done the checks in the "GET" request. But just in case.
|
||||
ctx.JSONError(ctx.Locale.TrString("error.not_found"))
|
||||
return nil
|
||||
}
|
||||
|
||||
// check commit behavior
|
||||
targetBranchName := util.Iif(commonForm.CommitChoice == editorCommitChoiceNewBranch, commonForm.NewBranchName, ctx.Repo.BranchName)
|
||||
if targetBranchName == ctx.Repo.BranchName && !commitFormBehaviors.CanCommitToBranch {
|
||||
if targetBranchName == ctx.Repo.BranchName && !commitFormOptions.CanCommitToBranch {
|
||||
ctx.JSONError(ctx.Tr("repo.editor.cannot_commit_to_protected_branch", targetBranchName))
|
||||
return nil
|
||||
}
|
||||
|
|
@ -125,28 +142,38 @@ func parseEditorCommitSubmittedForm[T forms.CommitCommonFormInterface](ctx *cont
|
|||
return nil
|
||||
}
|
||||
|
||||
return &parsedEditorCommitForm[T]{
|
||||
form: form,
|
||||
commonForm: commonForm,
|
||||
CommitFormBehaviors: commitFormBehaviors,
|
||||
TargetBranchName: targetBranchName,
|
||||
GitCommitter: gitCommitter,
|
||||
fromBaseBranch := ctx.FormString("from_base_branch")
|
||||
if fromBaseBranch != "" {
|
||||
err = editorPushBranchToForkedRepository(ctx, ctx.Doer, ctx.Repo.Repository.BaseRepo, fromBaseBranch, ctx.Repo.Repository, ctx.Repo.RefFullName.BranchName())
|
||||
if err != nil {
|
||||
log.Error("Unable to editorPushBranchToForkedRepository: %v", err)
|
||||
ctx.JSONError(ctx.Tr("repo.editor.fork_failed_to_push_branch", targetBranchName))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return &preparedEditorCommitForm[T]{
|
||||
form: form,
|
||||
commonForm: commonForm,
|
||||
CommitFormOptions: commitFormOptions,
|
||||
TargetBranchName: targetBranchName,
|
||||
GitCommitter: gitCommitter,
|
||||
}
|
||||
}
|
||||
|
||||
// redirectForCommitChoice redirects after committing the edit to a branch
|
||||
func redirectForCommitChoice[T any](ctx *context.Context, parsed *parsedEditorCommitForm[T], treePath string) {
|
||||
func redirectForCommitChoice[T any](ctx *context.Context, parsed *preparedEditorCommitForm[T], treePath string) {
|
||||
if parsed.commonForm.CommitChoice == editorCommitChoiceNewBranch {
|
||||
// Redirect to a pull request when possible
|
||||
redirectToPullRequest := false
|
||||
repo, baseBranch, headBranch := ctx.Repo.Repository, ctx.Repo.BranchName, parsed.TargetBranchName
|
||||
if repo.UnitEnabled(ctx, unit.TypePullRequests) {
|
||||
redirectToPullRequest = true
|
||||
} else if parsed.CommitFormBehaviors.CanCreateBasePullRequest {
|
||||
if ctx.Repo.Repository.IsFork && parsed.CommitFormOptions.CanCreateBasePullRequest {
|
||||
redirectToPullRequest = true
|
||||
baseBranch = repo.BaseRepo.DefaultBranch
|
||||
headBranch = repo.Owner.Name + "/" + repo.Name + ":" + headBranch
|
||||
repo = repo.BaseRepo
|
||||
} else if repo.UnitEnabled(ctx, unit.TypePullRequests) {
|
||||
redirectToPullRequest = true
|
||||
}
|
||||
if redirectToPullRequest {
|
||||
ctx.JSONRedirect(repo.Link() + "/compare/" + util.PathEscapeSegments(baseBranch) + "..." + util.PathEscapeSegments(headBranch))
|
||||
|
|
@ -268,7 +295,7 @@ func EditFile(ctx *context.Context) {
|
|||
func EditFilePost(ctx *context.Context) {
|
||||
editorAction := ctx.PathParam("editor_action")
|
||||
isNewFile := editorAction == "_new"
|
||||
parsed := parseEditorCommitSubmittedForm[*forms.EditRepoFileForm](ctx)
|
||||
parsed := prepareEditorCommitSubmittedForm[*forms.EditRepoFileForm](ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
|
@ -327,7 +354,7 @@ func DeleteFile(ctx *context.Context) {
|
|||
|
||||
// DeleteFilePost response for deleting file
|
||||
func DeleteFilePost(ctx *context.Context) {
|
||||
parsed := parseEditorCommitSubmittedForm[*forms.DeleteRepoFileForm](ctx)
|
||||
parsed := prepareEditorCommitSubmittedForm[*forms.DeleteRepoFileForm](ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
|
@ -360,18 +387,18 @@ func DeleteFilePost(ctx *context.Context) {
|
|||
|
||||
func UploadFile(ctx *context.Context) {
|
||||
ctx.Data["PageIsUpload"] = true
|
||||
upload.AddUploadContext(ctx, "repo")
|
||||
prepareTreePathFieldsAndPaths(ctx, ctx.Repo.TreePath)
|
||||
|
||||
prepareEditorCommitFormOptions(ctx, "_upload")
|
||||
opts := prepareEditorCommitFormOptions(ctx, "_upload")
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
upload.AddUploadContextForRepo(ctx, opts.TargetRepo)
|
||||
|
||||
ctx.HTML(http.StatusOK, tplUploadFile)
|
||||
}
|
||||
|
||||
func UploadFilePost(ctx *context.Context) {
|
||||
parsed := parseEditorCommitSubmittedForm[*forms.UploadRepoFileForm](ctx)
|
||||
parsed := prepareEditorCommitSubmittedForm[*forms.UploadRepoFileForm](ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ func NewDiffPatch(ctx *context.Context) {
|
|||
|
||||
// NewDiffPatchPost response for sending patch page
|
||||
func NewDiffPatchPost(ctx *context.Context) {
|
||||
parsed := parseEditorCommitSubmittedForm[*forms.EditRepoFileForm](ctx)
|
||||
parsed := prepareEditorCommitSubmittedForm[*forms.EditRepoFileForm](ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ func CherryPick(ctx *context.Context) {
|
|||
|
||||
func CherryPickPost(ctx *context.Context) {
|
||||
fromCommitID := ctx.PathParam("sha")
|
||||
parsed := parseEditorCommitSubmittedForm[*forms.CherryPickForm](ctx)
|
||||
parsed := prepareEditorCommitSubmittedForm[*forms.CherryPickForm](ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package repo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/modules/templates"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
repo_service "code.gitea.io/gitea/services/repository"
|
||||
)
|
||||
|
||||
const tplEditorFork templates.TplName = "repo/editor/fork"
|
||||
|
||||
func ForkToEdit(ctx *context.Context) {
|
||||
ctx.HTML(http.StatusOK, tplEditorFork)
|
||||
}
|
||||
|
||||
func ForkToEditPost(ctx *context.Context) {
|
||||
ForkRepoTo(ctx, ctx.Doer, repo_service.ForkRepoOptions{
|
||||
BaseRepo: ctx.Repo.Repository,
|
||||
Name: getUniqueRepositoryName(ctx, ctx.Doer.ID, ctx.Repo.Repository.Name),
|
||||
Description: ctx.Repo.Repository.Description,
|
||||
SingleBranch: ctx.Repo.Repository.DefaultBranch, // maybe we only need the default branch in the fork?
|
||||
})
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
ctx.JSONRedirect("") // reload the page, the new fork should be editable now
|
||||
}
|
||||
|
|
@ -11,9 +11,11 @@ import (
|
|||
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
repo_module "code.gitea.io/gitea/modules/repository"
|
||||
context_service "code.gitea.io/gitea/services/context"
|
||||
)
|
||||
|
||||
|
|
@ -83,3 +85,26 @@ func getParentTreeFields(treePath string) (treeNames, treePaths []string) {
|
|||
}
|
||||
return treeNames, treePaths
|
||||
}
|
||||
|
||||
// getUniqueRepositoryName Gets a unique repository name for a user
|
||||
// It will append a -<num> postfix if the name is already taken
|
||||
func getUniqueRepositoryName(ctx context.Context, ownerID int64, name string) string {
|
||||
uniqueName := name
|
||||
for i := 1; i < 1000; i++ {
|
||||
_, err := repo_model.GetRepositoryByName(ctx, ownerID, uniqueName)
|
||||
if err != nil || repo_model.IsErrRepoNotExist(err) {
|
||||
return uniqueName
|
||||
}
|
||||
uniqueName = fmt.Sprintf("%s-%d", name, i)
|
||||
i++
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func editorPushBranchToForkedRepository(ctx context.Context, doer *user_model.User, baseRepo *repo_model.Repository, baseBranchName string, targetRepo *repo_model.Repository, targetBranchName string) error {
|
||||
return git.Push(ctx, baseRepo.RepoPath(), git.PushOptions{
|
||||
Remote: targetRepo.RepoPath(),
|
||||
Branch: baseBranchName + ":" + targetBranchName,
|
||||
Env: repo_module.PushingEnvironment(doer, targetRepo),
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -189,17 +189,25 @@ func ForkPost(ctx *context.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
repo, err := repo_service.ForkRepository(ctx, ctx.Doer, ctxUser, repo_service.ForkRepoOptions{
|
||||
repo := ForkRepoTo(ctx, ctxUser, repo_service.ForkRepoOptions{
|
||||
BaseRepo: forkRepo,
|
||||
Name: form.RepoName,
|
||||
Description: form.Description,
|
||||
SingleBranch: form.ForkSingleBranch,
|
||||
})
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
ctx.JSONRedirect(ctxUser.HomeLink() + "/" + url.PathEscape(repo.Name))
|
||||
}
|
||||
|
||||
func ForkRepoTo(ctx *context.Context, owner *user_model.User, forkOpts repo_service.ForkRepoOptions) *repo_model.Repository {
|
||||
repo, err := repo_service.ForkRepository(ctx, ctx.Doer, owner, forkOpts)
|
||||
if err != nil {
|
||||
ctx.Data["Err_RepoName"] = true
|
||||
switch {
|
||||
case repo_model.IsErrReachLimitOfRepo(err):
|
||||
maxCreationLimit := ctxUser.MaxCreationLimit()
|
||||
maxCreationLimit := owner.MaxCreationLimit()
|
||||
msg := ctx.TrN(maxCreationLimit, "repo.form.reach_limit_of_creation_1", "repo.form.reach_limit_of_creation_n", maxCreationLimit)
|
||||
ctx.JSONError(msg)
|
||||
case repo_model.IsErrRepoAlreadyExist(err):
|
||||
|
|
@ -224,9 +232,7 @@ func ForkPost(ctx *context.Context) {
|
|||
default:
|
||||
ctx.ServerError("ForkPost", err)
|
||||
}
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Trace("Repository forked[%d]: %s/%s", forkRepo.ID, ctxUser.Name, repo.Name)
|
||||
ctx.JSONRedirect(ctxUser.HomeLink() + "/" + url.PathEscape(repo.Name))
|
||||
return repo
|
||||
}
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@ func prepareToRenderReadmeFile(ctx *context.Context, subfolder string, readmeFil
|
|||
ctx.Data["EscapeStatus"], ctx.Data["FileContent"] = charset.EscapeControlHTML(template.HTML(contentEscaped), ctx.Locale)
|
||||
}
|
||||
|
||||
if !fInfo.isLFSFile && ctx.Repo.CanEnableEditor(ctx, ctx.Doer) {
|
||||
if !fInfo.isLFSFile && ctx.Repo.Repository.CanEnableEditor() {
|
||||
ctx.Data["CanEditReadmeFile"] = true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1312,23 +1312,35 @@ func registerWebRoutes(m *web.Router) {
|
|||
}, reqSignIn, context.RepoAssignment, context.RepoMustNotBeArchived())
|
||||
// end "/{username}/{reponame}": create or edit issues, pulls, labels, milestones
|
||||
|
||||
m.Group("/{username}/{reponame}", func() { // repo code
|
||||
m.Group("/{username}/{reponame}", func() { // repo code (at least "code reader")
|
||||
m.Group("", func() {
|
||||
m.Group("", func() {
|
||||
m.Post("/_preview/*", repo.DiffPreviewPost)
|
||||
m.Combo("/{editor_action:_edit}/*").Get(repo.EditFile).
|
||||
Post(web.Bind(forms.EditRepoFileForm{}), repo.EditFilePost)
|
||||
m.Combo("/{editor_action:_new}/*").Get(repo.EditFile).
|
||||
Post(web.Bind(forms.EditRepoFileForm{}), repo.EditFilePost)
|
||||
m.Combo("/_delete/*").Get(repo.DeleteFile).
|
||||
Post(web.Bind(forms.DeleteRepoFileForm{}), repo.DeleteFilePost)
|
||||
m.Combo("/_upload/*", repo.MustBeAbleToUpload).Get(repo.UploadFile).
|
||||
Post(web.Bind(forms.UploadRepoFileForm{}), repo.UploadFilePost)
|
||||
m.Combo("/_diffpatch/*").Get(repo.NewDiffPatch).
|
||||
Post(web.Bind(forms.EditRepoFileForm{}), repo.NewDiffPatchPost)
|
||||
m.Combo("/_cherrypick/{sha:([a-f0-9]{7,64})}/*").Get(repo.CherryPick).
|
||||
Post(web.Bind(forms.CherryPickForm{}), repo.CherryPickPost)
|
||||
}, context.RepoRefByType(git.RefTypeBranch), context.CanWriteToBranch(), repo.WebGitOperationCommonData)
|
||||
// "GET" requests only need "code reader" permission, "POST" requests need "code writer" permission.
|
||||
// Because reader can "fork and edit"
|
||||
canWriteToBranch := context.CanWriteToBranch()
|
||||
m.Post("/_preview/*", repo.DiffPreviewPost) // read-only, fine with "code reader"
|
||||
m.Post("/_fork/*", repo.ForkToEditPost) // read-only, fork to own repo, fine with "code reader"
|
||||
|
||||
// the path params are used in PrepareCommitFormOptions to construct the correct form action URL
|
||||
m.Combo("/{editor_action:_edit}/*").
|
||||
Get(repo.EditFile).
|
||||
Post(web.Bind(forms.EditRepoFileForm{}), canWriteToBranch, repo.EditFilePost)
|
||||
m.Combo("/{editor_action:_new}/*").
|
||||
Get(repo.EditFile).
|
||||
Post(web.Bind(forms.EditRepoFileForm{}), canWriteToBranch, repo.EditFilePost)
|
||||
m.Combo("/{editor_action:_delete}/*").
|
||||
Get(repo.DeleteFile).
|
||||
Post(web.Bind(forms.DeleteRepoFileForm{}), canWriteToBranch, repo.DeleteFilePost)
|
||||
m.Combo("/{editor_action:_upload}/*", repo.MustBeAbleToUpload).
|
||||
Get(repo.UploadFile).
|
||||
Post(web.Bind(forms.UploadRepoFileForm{}), canWriteToBranch, repo.UploadFilePost)
|
||||
m.Combo("/{editor_action:_diffpatch}/*").
|
||||
Get(repo.NewDiffPatch).
|
||||
Post(web.Bind(forms.EditRepoFileForm{}), canWriteToBranch, repo.NewDiffPatchPost)
|
||||
m.Combo("/{editor_action:_cherrypick}/{sha:([a-f0-9]{7,64})}/*").
|
||||
Get(repo.CherryPick).
|
||||
Post(web.Bind(forms.CherryPickForm{}), canWriteToBranch, repo.CherryPickPost)
|
||||
}, context.RepoRefByType(git.RefTypeBranch), repo.WebGitOperationCommonData)
|
||||
m.Group("", func() {
|
||||
m.Post("/upload-file", repo.UploadFileToServer)
|
||||
m.Post("/upload-remove", repo.RemoveUploadFileFromServer)
|
||||
|
|
|
|||
|
|
@ -71,11 +71,6 @@ func (r *Repository) CanWriteToBranch(ctx context.Context, user *user_model.User
|
|||
return issues_model.CanMaintainerWriteToBranch(ctx, r.Permission, branch, user)
|
||||
}
|
||||
|
||||
// CanEnableEditor returns true if repository is editable and user has proper access level.
|
||||
func (r *Repository) CanEnableEditor(ctx context.Context, user *user_model.User) bool {
|
||||
return r.RefFullName.IsBranch() && r.CanWriteToBranch(ctx, user, r.BranchName) && r.Repository.CanEnableEditor() && !r.Repository.IsArchived
|
||||
}
|
||||
|
||||
// CanCreateBranch returns true if repository is editable and user has proper access level.
|
||||
func (r *Repository) CanCreateBranch() bool {
|
||||
return r.Permission.CanWrite(unit_model.TypeCode) && r.Repository.CanCreateBranch()
|
||||
|
|
@ -94,9 +89,13 @@ func RepoMustNotBeArchived() func(ctx *Context) {
|
|||
}
|
||||
}
|
||||
|
||||
type CommitFormBehaviors struct {
|
||||
type CommitFormOptions struct {
|
||||
NeedFork bool
|
||||
|
||||
TargetRepo *repo_model.Repository
|
||||
TargetFormAction string
|
||||
WillSubmitToFork bool
|
||||
CanCommitToBranch bool
|
||||
EditorEnabled bool
|
||||
UserCanPush bool
|
||||
RequireSigned bool
|
||||
WillSign bool
|
||||
|
|
@ -106,51 +105,79 @@ type CommitFormBehaviors struct {
|
|||
CanCreateBasePullRequest bool
|
||||
}
|
||||
|
||||
func (r *Repository) PrepareCommitFormBehaviors(ctx *Context, doer *user_model.User) (*CommitFormBehaviors, error) {
|
||||
protectedBranch, err := git_model.GetFirstMatchProtectedBranchRule(ctx, r.Repository.ID, r.BranchName)
|
||||
func PrepareCommitFormOptions(ctx *Context, doer *user_model.User, targetRepo *repo_model.Repository, doerRepoPerm access_model.Permission, refName git.RefName) (*CommitFormOptions, error) {
|
||||
if !refName.IsBranch() {
|
||||
// it shouldn't happen because middleware already checks
|
||||
return nil, util.NewInvalidArgumentErrorf("ref %q is not a branch", refName)
|
||||
}
|
||||
|
||||
originRepo := targetRepo
|
||||
branchName := refName.ShortName()
|
||||
// TODO: CanMaintainerWriteToBranch is a bad name, but it really does what "CanWriteToBranch" does
|
||||
if !issues_model.CanMaintainerWriteToBranch(ctx, doerRepoPerm, branchName, doer) {
|
||||
targetRepo = repo_model.GetForkedRepo(ctx, doer.ID, targetRepo.ID)
|
||||
if targetRepo == nil {
|
||||
return &CommitFormOptions{NeedFork: true}, nil
|
||||
}
|
||||
// now, we get our own forked repo; it must be writable by us.
|
||||
}
|
||||
submitToOriginRepo := targetRepo.ID == originRepo.ID
|
||||
err := targetRepo.GetBaseRepo(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userCanPush := true
|
||||
requireSigned := false
|
||||
if protectedBranch != nil {
|
||||
protectedBranch.Repo = r.Repository
|
||||
userCanPush = protectedBranch.CanUserPush(ctx, doer)
|
||||
requireSigned = protectedBranch.RequireSignedCommits
|
||||
}
|
||||
|
||||
sign, keyID, _, err := asymkey_service.SignCRUDAction(ctx, r.Repository.RepoPath(), doer, r.Repository.RepoPath(), git.BranchPrefix+r.BranchName)
|
||||
|
||||
canEnableEditor := r.CanEnableEditor(ctx, doer)
|
||||
canCommit := canEnableEditor && userCanPush
|
||||
if requireSigned {
|
||||
canCommit = canCommit && sign
|
||||
}
|
||||
wontSignReason := ""
|
||||
protectedBranch, err := git_model.GetFirstMatchProtectedBranchRule(ctx, targetRepo.ID, branchName)
|
||||
if err != nil {
|
||||
if asymkey_service.IsErrWontSign(err) {
|
||||
wontSignReason = string(err.(*asymkey_service.ErrWontSign).Reason)
|
||||
err = nil
|
||||
} else {
|
||||
wontSignReason = "error"
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
canPushWithProtection := true
|
||||
protectionRequireSigned := false
|
||||
if protectedBranch != nil {
|
||||
protectedBranch.Repo = targetRepo
|
||||
canPushWithProtection = protectedBranch.CanUserPush(ctx, doer)
|
||||
protectionRequireSigned = protectedBranch.RequireSignedCommits
|
||||
}
|
||||
|
||||
canCreateBasePullRequest := ctx.Repo.Repository.BaseRepo != nil && ctx.Repo.Repository.BaseRepo.UnitEnabled(ctx, unit_model.TypePullRequests)
|
||||
canCreatePullRequest := ctx.Repo.Repository.UnitEnabled(ctx, unit_model.TypePullRequests) || canCreateBasePullRequest
|
||||
willSign, signKeyID, _, err := asymkey_service.SignCRUDAction(ctx, targetRepo.RepoPath(), doer, targetRepo.RepoPath(), refName.String())
|
||||
wontSignReason := ""
|
||||
if asymkey_service.IsErrWontSign(err) {
|
||||
wontSignReason = string(err.(*asymkey_service.ErrWontSign).Reason)
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &CommitFormBehaviors{
|
||||
CanCommitToBranch: canCommit,
|
||||
EditorEnabled: canEnableEditor,
|
||||
UserCanPush: userCanPush,
|
||||
RequireSigned: requireSigned,
|
||||
WillSign: sign,
|
||||
SigningKey: keyID,
|
||||
canCommitToBranch := submitToOriginRepo && targetRepo.CanEnableEditor() && canPushWithProtection
|
||||
if protectionRequireSigned {
|
||||
canCommitToBranch = canCommitToBranch && willSign
|
||||
}
|
||||
|
||||
canCreateBasePullRequest := targetRepo.BaseRepo != nil && targetRepo.BaseRepo.UnitEnabled(ctx, unit_model.TypePullRequests)
|
||||
canCreatePullRequest := targetRepo.UnitEnabled(ctx, unit_model.TypePullRequests) || canCreateBasePullRequest
|
||||
|
||||
cfb := &CommitFormOptions{
|
||||
TargetRepo: targetRepo,
|
||||
WillSubmitToFork: !submitToOriginRepo,
|
||||
CanCommitToBranch: canCommitToBranch,
|
||||
UserCanPush: canPushWithProtection,
|
||||
RequireSigned: protectionRequireSigned,
|
||||
WillSign: willSign,
|
||||
SigningKey: signKeyID,
|
||||
WontSignReason: wontSignReason,
|
||||
|
||||
CanCreatePullRequest: canCreatePullRequest,
|
||||
CanCreateBasePullRequest: canCreateBasePullRequest,
|
||||
}, err
|
||||
}
|
||||
editorAction, editorPathParamRemaining := ctx.PathParam("editor_action"), ctx.PathParam("*")
|
||||
if editorAction == "_cherrypick" {
|
||||
cfb.TargetFormAction = targetRepo.Link() + "/" + editorAction + "/" + ctx.PathParam("sha") + "/" + editorPathParamRemaining
|
||||
} else {
|
||||
cfb.TargetFormAction = targetRepo.Link() + "/" + editorAction + "/" + editorPathParamRemaining
|
||||
}
|
||||
if originRepo.ID != targetRepo.ID {
|
||||
cfb.TargetFormAction += "?from_base_branch=" + url.QueryEscape(branchName)
|
||||
}
|
||||
return cfb, nil
|
||||
}
|
||||
|
||||
// CanUseTimetracker returns whether a user can use the timetracker.
|
||||
|
|
|
|||
|
|
@ -11,7 +11,9 @@ import (
|
|||
"regexp"
|
||||
"strings"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/reqctx"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
)
|
||||
|
|
@ -106,14 +108,17 @@ func AddUploadContext(ctx *context.Context, uploadType string) {
|
|||
ctx.Data["UploadAccepts"] = strings.ReplaceAll(setting.Attachment.AllowedTypes, "|", ",")
|
||||
ctx.Data["UploadMaxFiles"] = setting.Attachment.MaxFiles
|
||||
ctx.Data["UploadMaxSize"] = setting.Attachment.MaxSize
|
||||
case "repo":
|
||||
ctx.Data["UploadUrl"] = ctx.Repo.RepoLink + "/upload-file"
|
||||
ctx.Data["UploadRemoveUrl"] = ctx.Repo.RepoLink + "/upload-remove"
|
||||
ctx.Data["UploadLinkUrl"] = ctx.Repo.RepoLink + "/upload-file"
|
||||
ctx.Data["UploadAccepts"] = strings.ReplaceAll(setting.Repository.Upload.AllowedTypes, "|", ",")
|
||||
ctx.Data["UploadMaxFiles"] = setting.Repository.Upload.MaxFiles
|
||||
ctx.Data["UploadMaxSize"] = setting.Repository.Upload.FileMaxSize
|
||||
default:
|
||||
setting.PanicInDevOrTesting("Invalid upload type: %s", uploadType)
|
||||
}
|
||||
}
|
||||
|
||||
func AddUploadContextForRepo(ctx reqctx.RequestContext, repo *repo_model.Repository) {
|
||||
ctxData, repoLink := ctx.GetData(), repo.Link()
|
||||
ctxData["UploadUrl"] = repoLink + "/upload-file"
|
||||
ctxData["UploadRemoveUrl"] = repoLink + "/upload-remove"
|
||||
ctxData["UploadLinkUrl"] = repoLink + "/upload-file"
|
||||
ctxData["UploadAccepts"] = strings.ReplaceAll(setting.Repository.Upload.AllowedTypes, "|", ",")
|
||||
ctxData["UploadMaxFiles"] = setting.Repository.Upload.MaxFiles
|
||||
ctxData["UploadMaxSize"] = setting.Repository.Upload.FileMaxSize
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
{{template "repo/header" .}}
|
||||
<div class="ui container">
|
||||
{{template "base/alert" .}}
|
||||
<form class="ui edit form form-fetch-action" method="post" action="{{.RepoLink}}/_cherrypick/{{.FromCommitID}}/{{.BranchName | PathEscapeSegments}}">
|
||||
<form class="ui edit form form-fetch-action" method="post" action="{{.CommitFormOptions.TargetFormAction}}">
|
||||
{{.CsrfTokenHtml}}
|
||||
<input type="hidden" name="revert" value="{{if eq .CherryPickType "revert"}}true{{else}}false{{end}}">
|
||||
<div class="repo-editor-header">
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
<div class="commit-form-wrapper">
|
||||
{{ctx.AvatarUtils.Avatar .SignedUser 40 "commit-avatar"}}
|
||||
<div class="commit-form">
|
||||
<h3>{{- if .CommitFormBehaviors.WillSign}}
|
||||
<span title="{{ctx.Locale.Tr "repo.signing.will_sign" .CommitFormBehaviors.SigningKey}}">{{svg "octicon-lock" 24}}</span>
|
||||
<h3>{{- if .CommitFormOptions.WillSign}}
|
||||
<span title="{{ctx.Locale.Tr "repo.signing.will_sign" .CommitFormOptions.SigningKey}}">{{svg "octicon-lock" 24}}</span>
|
||||
{{ctx.Locale.Tr "repo.editor.commit_signed_changes"}}
|
||||
{{- else}}
|
||||
<span title="{{ctx.Locale.Tr (printf "repo.signing.wont_sign.%s" .CommitFormBehaviors.WontSignReason)}}">{{svg "octicon-unlock" 24}}</span>
|
||||
<span title="{{ctx.Locale.Tr (printf "repo.signing.wont_sign.%s" .CommitFormOptions.WontSignReason)}}">{{svg "octicon-unlock" 24}}</span>
|
||||
{{ctx.Locale.Tr "repo.editor.commit_changes"}}
|
||||
{{- end}}</h3>
|
||||
<div class="field">
|
||||
|
|
@ -22,17 +22,17 @@
|
|||
</div>
|
||||
<div class="quick-pull-choice js-quick-pull-choice">
|
||||
<div class="field">
|
||||
<div class="ui radio checkbox {{if not .CommitFormBehaviors.CanCommitToBranch}}disabled{{end}}">
|
||||
<div class="ui radio checkbox {{if not .CommitFormOptions.CanCommitToBranch}}disabled{{end}}">
|
||||
<input type="radio" class="js-quick-pull-choice-option" name="commit_choice" value="direct" data-button-text="{{ctx.Locale.Tr "repo.editor.commit_changes"}}" {{if eq .commit_choice "direct"}}checked{{end}}>
|
||||
<label>
|
||||
{{svg "octicon-git-commit"}}
|
||||
{{ctx.Locale.Tr "repo.editor.commit_directly_to_this_branch" .BranchName}}
|
||||
{{if not .CommitFormBehaviors.CanCommitToBranch}}
|
||||
{{if not .CommitFormOptions.CanCommitToBranch}}
|
||||
<div class="ui visible small warning message">
|
||||
{{ctx.Locale.Tr "repo.editor.no_commit_to_branch"}}
|
||||
<ul>
|
||||
{{if not .CommitFormBehaviors.UserCanPush}}<li>{{ctx.Locale.Tr "repo.editor.user_no_push_to_branch"}}</li>{{end}}
|
||||
{{if and .CommitFormBehaviors.RequireSigned (not .CommitFormBehaviors.WillSign)}}<li>{{ctx.Locale.Tr "repo.editor.require_signed_commit"}}</li>{{end}}
|
||||
{{if not .CommitFormOptions.UserCanPush}}<li>{{ctx.Locale.Tr "repo.editor.user_no_push_to_branch"}}</li>{{end}}
|
||||
{{if and .CommitFormOptions.RequireSigned (not .CommitFormOptions.WillSign)}}<li>{{ctx.Locale.Tr "repo.editor.require_signed_commit"}}</li>{{end}}
|
||||
</ul>
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
@ -42,14 +42,14 @@
|
|||
{{if and (not .Repository.IsEmpty) (not .IsEditingFileOnly)}}
|
||||
<div class="field">
|
||||
<div class="ui radio checkbox">
|
||||
{{if .CommitFormBehaviors.CanCreatePullRequest}}
|
||||
{{if .CommitFormOptions.CanCreatePullRequest}}
|
||||
<input type="radio" class="js-quick-pull-choice-option" name="commit_choice" value="commit-to-new-branch" data-button-text="{{ctx.Locale.Tr "repo.editor.propose_file_change"}}" {{if eq .commit_choice "commit-to-new-branch"}}checked{{end}}>
|
||||
{{else}}
|
||||
<input type="radio" class="js-quick-pull-choice-option" name="commit_choice" value="commit-to-new-branch" data-button-text="{{ctx.Locale.Tr "repo.editor.commit_changes"}}" {{if eq .commit_choice "commit-to-new-branch"}}checked{{end}}>
|
||||
{{end}}
|
||||
<label>
|
||||
{{svg "octicon-git-pull-request"}}
|
||||
{{if .CommitFormBehaviors.CanCreatePullRequest}}
|
||||
{{if .CommitFormOptions.CanCreatePullRequest}}
|
||||
{{ctx.Locale.Tr "repo.editor.create_new_branch"}}
|
||||
{{else}}
|
||||
{{ctx.Locale.Tr "repo.editor.create_new_branch_np"}}
|
||||
|
|
@ -78,6 +78,12 @@
|
|||
{{end}}
|
||||
</div>
|
||||
<input type="hidden" name="last_commit" value="{{.last_commit}}">
|
||||
|
||||
{{if .CommitFormOptions.WillSubmitToFork}}
|
||||
<div class="ui blue message">
|
||||
<p>{{ctx.Locale.Tr "repo.editor.fork_edit_description" .CommitFormOptions.TargetRepo.FullName}}</p>
|
||||
</div>
|
||||
{{end}}
|
||||
<button id="commit-button" type="submit" class="ui primary button">
|
||||
{{if eq .commit_choice "commit-to-new-branch"}}{{ctx.Locale.Tr "repo.editor.propose_file_change"}}{{else}}{{ctx.Locale.Tr "repo.editor.commit_changes"}}{{end}}
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
{{template "repo/header" .}}
|
||||
<div class="ui container">
|
||||
{{template "base/alert" .}}
|
||||
<form class="ui form form-fetch-action" method="post">
|
||||
<form class="ui form form-fetch-action" method="post" action="{{.CommitFormOptions.TargetFormAction}}">
|
||||
{{.CsrfTokenHtml}}
|
||||
{{template "repo/editor/commit_form" .}}
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
{{template "repo/header" .}}
|
||||
<div class="ui container">
|
||||
{{template "base/alert" .}}
|
||||
<form class="ui edit form form-fetch-action" method="post"
|
||||
<form class="ui edit form form-fetch-action" method="post" action="{{.CommitFormOptions.TargetFormAction}}"
|
||||
data-text-empty-confirm-header="{{ctx.Locale.Tr "repo.editor.commit_empty_file_header"}}"
|
||||
data-text-empty-confirm-content="{{ctx.Locale.Tr "repo.editor.commit_empty_file_text"}}"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
{{template "base/head" .}}
|
||||
<div role="main" aria-label="{{.Title}}" class="page-content repository">
|
||||
{{template "repo/header" .}}
|
||||
<div class="ui container">
|
||||
{{template "base/alert" .}}
|
||||
<form class="ui form form-fetch-action" method="post" action="{{.RepoLink}}/_fork/{{.BranchName | PathEscapeSegments}}">
|
||||
{{.CsrfTokenHtml}}
|
||||
<div class="field center">
|
||||
<h3>{{ctx.Locale.Tr "repo.editor.fork_create"}}</h3>
|
||||
<p>{{ctx.Locale.Tr "repo.editor.fork_create_description"}}</p>
|
||||
<button class="ui primary button">
|
||||
{{ctx.Locale.Tr "repo.fork_repo"}}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{{template "base/footer" .}}
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
{{template "repo/header" .}}
|
||||
<div class="ui container">
|
||||
{{template "base/alert" .}}
|
||||
<form class="ui edit form form-fetch-action" method="post" action="{{.RepoLink}}/_diffpatch/{{.BranchName | PathEscapeSegments}}"
|
||||
<form class="ui edit form form-fetch-action" method="post" action="{{.CommitFormOptions.TargetFormAction}}"
|
||||
data-text-empty-confirm-header="{{ctx.Locale.Tr "repo.editor.commit_empty_file_header"}}"
|
||||
data-text-empty-confirm-content="{{ctx.Locale.Tr "repo.editor.commit_empty_file_text"}}"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
{{template "repo/header" .}}
|
||||
<div class="ui container">
|
||||
{{template "base/alert" .}}
|
||||
<form class="ui comment form form-fetch-action" method="post">
|
||||
<form class="ui comment form form-fetch-action" method="post" action="{{.CommitFormOptions.TargetFormAction}}">
|
||||
{{.CsrfTokenHtml}}
|
||||
<div class="repo-editor-header">
|
||||
{{template "repo/editor/common_breadcrumb" .}}
|
||||
|
|
|
|||
|
|
@ -340,3 +340,58 @@ index 0000000000..bbbbbbbbbb
|
|||
})
|
||||
})
|
||||
}
|
||||
|
||||
func forkToEdit(t *testing.T, session *TestSession, owner, repo, operation, branch, filePath string) {
|
||||
// Attempt to edit file
|
||||
req := NewRequest(t, "GET", path.Join(owner, repo, operation, branch, filePath))
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
// Fork
|
||||
req = NewRequestWithValues(t, "POST", path.Join(owner, repo, "_fork", branch), map[string]string{"_csrf": GetUserCSRFToken(t, session)})
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.Equal(t, `{"redirect":""}`, strings.TrimSpace(resp.Body.String()))
|
||||
}
|
||||
|
||||
func testForkToEditFile(t *testing.T, session *TestSession, user, owner, repo, branch, filePath string) {
|
||||
// Fork repository because we can't edit it
|
||||
forkToEdit(t, session, owner, repo, "_edit", branch, filePath)
|
||||
|
||||
// Archive the repository
|
||||
req := NewRequestWithValues(t, "POST", path.Join(user, repo, "settings"),
|
||||
map[string]string{
|
||||
"_csrf": GetUserCSRFToken(t, session),
|
||||
"repo_name": repo,
|
||||
"action": "archive",
|
||||
},
|
||||
)
|
||||
session.MakeRequest(t, req, http.StatusSeeOther)
|
||||
|
||||
// Check editing archived repository is disabled
|
||||
req = NewRequest(t, "GET", path.Join(owner, repo, "_edit", branch, filePath)).SetHeader("Accept", "text/html")
|
||||
resp := session.MakeRequest(t, req, http.StatusNotFound)
|
||||
assert.Contains(t, resp.Body.String(), "You have forked this repository but your fork is not editable.")
|
||||
|
||||
// Unfork the repository
|
||||
req = NewRequestWithValues(t, "POST", path.Join(user, repo, "settings"),
|
||||
map[string]string{
|
||||
"_csrf": GetUserCSRFToken(t, session),
|
||||
"repo_name": repo,
|
||||
"action": "convert_fork",
|
||||
},
|
||||
)
|
||||
session.MakeRequest(t, req, http.StatusSeeOther)
|
||||
|
||||
// Fork repository again
|
||||
forkToEdit(t, session, owner, repo, "_edit", branch, filePath)
|
||||
|
||||
// Check the existence of the forked repo with unique name
|
||||
req = NewRequestf(t, "GET", "/%s/%s-1", user, repo)
|
||||
session.MakeRequest(t, req, http.StatusOK)
|
||||
}
|
||||
|
||||
func TestForkToEditFile(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||
session := loginUser(t, "user4")
|
||||
testForkToEditFile(t, session, "user4", "user2", "repo1", "master", "README.md")
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue