Skip to content

Fix sync fork for consistency (#33147) #33192

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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: 39 additions & 10 deletions services/repository/merge_upstream.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,20 @@
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/reqctx"

Check failure on line 17 in services/repository/merge_upstream.go

View workflow job for this annotation

GitHub Actions / test-e2e

no required module provides package code.gitea.io/gitea/modules/reqctx; to add it:

Check failure on line 17 in services/repository/merge_upstream.go

View workflow job for this annotation

GitHub Actions / test-pgsql

no required module provides package code.gitea.io/gitea/modules/reqctx; to add it:

Check failure on line 17 in services/repository/merge_upstream.go

View workflow job for this annotation

GitHub Actions / test-sqlite

no required module provides package code.gitea.io/gitea/modules/reqctx; to add it:

Check failure on line 17 in services/repository/merge_upstream.go

View workflow job for this annotation

GitHub Actions / test-unit

no required module provides package code.gitea.io/gitea/modules/reqctx; to add it:

Check failure on line 17 in services/repository/merge_upstream.go

View workflow job for this annotation

GitHub Actions / test-mssql

no required module provides package code.gitea.io/gitea/modules/reqctx; to add it:

Check failure on line 17 in services/repository/merge_upstream.go

View workflow job for this annotation

GitHub Actions / test-mysql

no required module provides package code.gitea.io/gitea/modules/reqctx; to add it:

Check failure on line 17 in services/repository/merge_upstream.go

View workflow job for this annotation

GitHub Actions / backend

no required module provides package code.gitea.io/gitea/modules/reqctx; to add it:
"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 +35,7 @@
}
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 +67,7 @@
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,7 +77,8 @@
return "merge", nil
}

func GetUpstreamDivergingInfo(ctx context.Context, repo *repo_model.Repository, branch string) (*UpstreamDivergingInfo, error) {
// GetUpstreamDivergingInfo returns the information about the divergence between the fork repository's branch and the base repository's default branch.
func GetUpstreamDivergingInfo(ctx reqctx.RequestContext, repo *repo_model.Repository, branch string) (*UpstreamDivergingInfo, error) {
if !repo.IsFork {
return nil, util.NewInvalidArgumentErrorf("repo is not a fork")
}
Expand All @@ -92,7 +96,7 @@
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 +106,39 @@
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, err := gitrepo.RepositoryFromRequestContextOrOpen(ctx, repo.BaseRepo)

Check failure on line 120 in services/repository/merge_upstream.go

View workflow job for this annotation

GitHub Actions / lint-backend

undefined: gitrepo.RepositoryFromRequestContextOrOpen (typecheck)

Check failure on line 120 in services/repository/merge_upstream.go

View workflow job for this annotation

GitHub Actions / lint-go-gogit

undefined: gitrepo.RepositoryFromRequestContextOrOpen (typecheck)

Check failure on line 120 in services/repository/merge_upstream.go

View workflow job for this annotation

GitHub Actions / lint-go-windows

undefined: gitrepo.RepositoryFromRequestContextOrOpen (typecheck)
if err != nil {
return nil, err
}
headGitRepo, err := gitrepo.RepositoryFromRequestContextOrOpen(ctx, repo)

Check failure on line 124 in services/repository/merge_upstream.go

View workflow job for this annotation

GitHub Actions / lint-backend

undefined: gitrepo.RepositoryFromRequestContextOrOpen (typecheck)

Check failure on line 124 in services/repository/merge_upstream.go

View workflow job for this annotation

GitHub Actions / lint-go-gogit

undefined: gitrepo.RepositoryFromRequestContextOrOpen (typecheck)

Check failure on line 124 in services/repository/merge_upstream.go

View workflow job for this annotation

GitHub Actions / lint-go-windows

undefined: gitrepo.RepositoryFromRequestContextOrOpen (typecheck)
if err != nil {
return nil, err
}

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
Loading