mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 19:06:18 +01:00 
			
		
		
		
	Implement update branch API (#32433)
Resolves #22526. Builds upon #23061. --------- Co-authored-by: sillyguodong <33891828+sillyguodong@users.noreply.github.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
		| @@ -278,6 +278,16 @@ type CreateBranchRepoOption struct { | |||||||
| 	OldRefName string `json:"old_ref_name" binding:"GitRefName;MaxSize(100)"` | 	OldRefName string `json:"old_ref_name" binding:"GitRefName;MaxSize(100)"` | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // UpdateBranchRepoOption options when updating a branch in a repository | ||||||
|  | // swagger:model | ||||||
|  | type UpdateBranchRepoOption struct { | ||||||
|  | 	// New branch name | ||||||
|  | 	// | ||||||
|  | 	// required: true | ||||||
|  | 	// unique: true | ||||||
|  | 	Name string `json:"name" binding:"Required;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 { | ||||||
|   | |||||||
| @@ -1195,6 +1195,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.Patch("/*", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, bind(api.UpdateBranchRepoOption{}), repo.UpdateBranch) | ||||||
| 				}, context.ReferencesGitRepo(), reqRepoReader(unit.TypeCode)) | 				}, context.ReferencesGitRepo(), reqRepoReader(unit.TypeCode)) | ||||||
| 				m.Group("/branch_protections", func() { | 				m.Group("/branch_protections", func() { | ||||||
| 					m.Get("", repo.ListBranchProtections) | 					m.Get("", repo.ListBranchProtections) | ||||||
|   | |||||||
| @@ -386,6 +386,77 @@ func ListBranches(ctx *context.APIContext) { | |||||||
| 	ctx.JSON(http.StatusOK, apiBranches) | 	ctx.JSON(http.StatusOK, apiBranches) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // UpdateBranch updates a repository's branch. | ||||||
|  | func UpdateBranch(ctx *context.APIContext) { | ||||||
|  | 	// swagger:operation PATCH /repos/{owner}/{repo}/branches/{branch} 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: branch | ||||||
|  | 	//   in: path | ||||||
|  | 	//   description: name of the branch | ||||||
|  | 	//   type: string | ||||||
|  | 	//   required: true | ||||||
|  | 	// - name: body | ||||||
|  | 	//   in: body | ||||||
|  | 	//   schema: | ||||||
|  | 	//     "$ref": "#/definitions/UpdateBranchRepoOption" | ||||||
|  | 	// responses: | ||||||
|  | 	//   "204": | ||||||
|  | 	//     "$ref": "#/responses/empty" | ||||||
|  | 	//   "403": | ||||||
|  | 	//     "$ref": "#/responses/forbidden" | ||||||
|  | 	//   "404": | ||||||
|  | 	//     "$ref": "#/responses/notFound" | ||||||
|  | 	//   "422": | ||||||
|  | 	//     "$ref": "#/responses/validationError" | ||||||
|  |  | ||||||
|  | 	opt := web.GetForm(ctx).(*api.UpdateBranchRepoOption) | ||||||
|  |  | ||||||
|  | 	oldName := ctx.PathParam("*") | ||||||
|  | 	repo := ctx.Repo.Repository | ||||||
|  |  | ||||||
|  | 	if repo.IsEmpty { | ||||||
|  | 		ctx.Error(http.StatusNotFound, "", "Git Repository is empty.") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if repo.IsMirror { | ||||||
|  | 		ctx.Error(http.StatusForbidden, "", "Git Repository is a mirror.") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	msg, err := repo_service.RenameBranch(ctx, repo, ctx.Doer, ctx.Repo.GitRepo, oldName, opt.Name) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.Error(http.StatusInternalServerError, "RenameBranch", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if msg == "target_exist" { | ||||||
|  | 		ctx.Error(http.StatusUnprocessableEntity, "", "Cannot rename a branch using the same name or rename to a branch that already exists.") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if msg == "from_not_exist" { | ||||||
|  | 		ctx.Error(http.StatusNotFound, "", "Branch doesn't exist.") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.Status(http.StatusNoContent) | ||||||
|  | } | ||||||
|  |  | ||||||
| // GetBranchProtection gets a branch protection | // GetBranchProtection gets a branch protection | ||||||
| func GetBranchProtection(ctx *context.APIContext) { | func GetBranchProtection(ctx *context.APIContext) { | ||||||
| 	// swagger:operation GET /repos/{owner}/{repo}/branch_protections/{name} repository repoGetBranchProtection | 	// swagger:operation GET /repos/{owner}/{repo}/branch_protections/{name} repository repoGetBranchProtection | ||||||
|   | |||||||
| @@ -90,6 +90,8 @@ type swaggerParameterBodies struct { | |||||||
| 	// in:body | 	// in:body | ||||||
| 	EditRepoOption api.EditRepoOption | 	EditRepoOption api.EditRepoOption | ||||||
| 	// in:body | 	// in:body | ||||||
|  | 	UpdateBranchRepoOption api.UpdateBranchRepoOption | ||||||
|  | 	// in:body | ||||||
| 	TransferRepoOption api.TransferRepoOption | 	TransferRepoOption api.TransferRepoOption | ||||||
| 	// in:body | 	// in:body | ||||||
| 	CreateForkOption api.CreateForkOption | 	CreateForkOption api.CreateForkOption | ||||||
|   | |||||||
							
								
								
									
										73
									
								
								templates/swagger/v1_json.tmpl
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										73
									
								
								templates/swagger/v1_json.tmpl
									
									
									
										generated
									
									
									
								
							| @@ -5045,6 +5045,63 @@ | |||||||
|             "$ref": "#/responses/repoArchivedError" |             "$ref": "#/responses/repoArchivedError" | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|  |       }, | ||||||
|  |       "patch": { | ||||||
|  |         "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 | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             "type": "string", | ||||||
|  |             "description": "name of the branch", | ||||||
|  |             "name": "branch", | ||||||
|  |             "in": "path", | ||||||
|  |             "required": true | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             "name": "body", | ||||||
|  |             "in": "body", | ||||||
|  |             "schema": { | ||||||
|  |               "$ref": "#/definitions/UpdateBranchRepoOption" | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         ], | ||||||
|  |         "responses": { | ||||||
|  |           "204": { | ||||||
|  |             "$ref": "#/responses/empty" | ||||||
|  |           }, | ||||||
|  |           "403": { | ||||||
|  |             "$ref": "#/responses/forbidden" | ||||||
|  |           }, | ||||||
|  |           "404": { | ||||||
|  |             "$ref": "#/responses/notFound" | ||||||
|  |           }, | ||||||
|  |           "422": { | ||||||
|  |             "$ref": "#/responses/validationError" | ||||||
|  |           } | ||||||
|  |         } | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "/repos/{owner}/{repo}/collaborators": { |     "/repos/{owner}/{repo}/collaborators": { | ||||||
| @@ -24968,6 +25025,22 @@ | |||||||
|       }, |       }, | ||||||
|       "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 in a repository", | ||||||
|  |       "type": "object", | ||||||
|  |       "required": [ | ||||||
|  |         "name" | ||||||
|  |       ], | ||||||
|  |       "properties": { | ||||||
|  |         "name": { | ||||||
|  |           "description": "New branch name", | ||||||
|  |           "type": "string", | ||||||
|  |           "uniqueItems": true, | ||||||
|  |           "x-go-name": "Name" | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |       "x-go-package": "code.gitea.io/gitea/modules/structs" | ||||||
|  |     }, | ||||||
|     "UpdateFileOptions": { |     "UpdateFileOptions": { | ||||||
|       "description": "UpdateFileOptions options for updating files\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 files\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", | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ package integration | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"net/http" | 	"net/http" | ||||||
|  | 	"net/http/httptest" | ||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| @@ -186,6 +187,37 @@ func testAPICreateBranch(t testing.TB, session *TestSession, user, repo, oldBran | |||||||
| 	return resp.Result().StatusCode == status | 	return resp.Result().StatusCode == status | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestAPIUpdateBranch(t *testing.T) { | ||||||
|  | 	onGiteaRun(t, func(t *testing.T, _ *url.URL) { | ||||||
|  | 		t.Run("UpdateBranchWithEmptyRepo", func(t *testing.T) { | ||||||
|  | 			testAPIUpdateBranch(t, "user10", "repo6", "master", "test", http.StatusNotFound) | ||||||
|  | 		}) | ||||||
|  | 		t.Run("UpdateBranchWithSameBranchNames", func(t *testing.T) { | ||||||
|  | 			resp := testAPIUpdateBranch(t, "user2", "repo1", "master", "master", http.StatusUnprocessableEntity) | ||||||
|  | 			assert.Contains(t, resp.Body.String(), "Cannot rename a branch using the same name or rename to a branch that already exists.") | ||||||
|  | 		}) | ||||||
|  | 		t.Run("UpdateBranchThatAlreadyExists", func(t *testing.T) { | ||||||
|  | 			resp := testAPIUpdateBranch(t, "user2", "repo1", "master", "branch2", http.StatusUnprocessableEntity) | ||||||
|  | 			assert.Contains(t, resp.Body.String(), "Cannot rename a branch using the same name or rename to a branch that already exists.") | ||||||
|  | 		}) | ||||||
|  | 		t.Run("UpdateBranchWithNonExistentBranch", func(t *testing.T) { | ||||||
|  | 			resp := testAPIUpdateBranch(t, "user2", "repo1", "i-dont-exist", "new-branch-name", http.StatusNotFound) | ||||||
|  | 			assert.Contains(t, resp.Body.String(), "Branch doesn't exist.") | ||||||
|  | 		}) | ||||||
|  | 		t.Run("RenameBranchNormalScenario", func(t *testing.T) { | ||||||
|  | 			testAPIUpdateBranch(t, "user2", "repo1", "branch2", "new-branch-name", http.StatusNoContent) | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func testAPIUpdateBranch(t *testing.T, ownerName, repoName, from, to string, expectedHTTPStatus int) *httptest.ResponseRecorder { | ||||||
|  | 	token := getUserToken(t, ownerName, auth_model.AccessTokenScopeWriteRepository) | ||||||
|  | 	req := NewRequestWithJSON(t, "PATCH", "api/v1/repos/"+ownerName+"/"+repoName+"/branches/"+from, &api.UpdateBranchRepoOption{ | ||||||
|  | 		Name: to, | ||||||
|  | 	}).AddTokenAuth(token) | ||||||
|  | 	return MakeRequest(t, req, expectedHTTPStatus) | ||||||
|  | } | ||||||
|  |  | ||||||
| func TestAPIBranchProtection(t *testing.T) { | func TestAPIBranchProtection(t *testing.T) { | ||||||
| 	defer tests.PrepareTestEnv(t)() | 	defer tests.PrepareTestEnv(t)() | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user