Skip to content

Commit

Permalink
fix
Browse files Browse the repository at this point in the history
  • Loading branch information
wxiaoguang committed Jan 9, 2025
1 parent 2a02734 commit e7b1806
Show file tree
Hide file tree
Showing 6 changed files with 235 additions and 0 deletions.
8 changes: 8 additions & 0 deletions modules/structs/repo_branch.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,11 @@ type EditBranchProtectionOption struct {
type UpdateBranchProtectionPriories struct {
IDs []int64 `json:"ids"`
}

type MergeUpstreamRequest struct {
Branch string `json:"branch"`
}

type MergeUpstreamResponse struct {
MergeStyle string `json:"merge_type"`
}
1 change: 1 addition & 0 deletions routers/api/v1/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1190,6 +1190,7 @@ func Routes() *web.Router {
m.Get("/archive/*", reqRepoReader(unit.TypeCode), repo.GetArchive)
m.Combo("/forks").Get(repo.ListForks).
Post(reqToken(), reqRepoReader(unit.TypeCode), bind(api.CreateForkOption{}), repo.CreateFork)
m.Post("/merge-upstream", reqToken(), mustNotBeArchived, reqRepoWriter(unit.TypeCode), bind(api.MergeUpstreamRequest{}), repo.MergeUpstream)
m.Group("/branches", func() {
m.Get("", repo.ListBranches)
m.Get("/*", repo.GetBranch)
Expand Down
45 changes: 45 additions & 0 deletions routers/api/v1/repo/branch.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"code.gitea.io/gitea/modules/optional"
repo_module "code.gitea.io/gitea/modules/repository"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
"code.gitea.io/gitea/services/context"
Expand Down Expand Up @@ -1186,3 +1187,47 @@ func UpdateBranchProtectionPriories(ctx *context.APIContext) {

ctx.Status(http.StatusNoContent)
}

func MergeUpstream(ctx *context.APIContext) {
// swagger:operation POST /repos/{owner}/{repo}/merge-upstream repository repoMergeUpstream
// ---
// summary: Merge a branch from upstream
// 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/MergeUpstreamRequest"
// responses:
// "200":
// "$ref": "#/responses/MergeUpstreamResponse"
// "400":
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
form := web.GetForm(ctx).(*api.MergeUpstreamRequest)
mergeStyle, err := repo_service.MergeUpstream(ctx, ctx.Doer, ctx.Repo.Repository, form.Branch)
if err != nil {
if errors.Is(err, util.ErrInvalidArgument) {
ctx.Error(http.StatusBadRequest, "MergeUpstream", err)
return
} else if errors.Is(err, util.ErrNotExist) {
ctx.Error(http.StatusNotFound, "MergeUpstream", err)
return
}
ctx.Error(http.StatusInternalServerError, "MergeUpstream", err)
return
}
ctx.JSON(http.StatusOK, &api.MergeUpstreamResponse{MergeStyle: mergeStyle})
}
12 changes: 12 additions & 0 deletions routers/api/v1/swagger/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -448,3 +448,15 @@ type swaggerCompare struct {
// in:body
Body api.Compare `json:"body"`
}

// swagger:response MergeUpstreamRequest
type swaggerMergeUpstreamRequest struct {
// in:body
Body api.MergeUpstreamRequest `json:"body"`
}

// swagger:response MergeUpstreamResponse
type swaggerMergeUpstreamResponse struct {
// in:body
Body api.MergeUpstreamResponse `json:"body"`
}
78 changes: 78 additions & 0 deletions templates/swagger/v1_json.tmpl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

91 changes: 91 additions & 0 deletions tests/integration/repo_merge_upstream_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package integration

import (
"fmt"
"net/http"
"net/url"
"strings"
"testing"
"time"

auth_model "code.gitea.io/gitea/models/auth"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestRepoMergeUpstream(t *testing.T) {
onGiteaRun(t, func(*testing.T, *url.URL) {
forkUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})

baseRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
baseUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: baseRepo.OwnerID})

checkFileContent := func(exp string) {
req := NewRequest(t, "GET", fmt.Sprintf("/%s/test-repo-fork/raw/branch/master/new-file.txt", forkUser.Name))
resp := MakeRequest(t, req, http.StatusOK)
require.Equal(t, exp, resp.Body.String())
}

session := loginUser(t, forkUser.Name)
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)

// create a fork
req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/forks", baseUser.Name, baseRepo.Name), &api.CreateForkOption{
Name: util.ToPointer("test-repo-fork"),
}).AddTokenAuth(token)
MakeRequest(t, req, http.StatusAccepted)
forkRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: forkUser.ID, Name: "test-repo-fork"})

// add a file in base repo
require.NoError(t, createOrReplaceFileInBranch(baseUser, baseRepo, "new-file.txt", "master", "test-content-1"))

// the repo shows a prompt to "sync fork"
var mergeUpstreamLink string
require.Eventually(t, func() bool {
resp := session.MakeRequest(t, NewRequestf(t, "GET", "/%s/test-repo-fork", forkUser.Name), http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
respMsg, _ := htmlDoc.Find(".ui.message").Html()
if !strings.Contains(respMsg, `This branch is 1 commit behind <a href="/user2/repo1/src/branch/master">user2/repo1:master</a>`) {
return false
}
mergeUpstreamLink = htmlDoc.Find("button[data-url*='merge-upstream']").AttrOr("data-url", "")
require.NotEmpty(t, mergeUpstreamLink)
return true
}, 5*time.Second, 100*time.Millisecond)

// click the "sync fork" button
req = NewRequestWithValues(t, "POST", mergeUpstreamLink, map[string]string{"_csrf": GetUserCSRFToken(t, session)})
session.MakeRequest(t, req, http.StatusOK)
checkFileContent("test-content-1")

// update the files
require.NoError(t, createOrReplaceFileInBranch(forkUser, forkRepo, "new-file-other.txt", "master", "test-content-other"))
require.NoError(t, createOrReplaceFileInBranch(baseUser, baseRepo, "new-file.txt", "master", "test-content-2"))
require.Eventually(t, func() bool {
resp := session.MakeRequest(t, NewRequestf(t, "GET", "/%s/test-repo-fork", forkUser.Name), http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
respMsg, _ := htmlDoc.Find(".ui.message:not(.positive)").Html()
return strings.Contains(respMsg, `The base branch <a href="/user2/repo1/src/branch/master">user2/repo1:master</a> has new changes`)
}, 5*time.Second, 100*time.Millisecond)

// and do the merge-upstream by API
req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/test-repo-fork/merge-upstream", forkUser.Name), &api.MergeUpstreamRequest{
Branch: "master",
}).AddTokenAuth(token)
resp := MakeRequest(t, req, http.StatusOK)
checkFileContent("test-content-2")

var mergeResp api.MergeUpstreamResponse
DecodeJSON(t, resp, &mergeResp)
assert.Equal(t, "merge", mergeResp.MergeStyle)
})
}

0 comments on commit e7b1806

Please sign in to comment.