mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-03 20:36:07 +01:00 
			
		
		
		
	Support "merge upstream branch" (Sync fork) (#32741)
Add basic "sync fork" support (GitHub-like) <details>  </details>
This commit is contained in:
		@@ -223,7 +223,7 @@ func Push(ctx context.Context, repoPath string, opts PushOptions) error {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if strings.Contains(stderr, "non-fast-forward") {
 | 
							if strings.Contains(stderr, "non-fast-forward") {
 | 
				
			||||||
			return &ErrPushOutOfDate{StdOut: stdout, StdErr: stderr, Err: err}
 | 
								return &ErrPushOutOfDate{StdOut: stdout, StdErr: stderr, Err: err}
 | 
				
			||||||
		} else if strings.Contains(stderr, "! [remote rejected]") {
 | 
							} else if strings.Contains(stderr, "! [remote rejected]") || strings.Contains(stderr, "! [rejected]") {
 | 
				
			||||||
			err := &ErrPushRejected{StdOut: stdout, StdErr: stderr, Err: err}
 | 
								err := &ErrPushRejected{StdOut: stdout, StdErr: stderr, Err: err}
 | 
				
			||||||
			err.GenerateMessage()
 | 
								err.GenerateMessage()
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1946,6 +1946,10 @@ pulls.delete.title = Delete this pull request?
 | 
				
			|||||||
pulls.delete.text = Do you really want to delete this pull request? (This will permanently remove all content. Consider closing it instead, if you intend to keep it archived)
 | 
					pulls.delete.text = Do you really want to delete this pull request? (This will permanently remove all content. Consider closing it instead, if you intend to keep it archived)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pulls.recently_pushed_new_branches = You pushed on branch <strong>%[1]s</strong> %[2]s
 | 
					pulls.recently_pushed_new_branches = You pushed on branch <strong>%[1]s</strong> %[2]s
 | 
				
			||||||
 | 
					pulls.upstream_diverging_prompt_behind_1 = This branch is %d commit behind %s
 | 
				
			||||||
 | 
					pulls.upstream_diverging_prompt_behind_n = This branch is %d commits behind %s
 | 
				
			||||||
 | 
					pulls.upstream_diverging_prompt_base_newer = The base branch %s has new changes
 | 
				
			||||||
 | 
					pulls.upstream_diverging_merge = Sync fork
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pull.deleted_branch = (deleted):%s
 | 
					pull.deleted_branch = (deleted):%s
 | 
				
			||||||
pull.agit_documentation = Review documentation about AGit
 | 
					pull.agit_documentation = Review documentation about AGit
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -259,3 +259,20 @@ func CreateBranch(ctx *context.Context) {
 | 
				
			|||||||
	ctx.Flash.Success(ctx.Tr("repo.branch.create_success", form.NewBranchName))
 | 
						ctx.Flash.Success(ctx.Tr("repo.branch.create_success", form.NewBranchName))
 | 
				
			||||||
	ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + util.PathEscapeSegments(form.NewBranchName) + "/" + util.PathEscapeSegments(form.CurrentPath))
 | 
						ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + util.PathEscapeSegments(form.NewBranchName) + "/" + util.PathEscapeSegments(form.CurrentPath))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func MergeUpstream(ctx *context.Context) {
 | 
				
			||||||
 | 
						branchName := ctx.FormString("branch")
 | 
				
			||||||
 | 
						_, err := repo_service.MergeUpstream(ctx, ctx.Doer, ctx.Repo.Repository, branchName)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if errors.Is(err, util.ErrNotExist) {
 | 
				
			||||||
 | 
								ctx.JSONError(ctx.Tr("error.not_found"))
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							} else if models.IsErrMergeConflicts(err) {
 | 
				
			||||||
 | 
								ctx.JSONError(ctx.Tr("repo.pulls.merge_conflict"))
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							ctx.ServerError("MergeUpstream", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.JSONRedirect("")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,7 +31,7 @@ import (
 | 
				
			|||||||
	"github.com/nektos/act/pkg/model"
 | 
						"github.com/nektos/act/pkg/model"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func renderFile(ctx *context.Context, entry *git.TreeEntry) {
 | 
					func prepareToRenderFile(ctx *context.Context, entry *git.TreeEntry) {
 | 
				
			||||||
	ctx.Data["IsViewFile"] = true
 | 
						ctx.Data["IsViewFile"] = true
 | 
				
			||||||
	ctx.Data["HideRepoInfo"] = true
 | 
						ctx.Data["HideRepoInfo"] = true
 | 
				
			||||||
	blob := entry.Blob()
 | 
						blob := entry.Blob()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,7 @@
 | 
				
			|||||||
package repo
 | 
					package repo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"html/template"
 | 
						"html/template"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
@@ -86,7 +87,8 @@ func prepareOpenWithEditorApps(ctx *context.Context) {
 | 
				
			|||||||
	ctx.Data["OpenWithEditorApps"] = tmplApps
 | 
						ctx.Data["OpenWithEditorApps"] = tmplApps
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func prepareHomeSidebarCitationFile(ctx *context.Context, entry *git.TreeEntry) {
 | 
					func prepareHomeSidebarCitationFile(entry *git.TreeEntry) func(ctx *context.Context) {
 | 
				
			||||||
 | 
						return func(ctx *context.Context) {
 | 
				
			||||||
		if entry.Name() != "" {
 | 
							if entry.Name() != "" {
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -113,6 +115,7 @@ func prepareHomeSidebarCitationFile(ctx *context.Context, entry *git.TreeEntry)
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func prepareHomeSidebarLicenses(ctx *context.Context) {
 | 
					func prepareHomeSidebarLicenses(ctx *context.Context) {
 | 
				
			||||||
	repoLicenses, err := repo_model.GetRepoLicenses(ctx, ctx.Repo.Repository)
 | 
						repoLicenses, err := repo_model.GetRepoLicenses(ctx, ctx.Repo.Repository)
 | 
				
			||||||
@@ -174,12 +177,53 @@ func prepareHomeSidebarLatestRelease(ctx *context.Context) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func renderHomeCode(ctx *context.Context) {
 | 
					func prepareUpstreamDivergingInfo(ctx *context.Context) {
 | 
				
			||||||
	ctx.Data["PageIsViewCode"] = true
 | 
						if !ctx.Repo.Repository.IsFork || !ctx.Repo.IsViewBranch || ctx.Repo.TreePath != "" {
 | 
				
			||||||
	ctx.Data["RepositoryUploadEnabled"] = setting.Repository.Upload.Enabled
 | 
							return
 | 
				
			||||||
	prepareOpenWithEditorApps(ctx)
 | 
						}
 | 
				
			||||||
 | 
						upstreamDivergingInfo, err := repo_service.GetUpstreamDivergingInfo(ctx, ctx.Repo.Repository, ctx.Repo.BranchName)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if !errors.Is(err, util.ErrNotExist) && !errors.Is(err, util.ErrInvalidArgument) {
 | 
				
			||||||
 | 
								log.Error("GetUpstreamDivergingInfo: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.Data["UpstreamDivergingInfo"] = upstreamDivergingInfo
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ctx.Repo.Commit == nil || ctx.Repo.Repository.IsEmpty || ctx.Repo.Repository.IsBroken() {
 | 
					func prepareRecentlyPushedNewBranches(ctx *context.Context) {
 | 
				
			||||||
 | 
						if ctx.Doer != nil {
 | 
				
			||||||
 | 
							if err := ctx.Repo.Repository.GetBaseRepo(ctx); err != nil {
 | 
				
			||||||
 | 
								ctx.ServerError("GetBaseRepo", err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							opts := &git_model.FindRecentlyPushedNewBranchesOptions{
 | 
				
			||||||
 | 
								Repo:     ctx.Repo.Repository,
 | 
				
			||||||
 | 
								BaseRepo: ctx.Repo.Repository,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if ctx.Repo.Repository.IsFork {
 | 
				
			||||||
 | 
								opts.BaseRepo = ctx.Repo.Repository.BaseRepo
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							baseRepoPerm, err := access_model.GetUserRepoPermission(ctx, opts.BaseRepo, ctx.Doer)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								ctx.ServerError("GetUserRepoPermission", err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if !opts.Repo.IsMirror && !opts.BaseRepo.IsMirror &&
 | 
				
			||||||
 | 
								opts.BaseRepo.UnitEnabled(ctx, unit_model.TypePullRequests) &&
 | 
				
			||||||
 | 
								baseRepoPerm.CanRead(unit_model.TypePullRequests) {
 | 
				
			||||||
 | 
								ctx.Data["RecentlyPushedNewBranches"], err = git_model.FindRecentlyPushedNewBranches(ctx, ctx.Doer, opts)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									log.Error("FindRecentlyPushedNewBranches failed: %v", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func handleRepoEmptyOrBroken(ctx *context.Context) {
 | 
				
			||||||
	showEmpty := true
 | 
						showEmpty := true
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	if ctx.Repo.GitRepo != nil {
 | 
						if ctx.Repo.GitRepo != nil {
 | 
				
			||||||
@@ -218,6 +262,46 @@ func renderHomeCode(ctx *context.Context) {
 | 
				
			|||||||
		link += "?" + ctx.Req.URL.RawQuery
 | 
							link += "?" + ctx.Req.URL.RawQuery
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ctx.Redirect(link)
 | 
						ctx.Redirect(link)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func prepareToRenderDirOrFile(entry *git.TreeEntry) func(ctx *context.Context) {
 | 
				
			||||||
 | 
						return func(ctx *context.Context) {
 | 
				
			||||||
 | 
							if entry.IsDir() {
 | 
				
			||||||
 | 
								prepareToRenderDirectory(ctx)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								prepareToRenderFile(ctx, entry)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func handleRepoHomeFeed(ctx *context.Context) bool {
 | 
				
			||||||
 | 
						if setting.Other.EnableFeed {
 | 
				
			||||||
 | 
							isFeed, _, showFeedType := feed.GetFeedType(ctx.PathParam(":reponame"), ctx.Req)
 | 
				
			||||||
 | 
							if isFeed {
 | 
				
			||||||
 | 
								switch {
 | 
				
			||||||
 | 
								case ctx.Link == fmt.Sprintf("%s.%s", ctx.Repo.RepoLink, showFeedType):
 | 
				
			||||||
 | 
									feed.ShowRepoFeed(ctx, ctx.Repo.Repository, showFeedType)
 | 
				
			||||||
 | 
								case ctx.Repo.TreePath == "":
 | 
				
			||||||
 | 
									feed.ShowBranchFeed(ctx, ctx.Repo.Repository, showFeedType)
 | 
				
			||||||
 | 
								case ctx.Repo.TreePath != "":
 | 
				
			||||||
 | 
									feed.ShowFileFeed(ctx, ctx.Repo.Repository, showFeedType)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Home render repository home page
 | 
				
			||||||
 | 
					func Home(ctx *context.Context) {
 | 
				
			||||||
 | 
						if handleRepoHomeFeed(ctx) {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Check whether the repo is viewable: not in migration, and the code unit should be enabled
 | 
				
			||||||
 | 
						// Ideally the "feed" logic should be after this, but old code did so, so keep it as-is.
 | 
				
			||||||
 | 
						checkHomeCodeViewable(ctx)
 | 
				
			||||||
 | 
						if ctx.Written() {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -226,61 +310,23 @@ func renderHomeCode(ctx *context.Context) {
 | 
				
			|||||||
		title += ": " + ctx.Repo.Repository.Description
 | 
							title += ": " + ctx.Repo.Repository.Description
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ctx.Data["Title"] = title
 | 
						ctx.Data["Title"] = title
 | 
				
			||||||
 | 
						ctx.Data["PageIsViewCode"] = true
 | 
				
			||||||
 | 
						ctx.Data["RepositoryUploadEnabled"] = setting.Repository.Upload.Enabled // show New File / Upload File buttons
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get Topics of this repo
 | 
						if ctx.Repo.Commit == nil || ctx.Repo.Repository.IsEmpty || ctx.Repo.Repository.IsBroken() {
 | 
				
			||||||
	prepareHomeSidebarRepoTopics(ctx)
 | 
							// empty or broken repositories need to be handled differently
 | 
				
			||||||
	if ctx.Written() {
 | 
							handleRepoEmptyOrBroken(ctx)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get current entry user currently looking at.
 | 
						// get the current git entry which doer user is currently looking at.
 | 
				
			||||||
	entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath)
 | 
						entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		HandleGitError(ctx, "Repo.Commit.GetTreeEntryByPath", err)
 | 
							HandleGitError(ctx, "Repo.Commit.GetTreeEntryByPath", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	checkOutdatedBranch(ctx)
 | 
						// prepare the tree path
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if entry.IsDir() {
 | 
					 | 
				
			||||||
		prepareToRenderDirectory(ctx)
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		renderFile(ctx, entry)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if ctx.Written() {
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if ctx.Doer != nil {
 | 
					 | 
				
			||||||
		if err := ctx.Repo.Repository.GetBaseRepo(ctx); err != nil {
 | 
					 | 
				
			||||||
			ctx.ServerError("GetBaseRepo", err)
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		opts := &git_model.FindRecentlyPushedNewBranchesOptions{
 | 
					 | 
				
			||||||
			Repo:     ctx.Repo.Repository,
 | 
					 | 
				
			||||||
			BaseRepo: ctx.Repo.Repository,
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if ctx.Repo.Repository.IsFork {
 | 
					 | 
				
			||||||
			opts.BaseRepo = ctx.Repo.Repository.BaseRepo
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		baseRepoPerm, err := access_model.GetUserRepoPermission(ctx, opts.BaseRepo, ctx.Doer)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			ctx.ServerError("GetUserRepoPermission", err)
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if !opts.Repo.IsMirror && !opts.BaseRepo.IsMirror &&
 | 
					 | 
				
			||||||
			opts.BaseRepo.UnitEnabled(ctx, unit_model.TypePullRequests) &&
 | 
					 | 
				
			||||||
			baseRepoPerm.CanRead(unit_model.TypePullRequests) {
 | 
					 | 
				
			||||||
			ctx.Data["RecentlyPushedNewBranches"], err = git_model.FindRecentlyPushedNewBranches(ctx, ctx.Doer, opts)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				log.Error("FindRecentlyPushedNewBranches failed: %v", err)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var treeNames, paths []string
 | 
						var treeNames, paths []string
 | 
				
			||||||
	branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
 | 
						branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
 | 
				
			||||||
	treeLink := branchLink
 | 
						treeLink := branchLink
 | 
				
			||||||
@@ -295,57 +341,38 @@ func renderHomeCode(ctx *context.Context) {
 | 
				
			|||||||
			ctx.Data["ParentPath"] = "/" + paths[len(paths)-2]
 | 
								ctx.Data["ParentPath"] = "/" + paths[len(paths)-2]
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	isTreePathRoot := ctx.Repo.TreePath == ""
 | 
					 | 
				
			||||||
	if isTreePathRoot {
 | 
					 | 
				
			||||||
		prepareHomeSidebarLicenses(ctx)
 | 
					 | 
				
			||||||
		if ctx.Written() {
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		prepareHomeSidebarCitationFile(ctx, entry)
 | 
					 | 
				
			||||||
		if ctx.Written() {
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		prepareHomeSidebarLanguageStats(ctx)
 | 
					 | 
				
			||||||
		if ctx.Written() {
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		prepareHomeSidebarLatestRelease(ctx)
 | 
					 | 
				
			||||||
		if ctx.Written() {
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ctx.Data["Paths"] = paths
 | 
						ctx.Data["Paths"] = paths
 | 
				
			||||||
	ctx.Data["TreeLink"] = treeLink
 | 
						ctx.Data["TreeLink"] = treeLink
 | 
				
			||||||
	ctx.Data["TreeNames"] = treeNames
 | 
						ctx.Data["TreeNames"] = treeNames
 | 
				
			||||||
	ctx.Data["BranchLink"] = branchLink
 | 
						ctx.Data["BranchLink"] = branchLink
 | 
				
			||||||
	ctx.HTML(http.StatusOK, tplRepoHome)
 | 
					
 | 
				
			||||||
 | 
						// some UI components are only shown when the tree path is root
 | 
				
			||||||
 | 
						isTreePathRoot := ctx.Repo.TreePath == ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						prepareFuncs := []func(*context.Context){
 | 
				
			||||||
 | 
							prepareOpenWithEditorApps,
 | 
				
			||||||
 | 
							prepareHomeSidebarRepoTopics,
 | 
				
			||||||
 | 
							checkOutdatedBranch,
 | 
				
			||||||
 | 
							prepareToRenderDirOrFile(entry),
 | 
				
			||||||
 | 
							prepareRecentlyPushedNewBranches,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Home render repository home page
 | 
						if isTreePathRoot {
 | 
				
			||||||
func Home(ctx *context.Context) {
 | 
							prepareFuncs = append(prepareFuncs,
 | 
				
			||||||
	if setting.Other.EnableFeed {
 | 
								prepareUpstreamDivergingInfo,
 | 
				
			||||||
		isFeed, _, showFeedType := feed.GetFeedType(ctx.PathParam(":reponame"), ctx.Req)
 | 
								prepareHomeSidebarLicenses,
 | 
				
			||||||
		if isFeed {
 | 
								prepareHomeSidebarCitationFile(entry),
 | 
				
			||||||
			switch {
 | 
								prepareHomeSidebarLanguageStats,
 | 
				
			||||||
			case ctx.Link == fmt.Sprintf("%s.%s", ctx.Repo.RepoLink, showFeedType):
 | 
								prepareHomeSidebarLatestRelease,
 | 
				
			||||||
				feed.ShowRepoFeed(ctx, ctx.Repo.Repository, showFeedType)
 | 
							)
 | 
				
			||||||
			case ctx.Repo.TreePath == "":
 | 
					 | 
				
			||||||
				feed.ShowBranchFeed(ctx, ctx.Repo.Repository, showFeedType)
 | 
					 | 
				
			||||||
			case ctx.Repo.TreePath != "":
 | 
					 | 
				
			||||||
				feed.ShowFileFeed(ctx, ctx.Repo.Repository, showFeedType)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	checkHomeCodeViewable(ctx)
 | 
						for _, prepare := range prepareFuncs {
 | 
				
			||||||
 | 
							prepare(ctx)
 | 
				
			||||||
		if ctx.Written() {
 | 
							if ctx.Written() {
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	renderHomeCode(ctx)
 | 
					
 | 
				
			||||||
 | 
						ctx.HTML(http.StatusOK, tplRepoHome)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1320,6 +1320,7 @@ func registerRoutes(m *web.Router) {
 | 
				
			|||||||
			m.Post("/delete", repo.DeleteBranchPost)
 | 
								m.Post("/delete", repo.DeleteBranchPost)
 | 
				
			||||||
			m.Post("/restore", repo.RestoreBranchPost)
 | 
								m.Post("/restore", repo.RestoreBranchPost)
 | 
				
			||||||
			m.Post("/rename", web.Bind(forms.RenameBranchForm{}), repo_setting.RenameBranchPost)
 | 
								m.Post("/rename", web.Bind(forms.RenameBranchForm{}), repo_setting.RenameBranchPost)
 | 
				
			||||||
 | 
								m.Post("/merge-upstream", repo.MergeUpstream)
 | 
				
			||||||
		}, context.RepoMustNotBeArchived(), reqRepoCodeWriter, repo.MustBeNotEmpty)
 | 
							}, context.RepoMustNotBeArchived(), reqRepoCodeWriter, repo.MustBeNotEmpty)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		m.Combo("/fork").Get(repo.Fork).Post(web.Bind(forms.CreateRepoForm{}), repo.ForkPost)
 | 
							m.Combo("/fork").Get(repo.Fork).Post(web.Bind(forms.CreateRepoForm{}), repo.ForkPost)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -65,7 +65,9 @@ func Update(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.
 | 
				
			|||||||
		return fmt.Errorf("unable to load HeadRepo for PR[%d] during update-by-merge: %w", pr.ID, err)
 | 
							return fmt.Errorf("unable to load HeadRepo for PR[%d] during update-by-merge: %w", pr.ID, err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// use merge functions but switch repos and branches
 | 
						// 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
 | 
				
			||||||
 | 
						// now use a fake reverse PR to switch head&base repos/branches
 | 
				
			||||||
	reversePR := &issues_model.PullRequest{
 | 
						reversePR := &issues_model.PullRequest{
 | 
				
			||||||
		ID: pr.ID,
 | 
							ID: pr.ID,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										115
									
								
								services/repository/merge_upstream.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								services/repository/merge_upstream.go
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										18
									
								
								templates/repo/code/upstream_diverging_info.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								templates/repo/code/upstream_diverging_info.tmpl
									
									
									
									
									
										Normal file
									
								
							@@ -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}}
 | 
				
			||||||
@@ -136,6 +136,9 @@
 | 
				
			|||||||
				{{else if .IsBlame}}
 | 
									{{else if .IsBlame}}
 | 
				
			||||||
					{{template "repo/blame" .}}
 | 
										{{template "repo/blame" .}}
 | 
				
			||||||
				{{else}}{{/* IsViewDirectory */}}
 | 
									{{else}}{{/* IsViewDirectory */}}
 | 
				
			||||||
 | 
										{{if $isTreePathRoot}}
 | 
				
			||||||
 | 
											{{template "repo/code/upstream_diverging_info" .}}
 | 
				
			||||||
 | 
										{{end}}
 | 
				
			||||||
					{{template "repo/view_list" .}}
 | 
										{{template "repo/view_list" .}}
 | 
				
			||||||
				{{end}}
 | 
									{{end}}
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user