Files

403 lines
11 KiB
Go
Raw Permalink Normal View History

2019-02-03 11:35:17 +08:00
// Copyright 2018 The Gogs Authors. All rights reserved.
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
2019-02-03 11:35:17 +08:00
package repo
import (
"math"
2019-12-20 18:07:12 +01:00
"net/http"
"strconv"
"time"
2019-02-03 11:35:17 +08:00
issues_model "code.gitea.io/gitea/models/issues"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
2019-02-03 11:35:17 +08:00
"code.gitea.io/gitea/modules/setting"
2019-05-11 18:21:34 +08:00
api "code.gitea.io/gitea/modules/structs"
2020-01-24 19:00:29 +00:00
"code.gitea.io/gitea/routers/api/v1/utils"
"code.gitea.io/gitea/services/context"
2022-12-29 03:57:15 +01:00
"code.gitea.io/gitea/services/convert"
2019-02-03 11:35:17 +08:00
)
// GetSingleCommit get a commit via sha
func GetSingleCommit(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/git/commits/{sha} repository repoGetSingleCommit
2019-02-03 11:35:17 +08:00
// ---
// summary: Get a single commit from a repository
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: sha
// in: path
// description: a git ref or commit sha
2019-02-03 11:35:17 +08:00
// type: string
// required: true
2023-05-09 18:34:07 -07:00
// - name: stat
// in: query
// description: include diff stats for every commit (disable for speedup, default 'true')
// type: boolean
// - name: verification
// in: query
// description: include verification for every commit (disable for speedup, default 'true')
// type: boolean
// - name: files
// in: query
// description: include a list of affected files for every commit (disable for speedup, default 'true')
// type: boolean
2019-02-03 11:35:17 +08:00
// responses:
// "200":
// "$ref": "#/responses/Commit"
2020-04-08 04:54:46 +02:00
// "422":
// "$ref": "#/responses/validationError"
2019-02-03 11:35:17 +08:00
// "404":
// "$ref": "#/responses/notFound"
2024-12-24 21:47:45 +08:00
sha := ctx.PathParam("sha")
2022-09-04 11:47:56 +01:00
if !git.IsValidRefPattern(sha) {
2025-04-01 12:14:01 +02:00
ctx.APIError(http.StatusUnprocessableEntity, "no valid ref or sha: "+sha)
2020-04-08 04:54:46 +02:00
return
}
2023-05-09 18:34:07 -07:00
getCommit(ctx, sha, convert.ParseCommitOptions(ctx))
2020-04-08 04:54:46 +02:00
}
2023-05-09 18:34:07 -07:00
func getCommit(ctx *context.APIContext, identifier string, toCommitOpts convert.ToCommitOptions) {
commit, err := ctx.Repo.GitRepo.GetCommit(identifier)
2019-02-03 11:35:17 +08:00
if err != nil {
2021-01-26 23:36:53 +08:00
if git.IsErrNotExist(err) {
ctx.APIErrorNotFound("commit doesn't exist: " + identifier)
2021-01-26 23:36:53 +08:00
return
}
2025-02-18 04:41:03 +08:00
ctx.APIErrorInternal(err)
2019-02-03 11:35:17 +08:00
return
}
2023-05-09 18:34:07 -07:00
json, err := convert.ToCommit(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, commit, nil, toCommitOpts)
if err != nil {
2025-02-18 04:41:03 +08:00
ctx.APIErrorInternal(err)
return
}
2019-12-20 18:07:12 +01:00
ctx.JSON(http.StatusOK, json)
}
// GetAllCommits get all commits via
func GetAllCommits(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/commits repository repoGetAllCommits
// ---
// summary: Get a list of all commits from a repository
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: sha
// in: query
// description: SHA or branch to start listing commits from (usually 'master')
// type: string
2021-12-22 07:17:33 +01:00
// - name: path
// in: query
// description: filepath of a file/dir
// type: string
// - name: since
// in: query
// description: Only commits after this date will be returned (ISO 8601 format)
// type: string
// format: date-time
// - name: until
// in: query
// description: Only commits before this date will be returned (ISO 8601 format)
// type: string
// format: date-time
// - name: stat
// in: query
// description: include diff stats for every commit (disable for speedup, default 'true')
// type: boolean
2023-05-08 18:06:05 -07:00
// - name: verification
// in: query
// description: include verification for every commit (disable for speedup, default 'true')
// type: boolean
// - name: files
// in: query
// description: include a list of affected files for every commit (disable for speedup, default 'true')
// type: boolean
// - name: page
// in: query
2020-01-24 19:00:29 +00:00
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
2021-12-22 07:17:33 +01:00
// description: page size of results (ignored if used with 'path')
// type: integer
// - name: not
// in: query
// description: commits that match the given specifier will not be listed.
// type: string
// responses:
// "200":
// "$ref": "#/responses/CommitList"
// "404":
// "$ref": "#/responses/notFound"
// "409":
// "$ref": "#/responses/EmptyRepository"
since := ctx.FormString("since")
until := ctx.FormString("until")
// Validate since/until as ISO 8601 (RFC3339)
if since != "" {
if _, err := time.Parse(time.RFC3339, since); err != nil {
ctx.APIError(http.StatusUnprocessableEntity, "invalid 'since' format, expected ISO 8601 (RFC3339)")
return
}
}
if until != "" {
if _, err := time.Parse(time.RFC3339, until); err != nil {
ctx.APIError(http.StatusUnprocessableEntity, "invalid 'until' format, expected ISO 8601 (RFC3339)")
return
}
}
if ctx.Repo.Repository.IsEmpty {
2019-12-20 18:07:12 +01:00
ctx.JSON(http.StatusConflict, api.APIError{
Message: "Git Repository is empty.",
URL: setting.API.SwaggerURL,
})
return
}
2020-01-24 19:00:29 +00:00
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")
2021-12-22 07:17:33 +01:00
path := ctx.FormString("path")
2023-05-08 00:10:53 -07:00
not := ctx.FormString("not")
2021-12-22 07:17:33 +01:00
var (
commitsCountTotal int64
commits []*git.Commit
err error
2021-12-22 07:17:33 +01:00
)
if len(path) == 0 {
var baseCommit *git.Commit
if len(sha) == 0 {
// no sha supplied - use default branch
baseCommit, err = ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
2021-12-22 07:17:33 +01:00
if err != nil {
2025-02-18 04:41:03 +08:00
ctx.APIErrorInternal(err)
2021-12-22 07:17:33 +01:00
return
}
} else {
// get commit specified by sha
baseCommit, err = ctx.Repo.GitRepo.GetCommit(sha)
2021-12-22 07:17:33 +01:00
if err != nil {
2025-02-18 04:41:03 +08:00
ctx.NotFoundOrServerError(err)
2021-12-22 07:17:33 +01:00
return
}
}
2021-12-22 07:17:33 +01:00
// Total commit count
commitsCountTotal, err = gitrepo.CommitsCount(ctx, ctx.Repo.Repository, gitrepo.CommitsCountOptions{
2023-05-08 00:10:53 -07:00
Not: not,
Revision: []string{baseCommit.ID.String()},
Since: since,
Until: until,
2023-05-08 00:10:53 -07:00
})
if err != nil {
2025-02-18 04:41:03 +08:00
ctx.APIErrorInternal(err)
return
}
2021-12-22 07:17:33 +01:00
// Query commits
commits, err = baseCommit.CommitsByRange(listOptions.Page, listOptions.PageSize, not, since, until)
if err != nil {
2025-02-18 04:41:03 +08:00
ctx.APIErrorInternal(err)
return
}
} else {
2021-12-22 07:17:33 +01:00
if len(sha) == 0 {
sha = ctx.Repo.Repository.DefaultBranch
}
commitsCountTotal, err = gitrepo.CommitsCount(ctx, ctx.Repo.Repository,
gitrepo.CommitsCountOptions{
2023-05-08 00:10:53 -07:00
Not: not,
Revision: []string{sha},
RelPath: []string{path},
Since: since,
Until: until,
2023-05-08 00:10:53 -07:00
})
if err != nil {
2025-02-18 04:41:03 +08:00
ctx.APIErrorInternal(err)
2021-12-22 07:17:33 +01:00
return
} else if commitsCountTotal == 0 {
2025-02-17 14:13:17 +08:00
ctx.APIErrorNotFound("FileCommitsCount", nil)
return
}
2023-05-08 00:10:53 -07:00
commits, err = ctx.Repo.GitRepo.CommitsByFileAndRange(
git.CommitsByFileAndRangeOptions{
Revision: sha,
File: path,
Not: not,
Page: listOptions.Page,
Since: since,
Until: until,
2023-05-08 00:10:53 -07:00
})
2021-12-22 07:17:33 +01:00
if err != nil {
2025-02-18 04:41:03 +08:00
ctx.APIErrorInternal(err)
2021-12-22 07:17:33 +01:00
return
}
}
2020-01-24 19:00:29 +00:00
pageCount := int(math.Ceil(float64(commitsCountTotal) / float64(listOptions.PageSize)))
userCache := make(map[string]*user_model.User)
2021-08-09 20:08:51 +02:00
apiCommits := make([]*api.Commit, len(commits))
2021-08-09 20:08:51 +02:00
for i, commit := range commits {
// Create json struct
2023-05-09 18:34:07 -07:00
apiCommits[i], err = convert.ToCommit(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, commit, userCache, convert.ParseCommitOptions(ctx))
if err != nil {
2025-02-18 04:41:03 +08:00
ctx.APIErrorInternal(err)
return
}
}
2026-03-08 15:35:50 +01:00
ctx.SetLinkHeader(commitsCountTotal, listOptions.PageSize)
2021-08-12 14:43:08 +02:00
ctx.SetTotalCountHeader(commitsCountTotal)
// kept for backwards compatibility
2021-12-15 14:59:57 +08:00
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))
2021-08-12 14:43:08 +02:00
ctx.AppendAccessControlExposeHeaders("X-Page", "X-PerPage", "X-Total", "X-PageCount", "X-HasMore")
2019-12-20 18:07:12 +01:00
ctx.JSON(http.StatusOK, &apiCommits)
}
2021-09-20 18:14:29 +02:00
// DownloadCommitDiffOrPatch render a commit's raw diff or patch
func DownloadCommitDiffOrPatch(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/git/commits/{sha}.{diffType} repository repoDownloadCommitDiffOrPatch
// ---
// summary: Get a commit's diff or patch
// produces:
// - text/plain
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: sha
// in: path
// description: SHA of the commit to get
// type: string
// required: true
// - name: diffType
// in: path
// description: whether the output is diff or patch
// type: string
// enum: [diff, patch]
// required: true
// responses:
// "200":
// "$ref": "#/responses/string"
// "404":
// "$ref": "#/responses/notFound"
2024-12-24 21:47:45 +08:00
sha := ctx.PathParam("sha")
diffType := git.RawDiffType(ctx.PathParam("diffType"))
if err := git.GetRawDiff(ctx.Repo.GitRepo, sha, diffType, ctx.Resp); err != nil {
2021-09-20 18:14:29 +02:00
if git.IsErrNotExist(err) {
ctx.APIErrorNotFound("commit doesn't exist: " + sha)
2021-09-20 18:14:29 +02:00
return
}
2025-02-18 04:41:03 +08:00
ctx.APIErrorInternal(err)
2021-09-20 18:14:29 +02:00
return
}
}
// GetCommitPullRequest returns the merged pull request of the commit
func GetCommitPullRequest(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/commits/{sha}/pull repository repoGetCommitPullRequest
// ---
// summary: Get the merged pull request of the commit
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: sha
// in: path
// description: SHA of the commit to get
// type: string
// required: true
// responses:
// "200":
// "$ref": "#/responses/PullRequest"
// "404":
// "$ref": "#/responses/notFound"
pr, err := issues_model.GetPullRequestByMergedCommit(ctx, ctx.Repo.Repository.ID, ctx.PathParam("sha"))
if err != nil {
if issues_model.IsErrPullRequestNotExist(err) {
2025-02-17 14:13:17 +08:00
ctx.APIError(http.StatusNotFound, err)
} else {
2025-02-18 04:41:03 +08:00
ctx.APIErrorInternal(err)
}
return
}
if err = pr.LoadBaseRepo(ctx); err != nil {
2025-02-18 04:41:03 +08:00
ctx.APIErrorInternal(err)
return
}
if err = pr.LoadHeadRepo(ctx); err != nil {
2025-02-18 04:41:03 +08:00
ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, convert.ToAPIPullRequest(ctx, pr, ctx.Doer))
}