gitea_hat/routers/hat/repo/repo.go

552 lines
15 KiB
Go

package repo
import (
"fmt"
"net/http"
"strings"
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
system_model "code.gitea.io/gitea/models/system"
unit_model "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
gitea_git "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/services/gitdiff"
hat_models "code.gitlink.org.cn/Gitlink/gitea_hat.git/models"
)
func PrepareComapreDiff(
ctx *context.APIContext,
headUser *user_model.User,
headRepo *repo_model.Repository,
headGitRepo *gitea_git.Repository,
compareInfo *gitea_git.CompareInfo,
baseBranch, headBranch string) bool {
var (
err error
)
ctx.Data["CommitRepoLink"] = headRepo.Link()
headCommitID := headBranch
if ctx.Data["HeadIsCommit"] == false {
if ctx.Data["HeadIsTag"] == true {
headCommitID, err = headGitRepo.GetTagCommitID(headBranch)
} else {
headCommitID, err = headGitRepo.GetBranchCommitID(headBranch)
}
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetRefCommitID", err)
return false
}
}
ctx.Data["AfterCommitID"] = headCommitID
if headCommitID == compareInfo.MergeBase {
ctx.Data["IsNothingToCompare"] = true
return true
}
repoPath := repo_model.RepoPath(headUser.Name, headRepo.Name)
gitRepo, _ := gitea_git.OpenRepository(ctx, repoPath)
defer gitRepo.Close()
diff, err := gitdiff.GetDiff(gitRepo, &gitdiff.DiffOptions{
BeforeCommitID: compareInfo.MergeBase,
AfterCommitID: headCommitID,
MaxLines: setting.Git.MaxGitDiffLines,
MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters,
MaxFiles: setting.Git.MaxGitDiffFiles,
})
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetDiff", err)
return false
}
if diff.NumFiles == 0 {
ctx.Data["Diff"] = nil
ctx.Data["DiffNotAvailable"] = true
} else {
ctx.Data["Diff"] = diff
ctx.Data["DiffNotAvailable"] = false
}
return false
}
type CompareCommit struct {
*git.Commit
Sha string
ParentShas []string
}
func CompareDiff(ctx *context.APIContext) {
headUser, headRepo, headGitRepo, compareInfo, baseBranch, headBranch := ParseCompareInfo(ctx)
if ctx.Written() {
return
}
defer headGitRepo.Close()
_ = PrepareComapreDiff(ctx, headUser, headRepo, headGitRepo, compareInfo, baseBranch, headBranch)
result := make([]CompareCommit, 0)
for _, commit := range compareInfo.Commits {
compareCommit := CompareCommit{
Commit: commit,
Sha: commit.ID.String(),
}
for _, p := range commit.Parents {
compareCommit.ParentShas = append(compareCommit.ParentShas, p.String())
}
result = append(result, compareCommit)
}
different := struct {
Commits []CompareCommit
Diff interface{}
CommitsCount int
Latestsha string
}{
Commits: result,
Diff: ctx.Context.Data["Diff"],
}
different.CommitsCount = len(compareInfo.Commits)
different.Latestsha = compareInfo.HeadCommitID
ctx.JSON(http.StatusOK, different)
}
func ParseCompareInfo(ctx *context.APIContext) (*user_model.User, *repo_model.Repository, *gitea_git.Repository, *gitea_git.CompareInfo, string, string) {
baseRepo := ctx.Repo.Repository
var (
headUser *user_model.User
headRepo *repo_model.Repository
headBranch string
isSameRepo bool
infoPath string
err error
)
infoPath = ctx.Params("*")
infos := strings.SplitN(infoPath, "...", 2)
if len(infos) != 2 {
log.Trace("ParseCompareInfo[%d]: not enough compared branches information %s", baseRepo.ID, infos)
ctx.NotFound("CompareAndPullRequest", nil)
return nil, nil, nil, nil, "", ""
}
ctx.Data["BaseName"] = baseRepo.OwnerName
baseBranch := infos[0]
ctx.Data["BaseBranch"] = baseBranch
headInfos := strings.Split(infos[1], ":")
if len(headInfos) == 1 {
isSameRepo = true
headUser = ctx.Repo.Owner
headBranch = headInfos[0]
} else if len(headInfos) == 2 {
headInfosSplit := strings.Split(headInfos[0], "/")
if len(headInfosSplit) == 1 {
headUser, err = user_model.GetUserByName(ctx, headInfos[0])
if err != nil {
if user_model.IsErrUserNotExist(err) {
ctx.NotFound("GetUserByName", nil)
} else {
ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
}
return nil, nil, nil, nil, "", ""
}
headBranch = headInfos[1]
isSameRepo = headUser.ID == ctx.Repo.Owner.ID
if isSameRepo {
headRepo = baseRepo
}
} else {
headRepo, err = repo_model.GetRepositoryByOwnerAndName(headInfosSplit[0], headInfosSplit[1])
if err != nil {
if repo_model.IsErrRepoNotExist(err) {
ctx.NotFound("GetRepositoryByOwnerAndName", nil)
} else {
ctx.Error(http.StatusInternalServerError, "GetRepositoryByOwnerAndName", err)
}
return nil, nil, nil, nil, "", ""
}
if err := headRepo.GetOwner(ctx); err != nil {
if user_model.IsErrUserNotExist(err) {
ctx.NotFound("GetOwner", nil)
} else {
ctx.Error(http.StatusInternalServerError, "GetOwner", err)
}
return nil, nil, nil, nil, "", ""
}
headBranch = headInfos[1]
headUser = headRepo.Owner
isSameRepo = headRepo.ID == ctx.Repo.Repository.ID
}
} else {
ctx.NotFound("CompareAndPullRequest", nil)
return nil, nil, nil, nil, "", ""
}
ctx.Data["HeadUser"] = headUser
ctx.Data["HeadBranch"] = headBranch
ctx.Repo.PullRequest.SameRepo = isSameRepo
if ctx.Repo.GitRepo == nil && !baseRepo.IsEmpty {
var err error
ctx.Repo.GitRepo, err = git.OpenRepository(ctx, ctx.Repo.Repository.RepoPath())
if err != nil {
ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
return nil, nil, nil, nil, "", ""
}
defer ctx.Repo.GitRepo.Close()
}
baseIsCommit := ctx.Repo.GitRepo.IsCommitExist(baseBranch)
baseIsBranch := ctx.Repo.GitRepo.IsBranchExist(baseBranch)
baseIsTag := ctx.Repo.GitRepo.IsTagExist(baseBranch)
if !baseIsCommit && !baseIsBranch && !baseIsTag {
if baseCommit, _ := ctx.Repo.GitRepo.GetCommit(baseBranch); baseCommit == nil {
ctx.NotFound("IsRefExist", nil)
return nil, nil, nil, nil, "", ""
} else {
baseBranch = baseCommit.ID.String()
ctx.Data["BaseBranch"] = baseBranch
baseIsCommit = true
}
}
ctx.Data["BaseIsCommit"] = baseIsCommit
ctx.Data["BaseIsBranch"] = baseIsBranch
ctx.Data["BaseIsTag"] = baseIsTag
var rootRepo *repo_model.Repository
if baseRepo.IsFork {
err = baseRepo.GetBaseRepo()
if err != nil {
if !repo_model.IsErrRepoNotExist(err) {
ctx.NotFound("GetBaseRepo", nil)
} else {
ctx.Error(http.StatusInternalServerError, "GetBaseRepo", err)
}
return nil, nil, nil, nil, "", ""
} else {
rootRepo = baseRepo.BaseRepo
}
}
var ownForkRpo *repo_model.Repository
if ctx.Doer != nil && baseRepo.OwnerID != ctx.Doer.ID {
has := repo_model.HasForkedRepo(ctx.Doer.ID, baseRepo.ID)
if has {
ownForkRpo = repo_model.GetForkedRepo(ctx.Doer.ID, baseRepo.ID)
ctx.Data["OwnForkRepo"] = ownForkRpo
}
}
has := headRepo != nil
if !has && rootRepo != nil && rootRepo.OwnerID == headUser.ID {
headRepo = rootRepo
has = true
}
if !has && ownForkRpo != nil && ownForkRpo.OwnerID == headUser.ID {
headRepo = ownForkRpo
has = true
}
if !has {
has = repo_model.HasForkedRepo(headUser.ID, baseRepo.ID)
headRepo = repo_model.GetForkedRepo(headUser.ID, baseRepo.ID)
}
if !has && baseRepo.IsFork {
has = repo_model.HasForkedRepo(headUser.ID, baseRepo.ForkID)
headRepo = repo_model.GetForkedRepo(headUser.ID, baseRepo.ForkID)
}
if !isSameRepo && !has {
ctx.Data["PageIsComparePull"] = false
}
var headGitRepo *gitea_git.Repository
if isSameRepo {
headRepo = ctx.Repo.Repository
headGitRepo = ctx.Repo.GitRepo
} else if has {
headGitRepo, err = git.OpenRepository(ctx, headRepo.RepoPath())
if err != nil {
ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
return nil, nil, nil, nil, "", ""
}
defer headGitRepo.Close()
}
ctx.Data["HeadRepo"] = headRepo
permBase, err := access_model.GetUserRepoPermission(ctx, baseRepo, ctx.Doer)
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err)
return nil, nil, nil, nil, "", ""
}
if !permBase.CanRead(unit_model.TypeCode) {
if log.IsTrace() {
log.Trace("Permission Denied: User: %-v cannot read code in Repo: %-v\nUser in baseRepo has Permissions: %-+v",
ctx.Doer,
baseRepo,
permBase)
}
ctx.NotFound("permBase.CanRead", nil)
return nil, nil, nil, nil, "", ""
}
if !isSameRepo {
permHead, err := access_model.GetUserRepoPermission(ctx, headRepo, ctx.Doer)
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err)
return nil, nil, nil, nil, "", ""
}
if !permHead.CanRead(unit_model.TypeCode) {
if log.IsTrace() {
log.Trace("Permission Denied: User: %-v cannot read code in Repo: %-v\nUser in headRepo has Permissions: %-+v",
ctx.Doer,
headRepo,
permHead)
}
ctx.NotFound("perHead.CanRead", nil)
return nil, nil, nil, nil, "", ""
}
}
if rootRepo != nil &&
rootRepo.ID != headRepo.ID &&
rootRepo.ID != baseRepo.ID {
perm, branches, err := getBranchesForRepo(ctx, ctx.Doer, rootRepo)
if err != nil {
ctx.Error(http.StatusInternalServerError, "getBranchesForRepo", err)
return nil, nil, nil, nil, "", ""
}
if perm {
ctx.Data["RootRepo"] = rootRepo
ctx.Data["RootRepoBranches"] = branches
}
}
if ownForkRpo != nil &&
ownForkRpo.ID != headRepo.ID &&
ownForkRpo.ID != baseRepo.ID &&
(rootRepo == nil || ownForkRpo.ID != rootRepo.ID) {
perm, branches, err := getBranchesForRepo(ctx, ctx.Doer, ownForkRpo)
if err != nil {
ctx.Error(http.StatusInternalServerError, "getBranchesForRepo", err)
return nil, nil, nil, nil, "", ""
}
if perm {
ctx.Data["OwnForkRepo"] = ownForkRpo
ctx.Data["OwnForkRepoBranches"] = branches
}
}
headIsCommit := headGitRepo.IsCommitExist(headBranch)
headIsBranch := headGitRepo.IsBranchExist(headBranch)
headIsTag := headGitRepo.IsTagExist(headBranch)
if !headIsCommit && !headIsBranch && !headIsTag {
if headCommit, _ := headGitRepo.GetCommit(headBranch); headCommit == nil {
ctx.NotFound("isRefExist", nil)
return nil, nil, nil, nil, "", ""
} else {
headBranch = headCommit.ID.String()
ctx.Data["HeadBranch"] = headBranch
headIsCommit = true
}
}
ctx.Data["HeadIsCommit"] = headIsCommit
ctx.Data["HeadIsBranch"] = headIsBranch
ctx.Data["HeadIsTag"] = headIsTag
if ctx.Data["PageIsComparePull"] == nil {
ctx.Data["PageIsComparePull"] = headIsBranch && baseIsBranch
}
if ctx.Data["PageIsComparePull"] == true && !permBase.CanReadIssuesOrPulls(true) {
if log.IsTrace() {
log.Trace("Permission Denied: User: %-v cannot create/read pull requests in Repo: %-v\nUser in baseRepo has Permissions: %-+v",
ctx.Doer,
baseRepo,
permBase)
}
ctx.NotFound("can not read issues or pulls", nil)
return nil, nil, nil, nil, "", ""
}
baseBranchRef := baseBranch
if baseIsBranch {
baseBranchRef = git.BranchPrefix + baseBranch
} else if baseIsTag {
baseBranchRef = git.TagPrefix + baseBranch
}
headBranchRef := headBranch
if headIsBranch {
headBranchRef = git.BranchPrefix + headBranch
} else if headIsTag {
headBranchRef = git.TagPrefix + headBranch
}
compareInfo, err := headGitRepo.GetCompareInfo(baseRepo.RepoPath(), baseBranchRef, headBranchRef, false, false)
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetCompareInfo", err)
return nil, nil, nil, nil, "", ""
}
ctx.Data["BeforeCommitID"] = compareInfo.MergeBase
return headUser, headRepo, headGitRepo, compareInfo, baseBranch, headBranch
}
func getBranchesForRepo(ctx *context.APIContext, user *user_model.User, repo *repo_model.Repository) (bool, []string, error) {
perm, err := access_model.GetUserRepoPermission(ctx, repo, user)
if err != nil {
return false, nil, err
}
if !perm.CanRead(unit_model.TypeCode) {
return false, nil, nil
}
gitRepo, err := git.OpenRepository(ctx, repo.RepoPath())
if err != nil {
return false, nil, err
}
defer gitRepo.Close()
branches, _, err := gitRepo.GetBranchNames(0, 0)
if err != nil {
return false, nil, err
}
return true, branches, nil
}
func MustBeNotEmpty(ctx *context.Context) {
if ctx.Repo.Repository.IsEmpty {
ctx.NotFound("MustBeNotEmpty", nil)
}
}
func SetEditorconfigIfExists(ctx *context.Context) {
if ctx.Repo.Repository.IsEmpty {
ctx.Data["Edidorconfig"] = nil
return
}
ec, err := ctx.Repo.GetEditorconfig()
if err != nil && !git.IsErrNotExist(err) {
description := fmt.Sprintf("Error while getting .Editconfig file: %v", err)
if err := system_model.CreateRepositoryNotice(description); err != nil {
ctx.ServerError("CreateRepositoryNotice", err)
}
return
}
ctx.Data["Editorconfig"] = ec
}
// SetdiffViewStyle set diff style as render variable
func SetDiffViewStyle(ctx *context.Context) {
queryStyle := ctx.Params("style")
if !ctx.IsSigned {
ctx.Data["IsSplitStyle"] = queryStyle == "split"
return
}
var (
userStyle = ctx.Doer.DiffViewStyle
style string
)
if queryStyle == "unified" || queryStyle == "split" {
style = queryStyle
} else if userStyle == "unified" || userStyle == "split" {
style = userStyle
} else {
style = "unified"
}
ctx.Data["IsSplitStyle"] = style == "split"
if err := user_model.UpdateUserDiffViewStyle(ctx.Doer, style); err != nil {
ctx.ServerError("ErrUpdateDiffViewStyle", err)
}
}
func GetContributors(ctx *context.APIContext) {
list, err := hat_models.GetContributors(ctx.Repo.Repository.ID, ctx.Repo.Owner.ID)
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetContributors", err)
return
}
ctx.JSON(http.StatusOK, list)
}
type CountDTO struct {
Branch CountDTOBranch `json:"branch"`
ReleaseCount int64 `json:"release_count"`
TagCount int64 `json:"tag_count"`
BranchCount int64 `json:"branch_count"`
}
type CountDTOBranch struct {
CommitCount int64 `json:"commit_count"`
BranchName string `json:"branch_name"`
}
func GetCommitCount(ctx *context.APIContext) {
ref := ctx.FormString("ref")
if ref == "" {
ref = ctx.Repo.Repository.DefaultBranch
}
commit, err := ctx.Repo.GitRepo.GetCommit(ref)
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetCommit", err)
return
}
commitCount, err := commit.CommitsCount()
if err != nil {
ctx.Error(http.StatusInternalServerError, "CommitsCount", err)
return
}
releaseCount, err := repo_model.GetReleaseCountByRepoID(ctx.Repo.Repository.ID, repo_model.FindReleasesOptions{IncludeDrafts: true, IncludeTags: true})
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetReleaseCountByRepoID", err)
return
}
branches, _, err := ctx.Repo.GitRepo.GetBranchNames(0, 0)
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetBranchNames", err)
return
}
tags, err := ctx.Repo.GitRepo.GetTags(0, 0)
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetTags", err)
return
}
ctx.JSON(http.StatusOK, CountDTO{
Branch: CountDTOBranch{
CommitCount: commitCount,
BranchName: ref,
},
BranchCount: int64(len(branches)),
TagCount: int64(len(tags)),
ReleaseCount: releaseCount,
})
}