mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-26 12:27:06 +00: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)"` | ||||
| } | ||||
|  | ||||
| // 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 | ||||
| // swagger:model | ||||
| type TransferRepoOption struct { | ||||
|   | ||||
| @@ -1195,6 +1195,7 @@ func Routes() *web.Router { | ||||
| 					m.Get("/*", repo.GetBranch) | ||||
| 					m.Delete("/*", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, repo.DeleteBranch) | ||||
| 					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)) | ||||
| 				m.Group("/branch_protections", func() { | ||||
| 					m.Get("", repo.ListBranchProtections) | ||||
|   | ||||
| @@ -386,6 +386,77 @@ func ListBranches(ctx *context.APIContext) { | ||||
| 	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 | ||||
| func GetBranchProtection(ctx *context.APIContext) { | ||||
| 	// swagger:operation GET /repos/{owner}/{repo}/branch_protections/{name} repository repoGetBranchProtection | ||||
|   | ||||
| @@ -90,6 +90,8 @@ type swaggerParameterBodies struct { | ||||
| 	// in:body | ||||
| 	EditRepoOption api.EditRepoOption | ||||
| 	// in:body | ||||
| 	UpdateBranchRepoOption api.UpdateBranchRepoOption | ||||
| 	// in:body | ||||
| 	TransferRepoOption api.TransferRepoOption | ||||
| 	// in:body | ||||
| 	CreateForkOption api.CreateForkOption | ||||
|   | ||||
							
								
								
									
										73
									
								
								templates/swagger/v1_json.tmpl
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										73
									
								
								templates/swagger/v1_json.tmpl
									
									
									
										generated
									
									
									
								
							| @@ -5045,6 +5045,63 @@ | ||||
|             "$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": { | ||||
| @@ -24968,6 +25025,22 @@ | ||||
|       }, | ||||
|       "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": { | ||||
|       "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", | ||||
|   | ||||
| @@ -5,6 +5,7 @@ package integration | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"net/http/httptest" | ||||
| 	"net/url" | ||||
| 	"testing" | ||||
|  | ||||
| @@ -186,6 +187,37 @@ func testAPICreateBranch(t testing.TB, session *TestSession, user, repo, oldBran | ||||
| 	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) { | ||||
| 	defer tests.PrepareTestEnv(t)() | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Kemal Zebari
					Kemal Zebari