gitea_hat/routers/hat/repo/commits.go

451 lines
13 KiB
Go

package repo
import (
"fmt"
"math"
"net/http"
"strconv"
"code.gitea.io/gitea/models/repo"
user "code.gitea.io/gitea/models/user"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
"code.gitea.io/gitea/services/convert"
"code.gitea.io/gitea/services/gitdiff"
hat_convert "code.gitlink.org.cn/Gitlink/gitea_hat.git/modules/convert"
hat_git "code.gitlink.org.cn/Gitlink/gitea_hat.git/modules/git"
hat_api "code.gitlink.org.cn/Gitlink/gitea_hat.git/modules/structs"
)
func GetAllCommitsSliceByTime(ctx *context.APIContext) {
if ctx.Repo.Repository.IsEmpty {
ctx.JSON(http.StatusConflict, api.APIError{
Message: "Git Repository is empty.",
URL: setting.API.SwaggerURL,
})
return
}
gitRepo, err := git.OpenRepository(ctx, ctx.Repo.Repository.RepoPath())
if err != nil {
ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
return
}
defer gitRepo.Close()
listOptions := utils.GetListOptions(ctx)
if listOptions.Page <= 0 {
listOptions.Page = 1
}
if listOptions.PageSize > setting.Git.CommitsRangeSize {
listOptions.PageSize = setting.Git.CommitsRangeSize
}
sha := ctx.FormString("sha")
// path := ctx.FormString("path")
not := ctx.FormString("not")
var baseCommit *git.Commit
if len(sha) == 0 {
head, err := gitRepo.GetHEADBranch()
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetHEADBranch", err)
return
}
baseCommit, err = gitRepo.GetBranchCommit(head.Name)
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetBranchCommit", err)
return
}
} else {
baseCommit, err = gitRepo.GetCommit(sha)
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetCommit", err)
return
}
}
commitsCountTotal, err := baseCommit.CommitsCount()
if err != nil {
ctx.Error(http.StatusInternalServerError, "CommitsCount", err)
return
}
pageCount := int(math.Ceil(float64(commitsCountTotal)))
commits, err := baseCommit.CommitsByRange(listOptions.Page, listOptions.PageSize, not)
if err != nil {
ctx.Error(http.StatusInternalServerError, "CommitsByRange", err)
return
}
userCache := make(map[string]*user.User)
apiCommits := make([]*responseCommit, len(commits))
apiCommitsList := []responseCommit{}
for i, commitPoniter := range commits {
apiCommits[i], err = toResponseCommit(ctx, ctx.Repo.Repository, gitRepo, commitPoniter, userCache, convert.ParseCommitOptions(ctx))
if err != nil {
ctx.Error(http.StatusInternalServerError, "ToCommit", err)
return
}
apiCommitsList = append(apiCommitsList, *apiCommits[i])
}
ctx.RespHeader().Set("X-Page", strconv.Itoa(listOptions.Page))
ctx.RespHeader().Set("X-PerPage", strconv.Itoa(listOptions.PageSize))
ctx.RespHeader().Set("X-Total", strconv.FormatInt(commitsCountTotal, 10))
ctx.RespHeader().Set("X-PageCount", strconv.Itoa(pageCount))
ctx.RespHeader().Set("X-HasMore", strconv.FormatBool(listOptions.Page < pageCount))
ctx.SetLinkHeader(int(commitsCountTotal), listOptions.PageSize)
ctx.RespHeader().Set("X-Total-Count", fmt.Sprintf("%d", commitsCountTotal))
ctx.RespHeader().Set("Access-Control-Expose-Headers", "X-Total-Count, X-PerPage, X-Total, X-PageCount, X-HasMore, Link")
ctx.JSON(http.StatusOK, apiCommitsList)
}
type responseCommit struct {
*api.Commit
CommitDate string `json:"commit_date"`
}
func toResponseCommit(ctx *context.APIContext, repo *repo.Repository, gitRepo *git.Repository, commit *git.Commit, userCache map[string]*user.User, opts convert.ToCommitOptions) (*responseCommit, error) {
apiCommit, err := convert.ToCommit(ctx, repo, gitRepo, commit, userCache, opts)
if err != nil {
return nil, err
}
return &responseCommit{
Commit: apiCommit,
CommitDate: apiCommit.Created.Format("2006-01-02"),
}, nil
}
func GetRecentCommits(ctx *context.APIContext) {
if ctx.Repo.Repository.IsEmpty {
ctx.JSON(http.StatusConflict, api.APIError{
Message: "Git Repository is empty",
URL: setting.API.SwaggerURL,
})
return
}
keyword := ctx.FormString("keyword")
listOptions := utils.GetListOptions(ctx)
if listOptions.Page <= 0 {
listOptions.Page = 1
}
if listOptions.PageSize > setting.Git.CommitsRangeSize {
listOptions.PageSize = setting.Git.CommitsRangeSize
}
var baseCommit *git.Commit
var commitsCountTotal int64
var err error
head, err := ctx.Repo.GitRepo.GetHEADBranch()
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetHeadBranch", err)
return
}
baseCommit, err = ctx.Repo.GitRepo.GetBranchCommit(head.Name)
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetBranchCommit", err)
return
}
commitsCountTotal, err = hat_git.GetAllCommitsCount(ctx.Repo.GitRepo, keyword)
if err != nil {
ctx.Error(http.StatusInternalServerError, "CommitsCountFiles", err)
return
}
pageCount := int(math.Ceil(float64(commitsCountTotal) / float64(listOptions.PageSize)))
commits, err := hat_git.AllCommitsByFileAndRange(ctx.Repo.GitRepo,
git.CommitsByFileAndRangeOptions{
Revision: baseCommit.ID.String(),
File: ".",
Page: listOptions.Page,
}, keyword, listOptions.PageSize)
if err != nil {
ctx.Error(http.StatusInternalServerError, "CommitsByRange", err)
return
}
userCache := make(map[string]*user_model.User)
apiCommits := make([]*api.Commit, len(commits))
for i, commit := range commits {
apiCommits[i], err = convert.ToCommit(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, commit, userCache, convert.ParseCommitOptions(ctx))
if err != nil {
ctx.Error(http.StatusInternalServerError, "ToCommit", err)
return
}
}
ctx.Resp.Header().Set("X-Page", strconv.Itoa(listOptions.Page))
ctx.Resp.Header().Set("X-PerPage", strconv.Itoa(listOptions.PageSize))
ctx.Resp.Header().Set("X-Total", strconv.FormatInt(commitsCountTotal, 10))
ctx.Resp.Header().Set("X-PageCount", strconv.Itoa(pageCount))
ctx.Resp.Header().Set("X-HasMore", strconv.FormatBool(listOptions.Page < pageCount))
ctx.SetLinkHeader(int(commitsCountTotal), listOptions.PageSize)
ctx.Resp.Header().Set("X-Total-Count", fmt.Sprintf("%d", commitsCountTotal))
ctx.JSON(http.StatusOK, apiCommits)
}
func GetFileAllCommits(ctx *context.APIContext) {
if ctx.Repo.Repository.IsEmpty {
ctx.JSON(http.StatusConflict, api.APIError{
Message: "Git Repository is empty",
URL: setting.API.SwaggerURL,
})
return
}
listOptions := utils.GetListOptions(ctx)
if listOptions.Page <= 0 {
listOptions.Page = 1
}
if listOptions.PageSize > setting.Git.CommitsRangeSize {
listOptions.PageSize = setting.Git.CommitsRangeSize
}
ref := ctx.FormString("sha")
if ref == "" {
ref = ctx.FormString("ref")
}
treePath := ctx.Params("*")
var baseCommit *git.Commit
var commitsCountTotal int64
var err error
if len(ref) == 0 {
head, err := ctx.Repo.GitRepo.GetHEADBranch()
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetHeadBranch", err)
return
}
baseCommit, err = ctx.Repo.GitRepo.GetBranchCommit(head.Name)
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetBranchCommit", err)
return
}
commitsCountTotal, err = ctx.Repo.GitRepo.FileCommitsCount(head.Name, treePath)
if err != nil {
ctx.Error(http.StatusInternalServerError, "CommitsCountFiles", err)
return
}
} else {
baseCommit, err = ctx.Repo.GitRepo.GetCommit(ref)
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetCommit", err)
return
}
commitsCountTotal, err = ctx.Repo.GitRepo.FileCommitsCount(ref, treePath)
if err != nil {
ctx.Error(http.StatusInternalServerError, "CommitsCountFiles", err)
return
}
}
pageCount := int(math.Ceil(float64(commitsCountTotal) / float64(listOptions.PageSize)))
commits, err := ctx.Repo.GitRepo.CommitsByFileAndRange(
git.CommitsByFileAndRangeOptions{
Revision: baseCommit.ID.String(),
File: treePath,
Page: listOptions.Page,
})
if err != nil {
ctx.Error(http.StatusInternalServerError, "CommitsByRange", err)
return
}
userCache := make(map[string]*user_model.User)
apiCommits := make([]*api.Commit, len(commits))
for i, commit := range commits {
apiCommits[i], err = convert.ToCommit(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, commit, userCache, convert.ParseCommitOptions(ctx))
if err != nil {
ctx.Error(http.StatusInternalServerError, "ToCommit", err)
return
}
}
ctx.Resp.Header().Set("X-Page", strconv.Itoa(listOptions.Page))
ctx.Resp.Header().Set("X-PerPage", strconv.Itoa(listOptions.PageSize))
ctx.Resp.Header().Set("X-Total", strconv.FormatInt(commitsCountTotal, 10))
ctx.Resp.Header().Set("X-PageCount", strconv.Itoa(pageCount))
ctx.Resp.Header().Set("X-HasMore", strconv.FormatBool(listOptions.Page < pageCount))
ctx.SetLinkHeader(int(commitsCountTotal), listOptions.PageSize)
ctx.Resp.Header().Set("X-Total-Count", fmt.Sprintf("%d", commitsCountTotal))
ctx.JSON(http.StatusOK, apiCommits)
}
func GetSingleCommit(ctx *context.APIContext) {
sha := ctx.Params(":sha")
if !git.IsValidRefPattern(sha) {
ctx.Error(http.StatusUnprocessableEntity, "no valid ref or sha", fmt.Sprintf("no valid ref or sha: %s", sha))
return
}
commit, err := ctx.Repo.GitRepo.GetCommit(sha)
if err != nil {
if git.IsErrNotExist(err) {
ctx.NotFound(sha)
return
}
ctx.Error(http.StatusInternalServerError, "gitRepo.GetCommit", err)
return
}
json, err := hat_convert.ToCommit(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, commit, nil, convert.ParseCommitOptions(ctx))
if err != nil {
ctx.Error(http.StatusInternalServerError, "toCommit", err)
return
}
ctx.JSON(http.StatusOK, json)
}
func GetCommitFiles(ctx *context.APIContext) {
commitID := ctx.Params(":sha")
gitRepo := ctx.Repo.GitRepo
commit, err := gitRepo.GetCommit(commitID)
if err != nil {
if git.IsErrNotExist(err) {
ctx.NotFound(commitID)
return
}
ctx.Error(http.StatusInternalServerError, "gitRepo.GetCommit", err)
return
}
if len(commitID) != 40 {
commitID = commit.ID.String()
}
diff, err := gitdiff.GetDiff(gitRepo, &gitdiff.DiffOptions{
AfterCommitID: commitID,
SkipTo: ctx.FormString("skip-to"),
MaxLines: setting.Git.MaxGitDiffLines,
MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters,
MaxFiles: -1,
WhitespaceBehavior: gitdiff.GetWhitespaceFlag(ctx.FormString("whitespace")),
})
if err != nil {
ctx.NotFound("GetDIff", err)
return
}
listOptions := utils.GetListOptions(ctx)
totalNumberOfFiles := diff.NumFiles
totalNumberOfPages := int(math.Ceil(float64(totalNumberOfFiles) / float64(listOptions.PageSize)))
start, end := listOptions.GetStartEnd()
if end > totalNumberOfFiles {
end = totalNumberOfFiles
}
lenFiles := end - start
if lenFiles < 0 {
lenFiles = 0
}
apiFiles := make([]*hat_api.ChangedFile, 0, lenFiles)
for i := start; i < end; i++ {
apiFiles = append(apiFiles, hat_convert.ToChangedFile(diff.Files[i], ctx.Repo.Repository, commitID))
}
ctx.SetLinkHeader(totalNumberOfFiles, listOptions.PageSize)
ctx.SetTotalCountHeader(int64(totalNumberOfFiles))
ctx.RespHeader().Set("X-Page", strconv.Itoa(listOptions.Page))
ctx.RespHeader().Set("X-PerPage", strconv.Itoa(listOptions.PageSize))
ctx.RespHeader().Set("X-PageCount", strconv.Itoa(totalNumberOfPages))
ctx.RespHeader().Set("X-HasMore", strconv.FormatBool(listOptions.Page < totalNumberOfPages))
ctx.AppendAccessControlExposeHeaders("X-Page", "X-PerPage", "X-PageCount", "X-HasMore")
ctx.JSON(http.StatusOK, struct {
Files []*hat_api.ChangedFile `json:"files"`
TotalAddition int `json:"total_addition"`
TotalDeletion int `json:"total_deletion"`
}{
Files: apiFiles,
TotalAddition: diff.TotalAddition,
TotalDeletion: diff.TotalDeletion,
})
}
func GetCommitFilesByPath(ctx *context.APIContext) {
commitID := ctx.Params(":sha")
gitRepo := ctx.Repo.GitRepo
path := ctx.Params("*")
commit, err := gitRepo.GetCommit(commitID)
if err != nil {
if git.IsErrNotExist(err) {
ctx.NotFound(commitID)
return
}
ctx.Error(http.StatusInternalServerError, "gitRepo.GetCommit", err)
return
}
if len(commitID) != 40 {
commitID = commit.ID.String()
}
diff, err := gitdiff.GetDiff(gitRepo, &gitdiff.DiffOptions{
AfterCommitID: commitID,
SkipTo: ctx.FormString("skip-to"),
MaxLines: setting.Git.MaxGitDiffLines,
MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters,
MaxFiles: -1,
WhitespaceBehavior: gitdiff.GetWhitespaceFlag(ctx.FormString("whitespace")),
}, path)
if err != nil {
ctx.NotFound("GetDIff", err)
return
}
ctx.JSON(http.StatusOK, &diff)
}
func GetCommitDiff(ctx *context.APIContext) {
commitID := ctx.Params(":sha")
gitRepo := ctx.Repo.GitRepo
commit, err := gitRepo.GetCommit(commitID)
if err != nil {
if git.IsErrNotExist(err) {
ctx.NotFound(commitID)
return
}
ctx.Error(http.StatusInternalServerError, "gitRepo.GetCommit", err)
return
}
if len(commitID) != 40 {
commitID = commit.ID.String()
}
diff, err := gitdiff.GetDiff(gitRepo, &gitdiff.DiffOptions{
AfterCommitID: commitID,
MaxLines: setting.Git.MaxGitDiffLines,
MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters,
MaxFiles: setting.Git.MaxGitDiffFiles,
})
if err != nil {
ctx.NotFound("GetDIff", err)
return
}
ctx.JSON(http.StatusOK, &diff)
}