Skip to content

Commit

Permalink
Fix sync fork for consistency (#33147) (#33192)
Browse files Browse the repository at this point in the history
Backport #33147 by changchaishi

Fixes #33145

An integration test could be added.

---------

Co-authored-by: Chai-Shi <changchaishi@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
  • Loading branch information
3 people authored Jan 10, 2025
1 parent 940a930 commit d703488
Show file tree
Hide file tree
Showing 8 changed files with 309 additions and 12 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 @@ -19,6 +19,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 @@ -1194,3 +1195,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"`
}
49 changes: 40 additions & 9 deletions services/repository/merge_upstream.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,19 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/pull"
)

type UpstreamDivergingInfo struct {
BaseIsNewer bool
CommitsBehind int
CommitsAhead int
BaseHasNewCommits bool
CommitsBehind int
CommitsAhead int
}

// MergeUpstream merges the base repository's default branch into the fork repository's current branch.
func MergeUpstream(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, branch string) (mergeStyle string, err error) {
if err = repo.MustNotBeArchived(); err != nil {
return "", err
Expand All @@ -32,7 +34,7 @@ func MergeUpstream(ctx context.Context, doer *user_model.User, repo *repo_model.
}
err = git.Push(ctx, repo.BaseRepo.RepoPath(), git.PushOptions{
Remote: repo.RepoPath(),
Branch: fmt.Sprintf("%s:%s", branch, branch),
Branch: fmt.Sprintf("%s:%s", repo.BaseRepo.DefaultBranch, branch),
Env: repo_module.PushingEnvironment(doer, repo),
})
if err == nil {
Expand Down Expand Up @@ -64,7 +66,7 @@ func MergeUpstream(ctx context.Context, doer *user_model.User, repo *repo_model.
BaseRepoID: repo.BaseRepo.ID,
BaseRepo: repo.BaseRepo,
HeadBranch: branch, // maybe HeadCommitID is not needed
BaseBranch: branch,
BaseBranch: repo.BaseRepo.DefaultBranch,
}
fakeIssue.PullRequest = fakePR
err = pull.Update(ctx, fakePR, doer, "merge upstream", false)
Expand All @@ -74,6 +76,7 @@ func MergeUpstream(ctx context.Context, doer *user_model.User, repo *repo_model.
return "merge", nil
}

// GetUpstreamDivergingInfo returns the information about the divergence between the fork repository's branch and the base repository's default branch.
func GetUpstreamDivergingInfo(ctx context.Context, repo *repo_model.Repository, branch string) (*UpstreamDivergingInfo, error) {
if !repo.IsFork {
return nil, util.NewInvalidArgumentErrorf("repo is not a fork")
Expand All @@ -92,7 +95,7 @@ func GetUpstreamDivergingInfo(ctx context.Context, repo *repo_model.Repository,
return nil, err
}

baseBranch, err := git_model.GetBranch(ctx, repo.BaseRepo.ID, branch)
baseBranch, err := git_model.GetBranch(ctx, repo.BaseRepo.ID, repo.BaseRepo.DefaultBranch)
if err != nil {
return nil, err
}
Expand All @@ -102,14 +105,42 @@ func GetUpstreamDivergingInfo(ctx context.Context, repo *repo_model.Repository,
return info, nil
}

// TODO: if the fork repo has new commits, this call will fail:
// if the fork repo has new commits, this call will fail because they are not in the base repo
// exit status 128 - fatal: Invalid symmetric difference expression aaaaaaaaaaaa...bbbbbbbbbbbb
// so at the moment, we are not able to handle this case, should be improved in the future
// so at the moment, we first check the update time, then check whether the fork branch has base's head
diff, err := git.GetDivergingCommits(ctx, repo.BaseRepo.RepoPath(), baseBranch.CommitID, forkBranch.CommitID)
if err != nil {
info.BaseIsNewer = baseBranch.UpdatedUnix > forkBranch.UpdatedUnix
info.BaseHasNewCommits = baseBranch.UpdatedUnix > forkBranch.UpdatedUnix
if info.BaseHasNewCommits {
return info, nil
}

// if the base's update time is before the fork, check whether the base's head is in the fork
baseGitRepo, baseGitRepoCloser, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo.BaseRepo)
if err != nil {
return nil, err
}
defer baseGitRepoCloser.Close()

headGitRepo, headGitRepoCloser, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo)
if err != nil {
return nil, err
}
defer headGitRepoCloser.Close()

baseCommitID, err := baseGitRepo.ConvertToGitID(baseBranch.CommitID)
if err != nil {
return nil, err
}
headCommit, err := headGitRepo.GetCommit(forkBranch.CommitID)
if err != nil {
return nil, err
}
hasPreviousCommit, _ := headCommit.HasPreviousCommit(baseCommitID)
info.BaseHasNewCommits = !hasPreviousCommit
return info, nil
}

info.CommitsBehind, info.CommitsAhead = diff.Behind, diff.Ahead
return info, nil
}
6 changes: 3 additions & 3 deletions templates/repo/code/upstream_diverging_info.tmpl
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{{if and .UpstreamDivergingInfo (or .UpstreamDivergingInfo.BaseIsNewer .UpstreamDivergingInfo.CommitsBehind)}}
{{if and .UpstreamDivergingInfo (or .UpstreamDivergingInfo.BaseHasNewCommits .UpstreamDivergingInfo.CommitsBehind)}}
<div class="ui message flex-text-block">
<div class="tw-flex-1">
{{$upstreamLink := printf "%s/src/branch/%s" .Repository.BaseRepo.Link (.BranchName|PathEscapeSegments)}}
{{$upstreamHtml := HTMLFormat `<a href="%s">%s:%s</a>` $upstreamLink .Repository.BaseRepo.FullName .BranchName}}
{{$upstreamLink := printf "%s/src/branch/%s" .Repository.BaseRepo.Link (.Repository.BaseRepo.DefaultBranch|PathEscapeSegments)}}
{{$upstreamHtml := HTMLFormat `<a href="%s">%s:%s</a>` $upstreamLink .Repository.BaseRepo.FullName .Repository.BaseRepo.DefaultBranch}}
{{if .UpstreamDivergingInfo.CommitsBehind}}
{{ctx.Locale.TrN .UpstreamDivergingInfo.CommitsBehind "repo.pulls.upstream_diverging_prompt_behind_1" "repo.pulls.upstream_diverging_prompt_behind_n" .UpstreamDivergingInfo.CommitsBehind $upstreamHtml}}
{{else}}
Expand Down
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.

Loading

0 comments on commit d703488

Please sign in to comment.