diff --git a/routers/api/v1/repo/compare.go b/routers/api/v1/repo/compare.go index 56e00db943..dae9a19bd8 100644 --- a/routers/api/v1/repo/compare.go +++ b/routers/api/v1/repo/compare.go @@ -61,7 +61,7 @@ func CompareDiff(ctx *context.APIContext) { pathParam := ctx.PathParam("*") baseRepo := ctx.Repo.Repository - ci, err := common.ParseComparePathParams(ctx, pathParam, baseRepo, ctx.Repo.GitRepo) + ci, err := common.ParseComparePathParams(ctx, pathParam, baseRepo, ctx.Repo.GitRepo, ctx.Doer) if err != nil { switch { case user_model.IsErrUserNotExist(err): diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index b4a48a3c8b..0893d53713 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -403,7 +403,7 @@ func CreatePullRequest(ctx *context.APIContext) { ) // Get repo/branch information - ci, err := common.ParseComparePathParams(ctx, form.Base+"..."+form.Head, repo, ctx.Repo.GitRepo) + ci, err := common.ParseComparePathParams(ctx, form.Base+"..."+form.Head, repo, ctx.Repo.GitRepo, ctx.Doer) if err != nil { switch { case user_model.IsErrUserNotExist(err): diff --git a/routers/common/compare.go b/routers/common/compare.go index 0c9d7295fb..f7616166bf 100644 --- a/routers/common/compare.go +++ b/routers/common/compare.go @@ -101,6 +101,8 @@ type CompareInfo struct { HeadUser *user_model.User HeadRepo *repo_model.Repository HeadGitRepo *git.Repository + RootRepo *repo_model.Repository + OwnForkRepo *repo_model.Repository CompareInfo *git.CompareInfo close func() IsBaseCommit bool @@ -190,6 +192,20 @@ func findHeadRepoFromRootBase(ctx context.Context, baseRepo *repo_model.Reposito return nil, nil } +func getRootRepo(ctx context.Context, repo *repo_model.Repository) (*repo_model.Repository, error) { + curRepo := repo + for curRepo.IsFork { + if err := curRepo.GetBaseRepo(ctx); err != nil { + return nil, err + } + if curRepo.BaseRepo == nil { + break + } + curRepo = curRepo.BaseRepo + } + return curRepo, nil +} + // ParseComparePathParams Get compare information // A full compare url is of the form: // @@ -215,7 +231,7 @@ func findHeadRepoFromRootBase(ctx context.Context, baseRepo *repo_model.Reposito // format: ...[:] // base<-head: master...head:feature // same repo: master...feature -func ParseComparePathParams(ctx context.Context, pathParam string, baseRepo *repo_model.Repository, baseGitRepo *git.Repository) (*CompareInfo, error) { +func ParseComparePathParams(ctx context.Context, pathParam string, baseRepo *repo_model.Repository, baseGitRepo *git.Repository, doer *user_model.User) (*CompareInfo, error) { ci := &CompareInfo{} var err error @@ -273,13 +289,37 @@ func ParseComparePathParams(ctx context.Context, pathParam string, baseRepo *rep } } + // find root repo + if !baseRepo.IsFork { + ci.RootRepo = baseRepo + } else { + if !ci.HeadRepo.IsFork { + ci.RootRepo = ci.HeadRepo + } else { + ci.RootRepo, err = getRootRepo(ctx, baseRepo) + if err != nil { + return nil, err + } + } + } + + // find ownfork repo + if doer != nil && ci.HeadRepo.OwnerID != doer.ID && baseRepo.OwnerID != doer.ID { + ci.OwnForkRepo, err = findHeadRepo(ctx, baseRepo, doer.ID) + if err != nil { + return nil, err + } + } + ci.BaseFullRef, ci.IsBaseCommit, err = detectFullRef(ctx, baseRepo.ID, baseGitRepo, ci.BaseOriRef) if err != nil { + ci.Close() return nil, err } ci.HeadFullRef, ci.IsHeadCommit, err = detectFullRef(ctx, ci.HeadRepo.ID, ci.HeadGitRepo, ci.HeadOriRef) if err != nil { + ci.Close() return nil, err } return ci, nil diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index a6ce48506b..f4ca1ceb38 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -14,6 +14,7 @@ import ( "net/http" "net/url" "path/filepath" + "slices" "strings" "code.gitea.io/gitea/models/db" @@ -193,7 +194,7 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo { pathParam := ctx.PathParam("*") baseRepo := ctx.Repo.Repository - ci, err := common.ParseComparePathParams(ctx, pathParam, baseRepo, ctx.Repo.GitRepo) + ci, err := common.ParseComparePathParams(ctx, pathParam, baseRepo, ctx.Repo.GitRepo, ctx.Doer) if err != nil { switch { case user_model.IsErrUserNotExist(err): @@ -207,7 +208,6 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo { } return nil } - defer ci.Close() // remove the check when we support compare with carets if ci.CaretTimes > 0 { @@ -245,8 +245,6 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo { ctx.Data["CanWriteToHeadRepo"] = permHead.CanWrite(unit.TypeCode) } - // TODO: prepareRepos, branches and tags for dropdowns - ctx.Data["PageIsComparePull"] = ci.IsPull() && ctx.Repo.CanReadIssuesOrPulls(true) ctx.Data["BaseName"] = baseRepo.OwnerName ctx.Data["BaseBranch"] = ci.BaseOriRef @@ -258,7 +256,6 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo { ctx.Data["BaseIsBranch"] = ci.BaseFullRef.IsBranch() ctx.Data["BaseIsTag"] = ci.BaseFullRef.IsTag() ctx.Data["IsPull"] = true - // ctx.Data["OwnForkRepo"] = ownForkRepo FIXME: This is not used ctx.Data["HeadRepo"] = ci.HeadRepo ctx.Data["BaseCompareRepo"] = ctx.Repo.Repository @@ -399,14 +396,8 @@ func PrepareCompareDiff( return false } -func getBranchesAndTagsForRepo(ctx gocontext.Context, repo *repo_model.Repository) (branches, tags []string, err error) { - gitRepo, err := gitrepo.OpenRepository(ctx, repo) - if err != nil { - return nil, nil, err - } - defer gitRepo.Close() - - branches, err = git_model.FindBranchNames(ctx, git_model.FindBranchOptions{ +func getBranchesAndTagsForRepo(ctx gocontext.Context, repo *repo_model.Repository) ([]string, []string, error) { + branches, err := git_model.FindBranchNames(ctx, git_model.FindBranchOptions{ RepoID: repo.ID, ListOptions: db.ListOptionsAll, IsDeletedBranch: optional.Some(false), @@ -414,19 +405,82 @@ func getBranchesAndTagsForRepo(ctx gocontext.Context, repo *repo_model.Repositor if err != nil { return nil, nil, err } - tags, err = gitRepo.GetTags(0, 0) + // always put default branch on the top if it exists + if slices.Contains(branches, repo.DefaultBranch) { + branches = util.SliceRemoveAll(branches, repo.DefaultBranch) + branches = append([]string{repo.DefaultBranch}, branches...) + } + + tags, err := repo_model.GetTagNamesByRepoID(ctx, repo.ID) if err != nil { return nil, nil, err } + return branches, tags, nil } +func prepareCompareRepoBranchesTagsDropdowns(ctx *context.Context, ci *common.CompareInfo) { + baseRepo := ctx.Repo.Repository + // For compare repo branches + baseBranches, baseTags, err := getBranchesAndTagsForRepo(ctx, baseRepo) + if err != nil { + ctx.ServerError("getBranchesAndTagsForRepo", err) + return + } + + ctx.Data["Branches"] = baseBranches + ctx.Data["Tags"] = baseTags + + if ci.IsSameRepo() { + ctx.Data["HeadBranches"] = baseBranches + ctx.Data["HeadTags"] = baseTags + } else { + headBranches, headTags, err := getBranchesAndTagsForRepo(ctx, ci.HeadRepo) + if err != nil { + ctx.ServerError("getBranchesAndTagsForRepo", err) + return + } + ctx.Data["HeadBranches"] = headBranches + ctx.Data["HeadTags"] = headTags + } + + if ci.RootRepo != nil && + ci.RootRepo.ID != ci.HeadRepo.ID && + ci.RootRepo.ID != baseRepo.ID { + canRead := access_model.CheckRepoUnitUser(ctx, ci.RootRepo, ctx.Doer, unit.TypeCode) + if canRead { + ctx.Data["RootRepo"] = ci.RootRepo + branches, tags, err := getBranchesAndTagsForRepo(ctx, ci.RootRepo) + if err != nil { + ctx.ServerError("GetBranchesForRepo", err) + return + } + ctx.Data["RootRepoBranches"] = branches + ctx.Data["RootRepoTags"] = tags + } + } + + if ci.OwnForkRepo != nil && + ci.OwnForkRepo.ID != ci.HeadRepo.ID && + ci.OwnForkRepo.ID != baseRepo.ID && + (ci.RootRepo == nil || ci.OwnForkRepo.ID != ci.RootRepo.ID) { + ctx.Data["OwnForkRepo"] = ci.OwnForkRepo + branches, tags, err := getBranchesAndTagsForRepo(ctx, ci.OwnForkRepo) + if err != nil { + ctx.ServerError("GetBranchesForRepo", err) + return + } + ctx.Data["OwnForkRepoBranches"] = branches + ctx.Data["OwnForkRepoTags"] = tags + } +} + // CompareDiff show different from one commit to another commit func CompareDiff(ctx *context.Context) { ci := ParseCompareInfo(ctx) defer func() { - if ci != nil && ci.HeadGitRepo != nil { - ci.HeadGitRepo.Close() + if ci != nil { + ci.Close() } }() if ctx.Written() { @@ -448,43 +502,17 @@ func CompareDiff(ctx *context.Context) { return } - baseTags, err := repo_model.GetTagNamesByRepoID(ctx, ctx.Repo.Repository.ID) - if err != nil { - ctx.ServerError("GetTagNamesByRepoID", err) - return - } - ctx.Data["Tags"] = baseTags - fileOnly := ctx.FormBool("file-only") if fileOnly { ctx.HTML(http.StatusOK, tplDiffBox) return } - headBranches, err := git_model.FindBranchNames(ctx, git_model.FindBranchOptions{ - RepoID: ci.HeadRepo.ID, - ListOptions: db.ListOptionsAll, - IsDeletedBranch: optional.Some(false), - }) - if err != nil { - ctx.ServerError("GetBranches", err) - return - } - ctx.Data["HeadBranches"] = headBranches - - // For compare repo branches - PrepareBranchList(ctx) + prepareCompareRepoBranchesTagsDropdowns(ctx, ci) if ctx.Written() { return } - headTags, err := repo_model.GetTagNamesByRepoID(ctx, ci.HeadRepo.ID) - if err != nil { - ctx.ServerError("GetTagNamesByRepoID", err) - return - } - ctx.Data["HeadTags"] = headTags - if ctx.Data["PageIsComparePull"] == true { pr, err := issues_model.GetUnmergedPullRequest(ctx, ci.HeadRepo.ID, ctx.Repo.Repository.ID, ci.HeadOriRef, ci.BaseOriRef, issues_model.PullRequestFlowGithub) if err != nil { diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index fe0469e95e..a3907e738d 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -1266,8 +1266,8 @@ func CompareAndPullRequestPost(ctx *context.Context) { ci := ParseCompareInfo(ctx) defer func() { - if ci != nil && ci.HeadGitRepo != nil { - ci.HeadGitRepo.Close() + if ci != nil { + ci.Close() } }() if ctx.Written() {