mirror of https://github.com/go-gitea/gitea
Support "merge upstream branch" (Sync fork) (#32741)
Add basic "sync fork" support (GitHub-like) <details> ![image](https://github.com/user-attachments/assets/e71473f4-4518-48c7-b9e2-fedfcd564fc3) </details>pull/32737/head^2
parent
5a75160c92
commit
513da407f4
@ -0,0 +1,115 @@ |
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package repository |
||||
|
||||
import ( |
||||
"context" |
||||
"fmt" |
||||
|
||||
git_model "code.gitea.io/gitea/models/git" |
||||
issue_model "code.gitea.io/gitea/models/issues" |
||||
repo_model "code.gitea.io/gitea/models/repo" |
||||
user_model "code.gitea.io/gitea/models/user" |
||||
"code.gitea.io/gitea/modules/git" |
||||
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 |
||||
} |
||||
|
||||
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 |
||||
} |
||||
if err = repo.GetBaseRepo(ctx); err != nil { |
||||
return "", err |
||||
} |
||||
err = git.Push(ctx, repo.BaseRepo.RepoPath(), git.PushOptions{ |
||||
Remote: repo.RepoPath(), |
||||
Branch: fmt.Sprintf("%s:%s", branch, branch), |
||||
Env: repo_module.PushingEnvironment(doer, repo), |
||||
}) |
||||
if err == nil { |
||||
return "fast-forward", nil |
||||
} |
||||
if !git.IsErrPushOutOfDate(err) && !git.IsErrPushRejected(err) { |
||||
return "", err |
||||
} |
||||
|
||||
// TODO: FakePR: it is somewhat hacky, but it is the only way to "merge" at the moment
|
||||
// ideally in the future the "merge" functions should be refactored to decouple from the PullRequest
|
||||
fakeIssue := &issue_model.Issue{ |
||||
ID: -1, |
||||
RepoID: repo.ID, |
||||
Repo: repo, |
||||
Index: -1, |
||||
PosterID: doer.ID, |
||||
Poster: doer, |
||||
IsPull: true, |
||||
} |
||||
fakePR := &issue_model.PullRequest{ |
||||
ID: -1, |
||||
Status: issue_model.PullRequestStatusMergeable, |
||||
IssueID: -1, |
||||
Issue: fakeIssue, |
||||
Index: -1, |
||||
HeadRepoID: repo.ID, |
||||
HeadRepo: repo, |
||||
BaseRepoID: repo.BaseRepo.ID, |
||||
BaseRepo: repo.BaseRepo, |
||||
HeadBranch: branch, // maybe HeadCommitID is not needed
|
||||
BaseBranch: branch, |
||||
} |
||||
fakeIssue.PullRequest = fakePR |
||||
err = pull.Update(ctx, fakePR, doer, "merge upstream", false) |
||||
if err != nil { |
||||
return "", err |
||||
} |
||||
return "merge", nil |
||||
} |
||||
|
||||
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") |
||||
} |
||||
|
||||
if repo.IsArchived { |
||||
return nil, util.NewInvalidArgumentErrorf("repo is archived") |
||||
} |
||||
|
||||
if err := repo.GetBaseRepo(ctx); err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
forkBranch, err := git_model.GetBranch(ctx, repo.ID, branch) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
baseBranch, err := git_model.GetBranch(ctx, repo.BaseRepo.ID, branch) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
info := &UpstreamDivergingInfo{} |
||||
if forkBranch.CommitID == baseBranch.CommitID { |
||||
return info, nil |
||||
} |
||||
|
||||
// TODO: if the fork repo has new commits, this call will fail:
|
||||
// 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
|
||||
diff, err := git.GetDivergingCommits(ctx, repo.BaseRepo.RepoPath(), baseBranch.CommitID, forkBranch.CommitID) |
||||
if err != nil { |
||||
info.BaseIsNewer = baseBranch.UpdatedUnix > forkBranch.UpdatedUnix |
||||
return info, nil |
||||
} |
||||
info.CommitsBehind, info.CommitsAhead = diff.Behind, diff.Ahead |
||||
return info, nil |
||||
} |
@ -0,0 +1,18 @@ |
||||
{{if and .UpstreamDivergingInfo (or .UpstreamDivergingInfo.BaseIsNewer .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}} |
||||
{{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}} |
||||
{{ctx.Locale.Tr "repo.pulls.upstream_diverging_prompt_base_newer" $upstreamHtml}} |
||||
{{end}} |
||||
</div> |
||||
{{if .CanWriteCode}} |
||||
<button class="ui compact green button tw-m-0 link-action" data-url="{{.Repository.Link}}/branches/merge-upstream?branch={{.BranchName}}"> |
||||
{{ctx.Locale.Tr "repo.pulls.upstream_diverging_merge"}} |
||||
</button> |
||||
{{end}} |
||||
</div> |
||||
{{end}} |
Loading…
Reference in new issue