[API] Add branch delete (#11112)
* use same process as in routers/repo/branch.go/deleteBranch * make sure default branch can not be deleted * remove IsDefaultBranch from UI process - it is worth its own pull * permissions
This commit is contained in:
parent
0ef11ff2c9
commit
3c5a4d094a
4 changed files with 157 additions and 0 deletions
|
@ -80,6 +80,13 @@ func testAPIDeleteBranchProtection(t *testing.T, branchName string, expectedHTTP
|
||||||
session.MakeRequest(t, req, expectedHTTPStatus)
|
session.MakeRequest(t, req, expectedHTTPStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testAPIDeleteBranch(t *testing.T, branchName string, expectedHTTPStatus int) {
|
||||||
|
session := loginUser(t, "user2")
|
||||||
|
token := getTokenForLoggedInUser(t, session)
|
||||||
|
req := NewRequestf(t, "DELETE", "/api/v1/repos/user2/repo1/branches/%s?token=%s", branchName, token)
|
||||||
|
session.MakeRequest(t, req, expectedHTTPStatus)
|
||||||
|
}
|
||||||
|
|
||||||
func TestAPIGetBranch(t *testing.T) {
|
func TestAPIGetBranch(t *testing.T) {
|
||||||
for _, test := range []struct {
|
for _, test := range []struct {
|
||||||
BranchName string
|
BranchName string
|
||||||
|
@ -106,10 +113,17 @@ func TestAPIBranchProtection(t *testing.T) {
|
||||||
// Can only create once
|
// Can only create once
|
||||||
testAPICreateBranchProtection(t, "master", http.StatusForbidden)
|
testAPICreateBranchProtection(t, "master", http.StatusForbidden)
|
||||||
|
|
||||||
|
// Can't delete a protected branch
|
||||||
|
testAPIDeleteBranch(t, "master", http.StatusForbidden)
|
||||||
|
|
||||||
testAPIGetBranchProtection(t, "master", http.StatusOK)
|
testAPIGetBranchProtection(t, "master", http.StatusOK)
|
||||||
testAPIEditBranchProtection(t, "master", &api.BranchProtection{
|
testAPIEditBranchProtection(t, "master", &api.BranchProtection{
|
||||||
EnablePush: true,
|
EnablePush: true,
|
||||||
}, http.StatusOK)
|
}, http.StatusOK)
|
||||||
|
|
||||||
testAPIDeleteBranchProtection(t, "master", http.StatusNoContent)
|
testAPIDeleteBranchProtection(t, "master", http.StatusNoContent)
|
||||||
|
|
||||||
|
// Test branch deletion
|
||||||
|
testAPIDeleteBranch(t, "master", http.StatusForbidden)
|
||||||
|
testAPIDeleteBranch(t, "branch2", http.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
|
@ -664,6 +664,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
m.Group("/branches", func() {
|
m.Group("/branches", func() {
|
||||||
m.Get("", repo.ListBranches)
|
m.Get("", repo.ListBranches)
|
||||||
m.Get("/*", context.RepoRefByType(context.RepoRefBranch), repo.GetBranch)
|
m.Get("/*", context.RepoRefByType(context.RepoRefBranch), repo.GetBranch)
|
||||||
|
m.Delete("/*", reqRepoWriter(models.UnitTypeCode), context.RepoRefByType(context.RepoRefBranch), repo.DeleteBranch)
|
||||||
}, reqRepoReader(models.UnitTypeCode))
|
}, reqRepoReader(models.UnitTypeCode))
|
||||||
m.Group("/branch_protections", func() {
|
m.Group("/branch_protections", func() {
|
||||||
m.Get("", repo.ListBranchProtections)
|
m.Get("", repo.ListBranchProtections)
|
||||||
|
|
|
@ -6,12 +6,15 @@
|
||||||
package repo
|
package repo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/convert"
|
"code.gitea.io/gitea/modules/convert"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/repofiles"
|
||||||
repo_module "code.gitea.io/gitea/modules/repository"
|
repo_module "code.gitea.io/gitea/modules/repository"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
)
|
)
|
||||||
|
@ -81,6 +84,104 @@ func GetBranch(ctx *context.APIContext) {
|
||||||
ctx.JSON(http.StatusOK, br)
|
ctx.JSON(http.StatusOK, br)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteBranch get a branch of a repository
|
||||||
|
func DeleteBranch(ctx *context.APIContext) {
|
||||||
|
// swagger:operation DELETE /repos/{owner}/{repo}/branches/{branch} repository repoDeleteBranch
|
||||||
|
// ---
|
||||||
|
// summary: Delete a specific branch from a repository
|
||||||
|
// 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: branch
|
||||||
|
// in: path
|
||||||
|
// description: branch to delete
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// responses:
|
||||||
|
// "204":
|
||||||
|
// "$ref": "#/responses/empty"
|
||||||
|
// "403":
|
||||||
|
// "$ref": "#/responses/error"
|
||||||
|
|
||||||
|
if ctx.Repo.TreePath != "" {
|
||||||
|
// if TreePath != "", then URL contained extra slashes
|
||||||
|
// (i.e. "master/subbranch" instead of "master"), so branch does
|
||||||
|
// not exist
|
||||||
|
ctx.NotFound()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.Repo.Repository.DefaultBranch == ctx.Repo.BranchName {
|
||||||
|
ctx.Error(http.StatusForbidden, "DefaultBranch", fmt.Errorf("can not delete default branch"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isProtected, err := ctx.Repo.Repository.IsProtectedBranch(ctx.Repo.BranchName, ctx.User)
|
||||||
|
if err != nil {
|
||||||
|
ctx.InternalServerError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if isProtected {
|
||||||
|
ctx.Error(http.StatusForbidden, "IsProtectedBranch", fmt.Errorf("branch protected"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
branch, err := repo_module.GetBranch(ctx.Repo.Repository, ctx.Repo.BranchName)
|
||||||
|
if err != nil {
|
||||||
|
if git.IsErrBranchNotExist(err) {
|
||||||
|
ctx.NotFound(err)
|
||||||
|
} else {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "GetBranch", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := branch.GetCommit()
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "GetCommit", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ctx.Repo.GitRepo.DeleteBranch(ctx.Repo.BranchName, git.DeleteBranchOptions{
|
||||||
|
Force: true,
|
||||||
|
}); err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "DeleteBranch", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't return error below this
|
||||||
|
if err := repofiles.PushUpdate(
|
||||||
|
ctx.Repo.Repository,
|
||||||
|
ctx.Repo.BranchName,
|
||||||
|
repofiles.PushUpdateOptions{
|
||||||
|
RefFullName: git.BranchPrefix + ctx.Repo.BranchName,
|
||||||
|
OldCommitID: c.ID.String(),
|
||||||
|
NewCommitID: git.EmptySHA,
|
||||||
|
PusherID: ctx.User.ID,
|
||||||
|
PusherName: ctx.User.Name,
|
||||||
|
RepoUserName: ctx.Repo.Owner.Name,
|
||||||
|
RepoName: ctx.Repo.Repository.Name,
|
||||||
|
}); err != nil {
|
||||||
|
log.Error("Update: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ctx.Repo.Repository.AddDeletedBranch(ctx.Repo.BranchName, c.ID.String(), ctx.User.ID); err != nil {
|
||||||
|
log.Warn("AddDeletedBranch: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Status(http.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
// ListBranches list all the branches of a repository
|
// ListBranches list all the branches of a repository
|
||||||
func ListBranches(ctx *context.APIContext) {
|
func ListBranches(ctx *context.APIContext) {
|
||||||
// swagger:operation GET /repos/{owner}/{repo}/branches repository repoListBranches
|
// swagger:operation GET /repos/{owner}/{repo}/branches repository repoListBranches
|
||||||
|
|
|
@ -2269,6 +2269,47 @@
|
||||||
"$ref": "#/responses/Branch"
|
"$ref": "#/responses/Branch"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"repository"
|
||||||
|
],
|
||||||
|
"summary": "Delete a specific branch from a repository",
|
||||||
|
"operationId": "repoDeleteBranch",
|
||||||
|
"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
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "branch to delete",
|
||||||
|
"name": "branch",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"204": {
|
||||||
|
"$ref": "#/responses/empty"
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"$ref": "#/responses/error"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/repos/{owner}/{repo}/collaborators": {
|
"/repos/{owner}/{repo}/collaborators": {
|
||||||
|
|
Reference in a new issue