552 lines
15 KiB
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,
|
|
})
|
|
|
|
}
|