diff --git a/integrations/api_branch_test.go b/integrations/api_branch_test.go
index acf7525f80..26d8fb4b45 100644
--- a/integrations/api_branch_test.go
+++ b/integrations/api_branch_test.go
@@ -6,6 +6,7 @@ package integrations
 
 import (
 	"net/http"
+	"net/url"
 	"testing"
 
 	api "code.gitea.io/gitea/modules/structs"
@@ -100,6 +101,72 @@ func TestAPIGetBranch(t *testing.T) {
 	}
 }
 
+func TestAPICreateBranch(t *testing.T) {
+	onGiteaRun(t, testAPICreateBranches)
+}
+
+func testAPICreateBranches(t *testing.T, giteaURL *url.URL) {
+
+	username := "user2"
+	ctx := NewAPITestContext(t, username, "my-noo-repo")
+	giteaURL.Path = ctx.GitPath()
+
+	t.Run("CreateRepo", doAPICreateRepository(ctx, false))
+	tests := []struct {
+		OldBranch          string
+		NewBranch          string
+		ExpectedHTTPStatus int
+	}{
+		// Creating branch from default branch
+		{
+			OldBranch:          "",
+			NewBranch:          "new_branch_from_default_branch",
+			ExpectedHTTPStatus: http.StatusCreated,
+		},
+		// Creating branch from master
+		{
+			OldBranch:          "master",
+			NewBranch:          "new_branch_from_master_1",
+			ExpectedHTTPStatus: http.StatusCreated,
+		},
+		// Trying to create from master but already exists
+		{
+			OldBranch:          "master",
+			NewBranch:          "new_branch_from_master_1",
+			ExpectedHTTPStatus: http.StatusConflict,
+		},
+		// Trying to create from other branch (not default branch)
+		{
+			OldBranch:          "new_branch_from_master_1",
+			NewBranch:          "branch_2",
+			ExpectedHTTPStatus: http.StatusCreated,
+		},
+		// Trying to create from a branch which does not exist
+		{
+			OldBranch:          "does_not_exist",
+			NewBranch:          "new_branch_from_non_existent",
+			ExpectedHTTPStatus: http.StatusNotFound,
+		},
+	}
+	for _, test := range tests {
+		defer resetFixtures(t)
+		session := ctx.Session
+		token := getTokenForLoggedInUser(t, session)
+		req := NewRequestWithJSON(t, "POST", "/api/v1/repos/user2/my-noo-repo/branches?token="+token, &api.CreateBranchRepoOption{
+			BranchName:    test.NewBranch,
+			OldBranchName: test.OldBranch,
+		})
+		resp := session.MakeRequest(t, req, test.ExpectedHTTPStatus)
+
+		var branch api.Branch
+		DecodeJSON(t, resp, &branch)
+
+		if test.ExpectedHTTPStatus == http.StatusCreated {
+			assert.EqualValues(t, test.NewBranch, branch.Name)
+		}
+	}
+}
+
 func TestAPIBranchProtection(t *testing.T) {
 	defer prepareTestEnv(t)()
 
diff --git a/integrations/integration_test.go b/integrations/integration_test.go
index c6a4169751..3c0125af6c 100644
--- a/integrations/integration_test.go
+++ b/integrations/integration_test.go
@@ -26,6 +26,7 @@ import (
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/graceful"
+	"code.gitea.io/gitea/modules/queue"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/routers"
 	"code.gitea.io/gitea/routers/routes"
@@ -459,3 +460,14 @@ func GetCSRF(t testing.TB, session *TestSession, urlStr string) string {
 	doc := NewHTMLParser(t, resp.Body)
 	return doc.GetCSRF()
 }
+
+// resetFixtures flushes queues, reloads fixtures and resets test repositories within a single test.
+// Most tests should call defer prepareTestEnv(t)() (or have onGiteaRun do that for them) but sometimes
+// within a single test this is required
+func resetFixtures(t *testing.T) {
+	assert.NoError(t, queue.GetManager().FlushAll(context.Background(), -1))
+	assert.NoError(t, models.LoadFixtures())
+	assert.NoError(t, os.RemoveAll(setting.RepoRootPath))
+	assert.NoError(t, com.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"),
+		setting.RepoRootPath))
+}
diff --git a/models/error.go b/models/error.go
index 3b05a7152c..e9343cbe7c 100644
--- a/models/error.go
+++ b/models/error.go
@@ -995,6 +995,21 @@ func IsErrWontSign(err error) bool {
 //  |______  / |__|  (____  /___|  /\___  >___|  /
 //         \/             \/     \/     \/     \/
 
+// ErrBranchDoesNotExist represents an error that branch with such name does not exist.
+type ErrBranchDoesNotExist struct {
+	BranchName string
+}
+
+// IsErrBranchDoesNotExist checks if an error is an ErrBranchDoesNotExist.
+func IsErrBranchDoesNotExist(err error) bool {
+	_, ok := err.(ErrBranchDoesNotExist)
+	return ok
+}
+
+func (err ErrBranchDoesNotExist) Error() string {
+	return fmt.Sprintf("branch does not exist [name: %s]", err.BranchName)
+}
+
 // ErrBranchAlreadyExists represents an error that branch with such name already exists.
 type ErrBranchAlreadyExists struct {
 	BranchName string
diff --git a/modules/repository/branch.go b/modules/repository/branch.go
index 418ba25c89..94be6f0f5a 100644
--- a/modules/repository/branch.go
+++ b/modules/repository/branch.go
@@ -71,7 +71,9 @@ func CreateNewBranch(doer *models.User, repo *models.Repository, oldBranchName,
 	}
 
 	if !git.IsBranchExist(repo.RepoPath(), oldBranchName) {
-		return fmt.Errorf("OldBranch: %s does not exist. Cannot create new branch from this", oldBranchName)
+		return models.ErrBranchDoesNotExist{
+			BranchName: oldBranchName,
+		}
 	}
 
 	basePath, err := models.CreateTemporaryPath("branch-maker")
diff --git a/modules/structs/repo.go b/modules/structs/repo.go
index 70de9b7469..832d330e74 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 0d62b751cc..2eb39f6070 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 57c74d7dab..90db597ef7 100644
--- a/routers/api/v1/repo/branch.go
+++ b/routers/api/v1/repo/branch.go
@@ -182,6 +182,96 @@ func DeleteBranch(ctx *context.APIContext) {
 	ctx.Status(http.StatusNoContent)
 }
 
+// CreateBranch creates a branch for a user's 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"
+	//   "404":
+	//     description: The old branch does not exist.
+	//   "409":
+	//     description: The branch with the same name already exists.
+
+	if ctx.Repo.Repository.IsEmpty {
+		ctx.Error(http.StatusNotFound, "", "Git Repository is empty.")
+		return
+	}
+
+	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.IsErrBranchDoesNotExist(err) {
+			ctx.Error(http.StatusNotFound, "", "The old branch does not exist")
+		}
+		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)
+}
+
 // 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 f13dc63864..d9ef05c335 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 0cbe33bd24..70f12b083f 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -2241,6 +2241,53 @@
             "$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"
+          },
+          "404": {
+            "description": "The old branch does not exist."
+          },
+          "409": {
+            "description": "The branch with the same name already exists."
+          }
+        }
       }
     },
     "/repos/{owner}/{repo}/branches/{branch}": {
@@ -10886,6 +10933,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",