From 2036f4bb44eb9ce0accf68c48cf3539ccef28273 Mon Sep 17 00:00:00 2001 From: Terence Le Huu Phuong Date: Sun, 24 May 2020 17:07:46 +0200 Subject: [PATCH] [FEATURE] [API] Add Endpoint for Branch Creation Issue: https://github.com/go-gitea/gitea/issues/11376 This commit introduces an API endpoint for branch creation. The added route is POST /repos/{owner}/{repo}/branches. A JSON with the name of the new branch and the name of the old branch is required as parameters. Signed-off-by: Terence Le Huu Phuong --- modules/structs/repo.go | 16 ++++++ routers/api/v1/api.go | 1 + routers/api/v1/repo/branch.go | 86 +++++++++++++++++++++++++++++++ routers/api/v1/swagger/options.go | 3 ++ templates/swagger/v1_json.tmpl | 66 ++++++++++++++++++++++++ 5 files changed, 172 insertions(+) diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 70de9b74694f7..832d330e749a2 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -160,6 +160,22 @@ type EditRepoOption struct { Archived *bool `json:"archived,omitempty"` } +// CreateBranchRepoOption options when creating a branch in a repository +// swagger:model +type CreateBranchRepoOption struct { + + // Name of the branch to create + // + // required: true + // unique: true + BranchName string `json:"new_branch_name" binding:"Required;GitRefName;MaxSize(100)"` + + // Name of the old branch to create from + // + // unique: true + OldBranchName string `json:"old_branch_name" binding:"GitRefName;MaxSize(100)"` +} + // TransferRepoOption options when transfer a repository's ownership // swagger:model type TransferRepoOption struct { diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 0d62b751cc898..2eb39f607008e 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -665,6 +665,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Get("", repo.ListBranches) m.Get("/*", context.RepoRefByType(context.RepoRefBranch), repo.GetBranch) m.Delete("/*", reqRepoWriter(models.UnitTypeCode), context.RepoRefByType(context.RepoRefBranch), repo.DeleteBranch) + m.Post("", reqRepoWriter(models.UnitTypeCode), bind(api.CreateBranchRepoOption{}), repo.CreateBranch) }, reqRepoReader(models.UnitTypeCode)) m.Group("/branch_protections", func() { m.Get("", repo.ListBranchProtections) diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go index 57c74d7dab86a..8c27bf73074c9 100644 --- a/routers/api/v1/repo/branch.go +++ b/routers/api/v1/repo/branch.go @@ -182,6 +182,92 @@ func DeleteBranch(ctx *context.APIContext) { ctx.Status(http.StatusNoContent) } +// CreateRepoBranch creates a branch for a user's repository +func CreateRepoBranch(ctx *context.APIContext, opt api.CreateBranchRepoOption) { + + if len(opt.OldBranchName) == 0 { + opt.OldBranchName = ctx.Repo.Repository.DefaultBranch + } + + err := repo_module.CreateNewBranch(ctx.User, ctx.Repo.Repository, opt.OldBranchName, opt.BranchName) + + if err != nil { + if models.IsErrTagAlreadyExists(err) { + ctx.Error(http.StatusConflict, "", "The branch with the same tag already exists.") + + } else if models.IsErrBranchAlreadyExists(err) || git.IsErrPushOutOfDate(err) { + ctx.Error(http.StatusConflict, "", "The branch already exists.") + + } else if models.IsErrBranchNameConflict(err) { + ctx.Error(http.StatusConflict, "", "The branch with the same name already exists.") + + } else { + ctx.Error(http.StatusInternalServerError, "CreateRepoBranch", err) + + } + return + } + + branch, err := repo_module.GetBranch(ctx.Repo.Repository, opt.BranchName) + if err != nil { + ctx.Error(http.StatusInternalServerError, "GetBranch", err) + return + } + + commit, err := branch.GetCommit() + if err != nil { + ctx.Error(http.StatusInternalServerError, "GetCommit", err) + return + } + + branchProtection, err := ctx.Repo.Repository.GetBranchProtection(branch.Name) + if err != nil { + ctx.Error(http.StatusInternalServerError, "GetBranchProtection", err) + return + } + + br, err := convert.ToBranch(ctx.Repo.Repository, branch, commit, branchProtection, ctx.User, ctx.Repo.IsAdmin()) + if err != nil { + ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err) + return + } + + ctx.JSON(http.StatusCreated, br) +} + +// CreateBranch create a branch of a repository +func CreateBranch(ctx *context.APIContext, opt api.CreateBranchRepoOption) { + // swagger:operation POST /repos/{owner}/{repo}/branches repository repoCreateBranch + // --- + // summary: Create 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/CreateBranchRepoOption" + // responses: + // "201": + // "$ref": "#/responses/Branch" + // "409": + // description: The branch with the same name already exists. + + CreateRepoBranch(ctx, opt) +} + // ListBranches list all the branches of a repository func ListBranches(ctx *context.APIContext) { // swagger:operation GET /repos/{owner}/{repo}/branches repository repoListBranches diff --git a/routers/api/v1/swagger/options.go b/routers/api/v1/swagger/options.go index f13dc63864366..d9ef05c335991 100644 --- a/routers/api/v1/swagger/options.go +++ b/routers/api/v1/swagger/options.go @@ -129,6 +129,9 @@ type swaggerParameterBodies struct { // in:body EditReactionOption api.EditReactionOption + // in:body + CreateBranchRepoOption api.CreateBranchRepoOption + // in:body CreateBranchProtectionOption api.CreateBranchProtectionOption diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 0cbe33bd247ba..7590f19d5df08 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -2241,6 +2241,50 @@ "$ref": "#/responses/BranchList" } } + }, + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "repository" + ], + "summary": "Create a branch", + "operationId": "repoCreateBranch", + "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/CreateBranchRepoOption" + } + } + ], + "responses": { + "201": { + "$ref": "#/responses/Branch" + }, + "409": { + "description": "The branch with the same name already exists." + } + } } }, "/repos/{owner}/{repo}/branches/{branch}": { @@ -10886,6 +10930,28 @@ }, "x-go-package": "code.gitea.io/gitea/modules/structs" }, + "CreateBranchRepoOption": { + "description": "CreateBranchRepoOption options when creating a branch in a repository", + "type": "object", + "required": [ + "new_branch_name" + ], + "properties": { + "new_branch_name": { + "description": "Name of the branch to create", + "type": "string", + "uniqueItems": true, + "x-go-name": "BranchName" + }, + "old_branch_name": { + "description": "Name of the old branch to create from", + "type": "string", + "uniqueItems": true, + "x-go-name": "OldBranchName" + } + }, + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, "CreateEmailOption": { "description": "CreateEmailOption options when creating email addresses", "type": "object",