mirror of
https://github.com/go-gitea/gitea.git
synced 2025-12-08 15:45:27 +01:00
Add UpdateBranch API (#35368).
This is CreateBranch for branches that already exist making it possible to reset branch to an arbitrary commit. Consistent with CreateFile/UpdateFile new branches are created by POST and existing branches updated by PUT.
This commit is contained in:
parent
124d0d4ec4
commit
b8597b76b2
5
assets/go-licenses.json
generated
5
assets/go-licenses.json
generated
@ -749,6 +749,11 @@
|
|||||||
"path": "github.com/jhillyerd/enmime/LICENSE",
|
"path": "github.com/jhillyerd/enmime/LICENSE",
|
||||||
"licenseText": "The MIT License (MIT)\n\nCopyright (c) 2012-2016 James Hillyerd, All Rights Reserved\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
|
"licenseText": "The MIT License (MIT)\n\nCopyright (c) 2012-2016 James Hillyerd, All Rights Reserved\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "github.com/jinzhu/copier",
|
||||||
|
"path": "github.com/jinzhu/copier/License",
|
||||||
|
"licenseText": "The MIT License (MIT)\n\nCopyright (c) 2015 Jinzhu\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "github.com/josharian/intern",
|
"name": "github.com/josharian/intern",
|
||||||
"path": "github.com/josharian/intern/license.md",
|
"path": "github.com/josharian/intern/license.md",
|
||||||
|
|||||||
1
go.mod
1
go.mod
@ -77,6 +77,7 @@ require (
|
|||||||
github.com/huandu/xstrings v1.5.0
|
github.com/huandu/xstrings v1.5.0
|
||||||
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056
|
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056
|
||||||
github.com/jhillyerd/enmime v1.3.0
|
github.com/jhillyerd/enmime v1.3.0
|
||||||
|
github.com/jinzhu/copier v0.4.0
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||||
github.com/klauspost/compress v1.18.0
|
github.com/klauspost/compress v1.18.0
|
||||||
github.com/klauspost/cpuid/v2 v2.3.0
|
github.com/klauspost/cpuid/v2 v2.3.0
|
||||||
|
|||||||
2
go.sum
2
go.sum
@ -510,6 +510,8 @@ github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZ
|
|||||||
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
|
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
|
||||||
github.com/jhillyerd/enmime v1.3.0 h1:LV5kzfLidiOr8qRGIpYYmUZCnhrPbcFAnAFUnWn99rw=
|
github.com/jhillyerd/enmime v1.3.0 h1:LV5kzfLidiOr8qRGIpYYmUZCnhrPbcFAnAFUnWn99rw=
|
||||||
github.com/jhillyerd/enmime v1.3.0/go.mod h1:6c6jg5HdRRV2FtvVL69LjiX1M8oE0xDX9VEhV3oy4gs=
|
github.com/jhillyerd/enmime v1.3.0/go.mod h1:6c6jg5HdRRV2FtvVL69LjiX1M8oE0xDX9VEhV3oy4gs=
|
||||||
|
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
|
||||||
|
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
|
||||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
|
|||||||
@ -292,6 +292,30 @@ type RenameBranchRepoOption struct {
|
|||||||
Name string `json:"name" binding:"Required;GitRefName;MaxSize(100)"`
|
Name string `json:"name" binding:"Required;GitRefName;MaxSize(100)"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateBranchRepoOption options when updating a branch reference in a repository
|
||||||
|
// swagger:model
|
||||||
|
type UpdateBranchRepoOption struct {
|
||||||
|
// Name of the branch to update
|
||||||
|
//
|
||||||
|
// required: true
|
||||||
|
// unique: true
|
||||||
|
BranchName string `json:"new_branch_name" binding:"Required;GitRefName;MaxSize(100)"`
|
||||||
|
|
||||||
|
// the commit ID (SHA) for the branch that already exists to update
|
||||||
|
SHA string `json:"sha" binding:"Required"`
|
||||||
|
|
||||||
|
// Deprecated: true
|
||||||
|
// Name of the old branch to reset to
|
||||||
|
//
|
||||||
|
// unique: true
|
||||||
|
OldBranchName string `json:"old_branch_name" binding:"GitRefName;MaxSize(100)"`
|
||||||
|
|
||||||
|
// Name of the old branch/tag/commit to reset to
|
||||||
|
//
|
||||||
|
// unique: true
|
||||||
|
OldRefName string `json:"old_ref_name" binding:"GitRefName;MaxSize(100)"`
|
||||||
|
}
|
||||||
|
|
||||||
// TransferRepoOption options when transfer a repository's ownership
|
// TransferRepoOption options when transfer a repository's ownership
|
||||||
// swagger:model
|
// swagger:model
|
||||||
type TransferRepoOption struct {
|
type TransferRepoOption struct {
|
||||||
|
|||||||
@ -1242,6 +1242,7 @@ func Routes() *web.Router {
|
|||||||
m.Get("/*", repo.GetBranch)
|
m.Get("/*", repo.GetBranch)
|
||||||
m.Delete("/*", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, repo.DeleteBranch)
|
m.Delete("/*", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, repo.DeleteBranch)
|
||||||
m.Post("", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, bind(api.CreateBranchRepoOption{}), repo.CreateBranch)
|
m.Post("", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, bind(api.CreateBranchRepoOption{}), repo.CreateBranch)
|
||||||
|
m.Put("", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, bind(api.UpdateBranchRepoOption{}), repo.UpdateBranch)
|
||||||
m.Patch("/*", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, bind(api.RenameBranchRepoOption{}), repo.RenameBranch)
|
m.Patch("/*", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, bind(api.RenameBranchRepoOption{}), repo.RenameBranch)
|
||||||
}, context.ReferencesGitRepo(), reqRepoReader(unit.TypeCode))
|
}, context.ReferencesGitRepo(), reqRepoReader(unit.TypeCode))
|
||||||
m.Group("/branch_protections", func() {
|
m.Group("/branch_protections", func() {
|
||||||
|
|||||||
@ -6,6 +6,7 @@ package repo
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
@ -26,6 +27,8 @@ import (
|
|||||||
pull_service "code.gitea.io/gitea/services/pull"
|
pull_service "code.gitea.io/gitea/services/pull"
|
||||||
release_service "code.gitea.io/gitea/services/release"
|
release_service "code.gitea.io/gitea/services/release"
|
||||||
repo_service "code.gitea.io/gitea/services/repository"
|
repo_service "code.gitea.io/gitea/services/repository"
|
||||||
|
|
||||||
|
"github.com/jinzhu/copier"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetBranch get a branch of a repository
|
// GetBranch get a branch of a repository
|
||||||
@ -203,6 +206,62 @@ func CreateBranch(ctx *context.APIContext) {
|
|||||||
// "423":
|
// "423":
|
||||||
// "$ref": "#/responses/repoArchivedError"
|
// "$ref": "#/responses/repoArchivedError"
|
||||||
|
|
||||||
|
optCreate := web.GetForm(ctx).(*api.CreateBranchRepoOption)
|
||||||
|
opt := api.UpdateBranchRepoOption{}
|
||||||
|
err := copier.Copy(&opt, optCreate)
|
||||||
|
if err != nil {
|
||||||
|
ctx.APIError(http.StatusInternalServerError, fmt.Sprintf("Error processing request %s.", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateUpdateRepoBranch(ctx, &opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateBranch update (reset) a branch in a user's repository
|
||||||
|
func UpdateBranch(ctx *context.APIContext) {
|
||||||
|
// swagger:operation PUT /repos/{owner}/{repo}/branches repository repoUpdateBranch
|
||||||
|
// ---
|
||||||
|
// summary: Update a branch
|
||||||
|
// consumes:
|
||||||
|
// - application/json
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: owner
|
||||||
|
// in: path
|
||||||
|
// description: owner of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: repo
|
||||||
|
// in: path
|
||||||
|
// description: name of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: body
|
||||||
|
// in: body
|
||||||
|
// schema:
|
||||||
|
// "$ref": "#/definitions/UpdateBranchRepoOption"
|
||||||
|
// responses:
|
||||||
|
// "201":
|
||||||
|
// "$ref": "#/responses/Branch"
|
||||||
|
// "403":
|
||||||
|
// description: The branch is archived or a mirror.
|
||||||
|
// "404":
|
||||||
|
// description: The branch does not exist.
|
||||||
|
// "409":
|
||||||
|
// description: The branch SHA does not match.
|
||||||
|
// "423":
|
||||||
|
// "$ref": "#/responses/repoArchivedError"
|
||||||
|
|
||||||
|
opt := web.GetForm(ctx).(*api.UpdateBranchRepoOption)
|
||||||
|
|
||||||
|
CreateUpdateRepoBranch(ctx, opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateUpdateRepoBranch(ctx *context.APIContext, opt *api.UpdateBranchRepoOption) {
|
||||||
|
var oldCommit *git.Commit
|
||||||
|
var err error
|
||||||
|
|
||||||
if ctx.Repo.Repository.IsEmpty {
|
if ctx.Repo.Repository.IsEmpty {
|
||||||
ctx.APIError(http.StatusNotFound, "Git Repository is empty.")
|
ctx.APIError(http.StatusNotFound, "Git Repository is empty.")
|
||||||
return
|
return
|
||||||
@ -213,11 +272,6 @@ func CreateBranch(ctx *context.APIContext) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
opt := web.GetForm(ctx).(*api.CreateBranchRepoOption)
|
|
||||||
|
|
||||||
var oldCommit *git.Commit
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if len(opt.OldRefName) > 0 {
|
if len(opt.OldRefName) > 0 {
|
||||||
oldCommit, err = ctx.Repo.GitRepo.GetCommit(opt.OldRefName)
|
oldCommit, err = ctx.Repo.GitRepo.GetCommit(opt.OldRefName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -243,14 +297,16 @@ func CreateBranch(ctx *context.APIContext) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = repo_service.CreateNewBranchFromCommit(ctx, ctx.Doer, ctx.Repo.Repository, oldCommit.ID.String(), opt.BranchName)
|
err = repo_service.CreateUpdateBranchFromCommit(ctx, ctx.Doer, ctx.Repo.Repository, oldCommit.ID.String(), opt.BranchName, opt.SHA)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if git_model.IsErrBranchNotExist(err) {
|
if git_model.IsErrBranchNotExist(err) {
|
||||||
ctx.APIError(http.StatusNotFound, "The old branch does not exist")
|
ctx.APIError(http.StatusNotFound, "The old branch does not exist")
|
||||||
} else if release_service.IsErrTagAlreadyExists(err) {
|
} else if release_service.IsErrTagAlreadyExists(err) {
|
||||||
ctx.APIError(http.StatusConflict, "The branch with the same tag already exists.")
|
ctx.APIError(http.StatusConflict, "The branch with the same tag already exists.")
|
||||||
} else if git_model.IsErrBranchAlreadyExists(err) || git.IsErrPushOutOfDate(err) {
|
} else if git_model.IsErrBranchAlreadyExists(err) {
|
||||||
ctx.APIError(http.StatusConflict, "The branch already exists.")
|
ctx.APIError(http.StatusConflict, "The branch already exists.")
|
||||||
|
} else if git.IsErrPushOutOfDate(err) || git.IsErrPushRejected(err) {
|
||||||
|
ctx.APIError(http.StatusConflict, "The branch SHA does not match.")
|
||||||
} else if git_model.IsErrBranchNameConflict(err) {
|
} else if git_model.IsErrBranchNameConflict(err) {
|
||||||
ctx.APIError(http.StatusConflict, "The branch with the same name already exists.")
|
ctx.APIError(http.StatusConflict, "The branch with the same name already exists.")
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -148,6 +148,9 @@ type swaggerParameterBodies struct {
|
|||||||
// in:body
|
// in:body
|
||||||
CreateBranchRepoOption api.CreateBranchRepoOption
|
CreateBranchRepoOption api.CreateBranchRepoOption
|
||||||
|
|
||||||
|
// in:body
|
||||||
|
UpdateBranchRepoOption api.UpdateBranchRepoOption
|
||||||
|
|
||||||
// in:body
|
// in:body
|
||||||
CreateBranchProtectionOption api.CreateBranchProtectionOption
|
CreateBranchProtectionOption api.CreateBranchProtectionOption
|
||||||
|
|
||||||
|
|||||||
@ -194,9 +194,9 @@ func CreateBranch(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
err = release_service.CreateNewTag(ctx, ctx.Doer, ctx.Repo.Repository, target, form.NewBranchName, "")
|
err = release_service.CreateNewTag(ctx, ctx.Doer, ctx.Repo.Repository, target, form.NewBranchName, "")
|
||||||
} else if ctx.Repo.RefFullName.IsBranch() {
|
} else if ctx.Repo.RefFullName.IsBranch() {
|
||||||
err = repo_service.CreateNewBranch(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.BranchName, form.NewBranchName)
|
err = repo_service.CreateUpdateBranch(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.BranchName, form.NewBranchName, "")
|
||||||
} else {
|
} else {
|
||||||
err = repo_service.CreateNewBranchFromCommit(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.CommitID, form.NewBranchName)
|
err = repo_service.CreateUpdateBranchFromCommit(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.CommitID, form.NewBranchName, "")
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if release_service.IsErrProtectedTagName(err) {
|
if release_service.IsErrProtectedTagName(err) {
|
||||||
|
|||||||
@ -37,14 +37,14 @@ import (
|
|||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreateNewBranch creates a new repository branch
|
// CreateUpdateBranch creates or updates a repository branch
|
||||||
func CreateNewBranch(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, oldBranchName, branchName string) (err error) {
|
func CreateUpdateBranch(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, oldBranchName, branchName, sha string) (err error) {
|
||||||
branch, err := git_model.GetBranch(ctx, repo.ID, oldBranchName)
|
branch, err := git_model.GetBranch(ctx, repo.ID, oldBranchName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return CreateNewBranchFromCommit(ctx, doer, repo, branch.CommitID, branchName)
|
return CreateUpdateBranchFromCommit(ctx, doer, repo, branch.CommitID, branchName, sha)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Branch contains the branch information
|
// Branch contains the branch information
|
||||||
@ -373,23 +373,36 @@ func SyncBranchesToDB(ctx context.Context, repoID, pusherID int64, branchNames,
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateNewBranchFromCommit creates a new repository branch
|
// CreateUpdateBranchFromCommit creates or updates a repository branch
|
||||||
func CreateNewBranchFromCommit(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, commitID, branchName string) (err error) {
|
func CreateUpdateBranchFromCommit(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, commitID, branchName, sha string) (err error) {
|
||||||
err = repo.MustNotBeArchived()
|
err = repo.MustNotBeArchived()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if branch name can be used
|
if sha != "" {
|
||||||
if err := checkBranchName(ctx, repo, branchName); err != nil {
|
_, err := git_model.GetBranch(ctx, repo.ID, branchName)
|
||||||
return err
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Check if branch name can be used
|
||||||
|
if err := checkBranchName(ctx, repo, branchName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := git.Push(ctx, repo.RepoPath(), git.PushOptions{
|
pushOpts := git.PushOptions{
|
||||||
Remote: repo.RepoPath(),
|
Remote: repo.RepoPath(),
|
||||||
Branch: fmt.Sprintf("%s:%s%s", commitID, git.BranchPrefix, branchName),
|
Branch: fmt.Sprintf("%s:%s%s", commitID, git.BranchPrefix, branchName),
|
||||||
Env: repo_module.PushingEnvironment(doer, repo),
|
Env: repo_module.PushingEnvironment(doer, repo),
|
||||||
}); err != nil {
|
}
|
||||||
|
|
||||||
|
if sha != "" {
|
||||||
|
pushOpts.ForceWithLease = fmt.Sprintf("%s:%s", git.BranchPrefix+branchName, sha)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := git.Push(ctx, repo.RepoPath(), pushOpts); err != nil {
|
||||||
if git.IsErrPushOutOfDate(err) || git.IsErrPushRejected(err) {
|
if git.IsErrPushOutOfDate(err) || git.IsErrPushRejected(err) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
86
templates/swagger/v1_json.tmpl
generated
86
templates/swagger/v1_json.tmpl
generated
@ -6654,6 +6654,59 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"put": {
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"repository"
|
||||||
|
],
|
||||||
|
"summary": "Update a branch",
|
||||||
|
"operationId": "repoUpdateBranch",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "owner of the repo",
|
||||||
|
"name": "owner",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "name of the repo",
|
||||||
|
"name": "repo",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "body",
|
||||||
|
"in": "body",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/UpdateBranchRepoOption"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"201": {
|
||||||
|
"$ref": "#/responses/Branch"
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"description": "The branch is archived or a mirror."
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "The branch does not exist."
|
||||||
|
},
|
||||||
|
"409": {
|
||||||
|
"description": "The branch SHA does not match."
|
||||||
|
},
|
||||||
|
"423": {
|
||||||
|
"$ref": "#/responses/repoArchivedError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"post": {
|
"post": {
|
||||||
"consumes": [
|
"consumes": [
|
||||||
"application/json"
|
"application/json"
|
||||||
@ -28702,6 +28755,39 @@
|
|||||||
},
|
},
|
||||||
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||||
},
|
},
|
||||||
|
"UpdateBranchRepoOption": {
|
||||||
|
"description": "UpdateBranchRepoOption options when updating a branch reference in a repository",
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"new_branch_name"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"new_branch_name": {
|
||||||
|
"description": "Name of the branch to update",
|
||||||
|
"type": "string",
|
||||||
|
"uniqueItems": true,
|
||||||
|
"x-go-name": "BranchName"
|
||||||
|
},
|
||||||
|
"old_branch_name": {
|
||||||
|
"description": "Deprecated: true\nName of the old branch to reset to",
|
||||||
|
"type": "string",
|
||||||
|
"uniqueItems": true,
|
||||||
|
"x-go-name": "OldBranchName"
|
||||||
|
},
|
||||||
|
"old_ref_name": {
|
||||||
|
"description": "Name of the old branch/tag/commit to reset to",
|
||||||
|
"type": "string",
|
||||||
|
"uniqueItems": true,
|
||||||
|
"x-go-name": "OldRefName"
|
||||||
|
},
|
||||||
|
"sha": {
|
||||||
|
"description": "the commit ID (SHA) for the branch that already exists to update",
|
||||||
|
"type": "string",
|
||||||
|
"x-go-name": "SHA"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||||
|
},
|
||||||
"UpdateFileOptions": {
|
"UpdateFileOptions": {
|
||||||
"description": "UpdateFileOptions options for updating or creating a file\nNote: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)",
|
"description": "UpdateFileOptions options for updating or creating a file\nNote: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|||||||
@ -412,7 +412,7 @@ jobs:
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// create a branch
|
// create a branch
|
||||||
err = repo_service.CreateNewBranchFromCommit(t.Context(), user2, repo, branch.CommitID, "test-create-branch")
|
err = repo_service.CreateUpdateBranchFromCommit(t.Context(), user2, repo, branch.CommitID, "test-create-branch", "")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{
|
run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{
|
||||||
Title: "add workflow",
|
Title: "add workflow",
|
||||||
@ -530,7 +530,7 @@ jobs:
|
|||||||
|
|
||||||
// create a new branch
|
// create a new branch
|
||||||
testBranch := "test-branch"
|
testBranch := "test-branch"
|
||||||
err = repo_service.CreateNewBranch(t.Context(), user2, repo, "main", testBranch)
|
err = repo_service.CreateUpdateBranch(t.Context(), user2, repo, "main", testBranch, "")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// create Pull
|
// create Pull
|
||||||
@ -1509,7 +1509,7 @@ jobs:
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// create a branch
|
// create a branch
|
||||||
err = repo_service.CreateNewBranchFromCommit(t.Context(), user2, repo, branch.CommitID, "test-action-run-name-with-variables")
|
err = repo_service.CreateUpdateBranchFromCommit(t.Context(), user2, repo, branch.CommitID, "test-action-run-name-with-variables", "")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{
|
run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{
|
||||||
Title: user2.LoginName + " is running this workflow",
|
Title: user2.LoginName + " is running this workflow",
|
||||||
@ -1583,7 +1583,7 @@ jobs:
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// create a branch
|
// create a branch
|
||||||
err = repo_service.CreateNewBranchFromCommit(t.Context(), user2, repo, branch.CommitID, "test-action-run-name")
|
err = repo_service.CreateUpdateBranchFromCommit(t.Context(), user2, repo, branch.CommitID, "test-action-run-name", "")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{
|
run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{
|
||||||
Title: "run name without variables",
|
Title: "run name without variables",
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
package integration
|
package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
@ -18,14 +19,14 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func testAPIGetBranch(t *testing.T, branchName string, exists bool) {
|
func testAPIGetBranch(t *testing.T, user, repo, branchName string, exists bool) string {
|
||||||
token := getUserToken(t, "user2", auth_model.AccessTokenScopeReadRepository)
|
token := getUserToken(t, user, auth_model.AccessTokenScopeReadRepository)
|
||||||
req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/branches/%s", branchName).
|
req := NewRequestf(t, "GET", "/api/v1/repos/"+user+"/"+repo+"/branches/%s", branchName).
|
||||||
AddTokenAuth(token)
|
AddTokenAuth(token)
|
||||||
resp := MakeRequest(t, req, NoExpectedStatus)
|
resp := MakeRequest(t, req, NoExpectedStatus)
|
||||||
if !exists {
|
if !exists {
|
||||||
assert.Equal(t, http.StatusNotFound, resp.Code)
|
assert.Equal(t, http.StatusNotFound, resp.Code)
|
||||||
return
|
return ""
|
||||||
}
|
}
|
||||||
assert.Equal(t, http.StatusOK, resp.Code)
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
var branch api.Branch
|
var branch api.Branch
|
||||||
@ -33,6 +34,7 @@ func testAPIGetBranch(t *testing.T, branchName string, exists bool) {
|
|||||||
assert.Equal(t, branchName, branch.Name)
|
assert.Equal(t, branchName, branch.Name)
|
||||||
assert.True(t, branch.UserCanPush)
|
assert.True(t, branch.UserCanPush)
|
||||||
assert.True(t, branch.UserCanMerge)
|
assert.True(t, branch.UserCanMerge)
|
||||||
|
return branch.Commit.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
func testAPIGetBranchProtection(t *testing.T, branchName string, expectedHTTPStatus int) *api.BranchProtection {
|
func testAPIGetBranchProtection(t *testing.T, branchName string, expectedHTTPStatus int) *api.BranchProtection {
|
||||||
@ -103,7 +105,7 @@ func TestAPIGetBranch(t *testing.T) {
|
|||||||
{"feature/1", true},
|
{"feature/1", true},
|
||||||
{"feature/1/doesnotexist", false},
|
{"feature/1/doesnotexist", false},
|
||||||
} {
|
} {
|
||||||
testAPIGetBranch(t, test.BranchName, test.Exists)
|
testAPIGetBranch(t, "user2", "repo1", test.BranchName, test.Exists)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,7 +115,8 @@ func TestAPICreateBranch(t *testing.T) {
|
|||||||
|
|
||||||
func testAPICreateBranches(t *testing.T, giteaURL *url.URL) {
|
func testAPICreateBranches(t *testing.T, giteaURL *url.URL) {
|
||||||
username := "user2"
|
username := "user2"
|
||||||
ctx := NewAPITestContext(t, username, "my-noo-repo", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
|
reponame := "my-noo-repo"
|
||||||
|
ctx := NewAPITestContext(t, username, reponame, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
|
||||||
giteaURL.Path = ctx.GitPath()
|
giteaURL.Path = ctx.GitPath()
|
||||||
|
|
||||||
t.Run("CreateRepo", doAPICreateRepository(ctx, false))
|
t.Run("CreateRepo", doAPICreateRepository(ctx, false))
|
||||||
@ -164,7 +167,7 @@ func testAPICreateBranches(t *testing.T, giteaURL *url.URL) {
|
|||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
session := ctx.Session
|
session := ctx.Session
|
||||||
t.Run(test.NewBranch, func(t *testing.T) {
|
t.Run(test.NewBranch, func(t *testing.T) {
|
||||||
testAPICreateBranch(t, session, "user2", "my-noo-repo", test.OldBranch, test.NewBranch, test.ExpectedHTTPStatus)
|
testAPICreateBranch(t, session, username, reponame, test.OldBranch, test.NewBranch, test.ExpectedHTTPStatus)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -251,6 +254,81 @@ func testAPIRenameBranch(t *testing.T, doerName, ownerName, repoName, from, to s
|
|||||||
return MakeRequest(t, req, expectedHTTPStatus)
|
return MakeRequest(t, req, expectedHTTPStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAPIUpdateBranch(t *testing.T) {
|
||||||
|
onGiteaRun(t, testAPIUpdateBranches)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAPIUpdateBranches(t *testing.T, giteaURL *url.URL) {
|
||||||
|
username := "user2"
|
||||||
|
reponame := "my-nuu-repo"
|
||||||
|
branchname := "new-branch"
|
||||||
|
ctx := NewAPITestContext(t, username, reponame, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
|
||||||
|
giteaURL.Path = ctx.GitPath()
|
||||||
|
session := ctx.Session
|
||||||
|
|
||||||
|
t.Run("CreateRepo", doAPICreateRepository(ctx, false))
|
||||||
|
|
||||||
|
t.Run("create branch", func(t *testing.T) {
|
||||||
|
testAPICreateBranch(t, session, username, reponame, "", branchname, http.StatusCreated)
|
||||||
|
})
|
||||||
|
|
||||||
|
oldCommit := ""
|
||||||
|
t.Run("get commit ID", func(t *testing.T) {
|
||||||
|
oldCommit = testAPIGetBranch(t, username, reponame, branchname, true)
|
||||||
|
})
|
||||||
|
t.Run("advance branch", func(t *testing.T) {
|
||||||
|
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
|
||||||
|
req := NewRequestWithJSON(t, "POST", "/api/v1/repos/"+username+"/"+reponame+"/contents/a new file",
|
||||||
|
&api.CreateFileOptions{
|
||||||
|
FileOptions: api.FileOptions{
|
||||||
|
BranchName: branchname,
|
||||||
|
},
|
||||||
|
ContentBase64: base64.StdEncoding.EncodeToString([]byte("foo")),
|
||||||
|
}).AddTokenAuth(token)
|
||||||
|
resp := MakeRequest(t, req, http.StatusCreated)
|
||||||
|
assert.Equal(t, http.StatusCreated, resp.Result().StatusCode)
|
||||||
|
})
|
||||||
|
newCommit := ""
|
||||||
|
t.Run("get new commit ID", func(t *testing.T) {
|
||||||
|
newCommit = testAPIGetBranch(t, username, reponame, branchname, true)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("fail update nonexistent branch", func(t *testing.T) {
|
||||||
|
testAPIUpdateBranch(t, session, username, reponame, oldCommit, "no-branch-here", oldCommit, http.StatusNotFound)
|
||||||
|
})
|
||||||
|
t.Run("fail update on sha mismatch", func(t *testing.T) {
|
||||||
|
testAPIUpdateBranch(t, session, username, reponame, oldCommit, branchname, oldCommit, http.StatusConflict)
|
||||||
|
assert.Equal(t, newCommit, testAPIGetBranch(t, username, reponame, branchname, true))
|
||||||
|
})
|
||||||
|
t.Run("reset to old commit", func(t *testing.T) {
|
||||||
|
testAPIUpdateBranch(t, session, username, reponame, oldCommit, branchname, newCommit, http.StatusCreated)
|
||||||
|
assert.Equal(t, oldCommit, testAPIGetBranch(t, username, reponame, branchname, true))
|
||||||
|
})
|
||||||
|
t.Run("reset to new commit", func(t *testing.T) {
|
||||||
|
testAPIUpdateBranch(t, session, username, reponame, newCommit, branchname, oldCommit, http.StatusCreated)
|
||||||
|
assert.Equal(t, newCommit, testAPIGetBranch(t, username, reponame, branchname, true))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAPIUpdateBranch(t testing.TB, session *TestSession, user, repo, oldBranch, newBranch, sha string, status int) bool {
|
||||||
|
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
|
||||||
|
req := NewRequestWithJSON(t, "PUT", "/api/v1/repos/"+user+"/"+repo+"/branches", &api.UpdateBranchRepoOption{
|
||||||
|
BranchName: newBranch,
|
||||||
|
OldRefName: oldBranch,
|
||||||
|
SHA: sha,
|
||||||
|
}).AddTokenAuth(token)
|
||||||
|
resp := MakeRequest(t, req, status)
|
||||||
|
|
||||||
|
var branch api.Branch
|
||||||
|
DecodeJSON(t, resp, &branch)
|
||||||
|
|
||||||
|
if resp.Result().StatusCode == http.StatusCreated {
|
||||||
|
assert.Equal(t, newBranch, branch.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.Result().StatusCode == status
|
||||||
|
}
|
||||||
|
|
||||||
func TestAPIBranchProtection(t *testing.T) {
|
func TestAPIBranchProtection(t *testing.T) {
|
||||||
defer tests.PrepareTestEnv(t)()
|
defer tests.PrepareTestEnv(t)()
|
||||||
|
|
||||||
|
|||||||
@ -80,7 +80,7 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) {
|
|||||||
|
|
||||||
// Make a new branch in repo1
|
// Make a new branch in repo1
|
||||||
newBranch := "test_branch"
|
newBranch := "test_branch"
|
||||||
err = repo_service.CreateNewBranch(t.Context(), user2, repo1, repo1.DefaultBranch, newBranch)
|
err = repo_service.CreateUpdateBranch(t.Context(), user2, repo1, repo1.DefaultBranch, newBranch, "")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
commitID, _ := gitRepo.GetBranchCommitID(repo1.DefaultBranch)
|
commitID, _ := gitRepo.GetBranchCommitID(repo1.DefaultBranch)
|
||||||
|
|||||||
@ -85,7 +85,7 @@ func testAPIGetContents(t *testing.T, u *url.URL) {
|
|||||||
|
|
||||||
// Make a new branch in repo1
|
// Make a new branch in repo1
|
||||||
newBranch := "test_branch"
|
newBranch := "test_branch"
|
||||||
err = repo_service.CreateNewBranch(t.Context(), user2, repo1, repo1.DefaultBranch, newBranch)
|
err = repo_service.CreateUpdateBranch(t.Context(), user2, repo1, repo1.DefaultBranch, newBranch, "")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
commitID, err := gitRepo.GetBranchCommitID(repo1.DefaultBranch)
|
commitID, err := gitRepo.GetBranchCommitID(repo1.DefaultBranch)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user