summaryrefslogtreecommitdiffstats
path: root/routers
diff options
context:
space:
mode:
authorMike Schwörer <mailport@mikescher.de>2019-08-26 16:09:10 +0200
committerAntoine GIRARD <sapk@users.noreply.github.com>2019-08-26 16:09:10 +0200
commit042089fbaf8a818042405a75bee08dea8db347c0 (patch)
tree5f5692b5e29a4cf8ab5b2c03a735506073317480 /routers
parent6b3f52fe5f7db5b3122cc8481ab8fed83e273fde (diff)
downloadgitea-042089fbaf8a818042405a75bee08dea8db347c0.tar.gz
gitea-042089fbaf8a818042405a75bee08dea8db347c0.zip
API method to list all commits of a repository (#6408)
* Added API endpoint ListAllCommits (/repos/{owner}/{repo}/git/commits) Signed-off-by: Mike Schwörer <mailport@mikescher.de> * Fixed failing drone build Signed-off-by: Mike Schwörer <mailport@mikescher.de> * Implemented requested changes (PR reviews) Signed-off-by: Mike Schwörer <mailport@mikescher.de> * gofmt Signed-off-by: Mike Schwörer <mailport@mikescher.de> * Changed api route from "/repos/{owner}/{repo}/git/commits" to "/repos/{owner}/{repo}/commits" * Removed unnecessary line * better error message when git repo is empty * make generate-swagger * fixed removed return * Update routers/api/v1/repo/commits.go Co-Authored-By: Lauris BH <lauris@nix.lv> * Update routers/api/v1/repo/commits.go Co-Authored-By: Lauris BH <lauris@nix.lv> * go fmt * Refactored common code into ToCommit() * made toCommit not exported * added check for userCache == nil
Diffstat (limited to 'routers')
-rw-r--r--routers/api/v1/api.go11
-rw-r--r--routers/api/v1/repo/commits.go207
-rw-r--r--routers/api/v1/swagger/repo.go29
3 files changed, 221 insertions, 26 deletions
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index 64c4b47a64..2842d78cd3 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -744,10 +744,13 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Combo("/:sha").Get(repo.GetCommitStatuses).
Post(reqToken(), bind(api.CreateStatusOption{}), repo.NewCommitStatus)
}, reqRepoReader(models.UnitTypeCode))
- m.Group("/commits/:ref", func() {
- // TODO: Add m.Get("") for single commit (https://developer.github.com/v3/repos/commits/#get-a-single-commit)
- m.Get("/status", repo.GetCombinedCommitStatusByRef)
- m.Get("/statuses", repo.GetCommitStatusesByRef)
+ m.Group("/commits", func() {
+ m.Get("", repo.GetAllCommits)
+ m.Group("/:ref", func() {
+ // TODO: Add m.Get("") for single commit (https://developer.github.com/v3/repos/commits/#get-a-single-commit)
+ m.Get("/status", repo.GetCombinedCommitStatusByRef)
+ m.Get("/statuses", repo.GetCommitStatusesByRef)
+ })
}, reqRepoReader(models.UnitTypeCode))
m.Group("/git", func() {
m.Group("/commits", func() {
diff --git a/routers/api/v1/repo/commits.go b/routers/api/v1/repo/commits.go
index 795ac1f22f..0156aaaa05 100644
--- a/routers/api/v1/repo/commits.go
+++ b/routers/api/v1/repo/commits.go
@@ -6,6 +6,8 @@
package repo
import (
+ "math"
+ "strconv"
"time"
"code.gitea.io/gitea/models"
@@ -55,25 +57,186 @@ func GetSingleCommit(ctx *context.APIContext) {
return
}
- // Retrieve author and committer information
- var apiAuthor, apiCommitter *api.User
- author, err := models.GetUserByEmail(commit.Author.Email)
- if err != nil && !models.IsErrUserNotExist(err) {
- ctx.ServerError("Get user by author email", err)
+ json, err := toCommit(ctx, ctx.Repo.Repository, commit, nil)
+ if err != nil {
+ ctx.ServerError("toCommit", err)
+ return
+ }
+
+ ctx.JSON(200, 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
+ // - name: page
+ // in: query
+ // description: page number of requested commits
+ // type: integer
+ // responses:
+ // "200":
+ // "$ref": "#/responses/CommitList"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ // "409":
+ // "$ref": "#/responses/EmptyRepository"
+
+ if ctx.Repo.Repository.IsEmpty {
+ ctx.JSON(409, api.APIError{
+ Message: "Git Repository is empty.",
+ URL: setting.API.SwaggerURL,
+ })
+ return
+ }
+
+ gitRepo, err := git.OpenRepository(ctx.Repo.Repository.RepoPath())
+ if err != nil {
+ ctx.ServerError("OpenRepository", err)
+ return
+ }
+
+ page := ctx.QueryInt("page")
+ if page <= 0 {
+ page = 1
+ }
+
+ sha := ctx.Query("sha")
+
+ var baseCommit *git.Commit
+ if len(sha) == 0 {
+ // no sha supplied - use default branch
+ head, err := gitRepo.GetHEADBranch()
+ if err != nil {
+ ctx.ServerError("GetHEADBranch", err)
+ return
+ }
+
+ baseCommit, err = gitRepo.GetBranchCommit(head.Name)
+ if err != nil {
+ ctx.ServerError("GetCommit", err)
+ return
+ }
+ } else {
+ // get commit specified by sha
+ baseCommit, err = gitRepo.GetCommit(sha)
+ if err != nil {
+ ctx.ServerError("GetCommit", err)
+ return
+ }
+ }
+
+ // Total commit count
+ commitsCountTotal, err := baseCommit.CommitsCount()
+ if err != nil {
+ ctx.ServerError("GetCommitsCount", err)
+ return
+ }
+
+ pageCount := int(math.Ceil(float64(commitsCountTotal) / float64(git.CommitsRangeSize)))
+
+ // Query commits
+ commits, err := baseCommit.CommitsByRange(page)
+ if err != nil {
+ ctx.ServerError("CommitsByRange", err)
return
- } else if err == nil {
- apiAuthor = author.APIFormat()
}
- // Save one query if the author is also the committer
- if commit.Committer.Email == commit.Author.Email {
- apiCommitter = apiAuthor
+
+ userCache := make(map[string]*models.User)
+
+ apiCommits := make([]*api.Commit, commits.Len())
+
+ i := 0
+ for commitPointer := commits.Front(); commitPointer != nil; commitPointer = commitPointer.Next() {
+ commit := commitPointer.Value.(*git.Commit)
+
+ // Create json struct
+ apiCommits[i], err = toCommit(ctx, ctx.Repo.Repository, commit, userCache)
+ if err != nil {
+ ctx.ServerError("toCommit", err)
+ return
+ }
+
+ i++
+ }
+
+ ctx.SetLinkHeader(int(commitsCountTotal), git.CommitsRangeSize)
+
+ ctx.Header().Set("X-Page", strconv.Itoa(page))
+ ctx.Header().Set("X-PerPage", strconv.Itoa(git.CommitsRangeSize))
+ ctx.Header().Set("X-Total", strconv.FormatInt(commitsCountTotal, 10))
+ ctx.Header().Set("X-PageCount", strconv.Itoa(pageCount))
+ ctx.Header().Set("X-HasMore", strconv.FormatBool(page < pageCount))
+
+ ctx.JSON(200, &apiCommits)
+}
+
+func toCommit(ctx *context.APIContext, repo *models.Repository, commit *git.Commit, userCache map[string]*models.User) (*api.Commit, error) {
+
+ var apiAuthor, apiCommitter *api.User
+
+ // Retrieve author and committer information
+
+ var cacheAuthor *models.User
+ var ok bool
+ if userCache == nil {
+ cacheAuthor = ((*models.User)(nil))
+ ok = false
+ } else {
+ cacheAuthor, ok = userCache[commit.Author.Email]
+ }
+
+ if ok {
+ apiAuthor = cacheAuthor.APIFormat()
+ } else {
+ author, err := models.GetUserByEmail(commit.Author.Email)
+ if err != nil && !models.IsErrUserNotExist(err) {
+ return nil, err
+ } else if err == nil {
+ apiAuthor = author.APIFormat()
+ if userCache != nil {
+ userCache[commit.Author.Email] = author
+ }
+ }
+ }
+
+ var cacheCommitter *models.User
+ if userCache == nil {
+ cacheCommitter = ((*models.User)(nil))
+ ok = false
+ } else {
+ cacheCommitter, ok = userCache[commit.Committer.Email]
+ }
+
+ if ok {
+ apiCommitter = cacheCommitter.APIFormat()
} else {
committer, err := models.GetUserByEmail(commit.Committer.Email)
if err != nil && !models.IsErrUserNotExist(err) {
- ctx.ServerError("Get user by committer email", err)
- return
+ return nil, err
} else if err == nil {
apiCommitter = committer.APIFormat()
+ if userCache != nil {
+ userCache[commit.Committer.Email] = committer
+ }
}
}
@@ -82,23 +245,23 @@ func GetSingleCommit(ctx *context.APIContext) {
for i := 0; i < commit.ParentCount(); i++ {
sha, _ := commit.ParentID(i)
apiParents[i] = &api.CommitMeta{
- URL: ctx.Repo.Repository.APIURL() + "/git/commits/" + sha.String(),
+ URL: repo.APIURL() + "/git/commits/" + sha.String(),
SHA: sha.String(),
}
}
- ctx.JSON(200, &api.Commit{
+ return &api.Commit{
CommitMeta: &api.CommitMeta{
- URL: setting.AppURL + ctx.Link[1:],
+ URL: repo.APIURL() + "/git/commits/" + commit.ID.String(),
SHA: commit.ID.String(),
},
- HTMLURL: ctx.Repo.Repository.HTMLURL() + "/commit/" + commit.ID.String(),
+ HTMLURL: repo.HTMLURL() + "/commit/" + commit.ID.String(),
RepoCommit: &api.RepoCommit{
- URL: setting.AppURL + ctx.Link[1:],
+ URL: repo.APIURL() + "/git/commits/" + commit.ID.String(),
Author: &api.CommitUser{
Identity: api.Identity{
- Name: commit.Author.Name,
- Email: commit.Author.Email,
+ Name: commit.Committer.Name,
+ Email: commit.Committer.Email,
},
Date: commit.Author.When.Format(time.RFC3339),
},
@@ -109,14 +272,14 @@ func GetSingleCommit(ctx *context.APIContext) {
},
Date: commit.Committer.When.Format(time.RFC3339),
},
- Message: commit.Message(),
+ Message: commit.Summary(),
Tree: &api.CommitMeta{
- URL: ctx.Repo.Repository.APIURL() + "/git/trees/" + commit.ID.String(),
+ URL: repo.APIURL() + "/git/trees/" + commit.ID.String(),
SHA: commit.ID.String(),
},
},
Author: apiAuthor,
Committer: apiCommitter,
Parents: apiParents,
- })
+ }, nil
}
diff --git a/routers/api/v1/swagger/repo.go b/routers/api/v1/swagger/repo.go
index 2cab5b0ed4..422cc0861c 100644
--- a/routers/api/v1/swagger/repo.go
+++ b/routers/api/v1/swagger/repo.go
@@ -190,6 +190,35 @@ type swaggerCommit struct {
Body api.Commit `json:"body"`
}
+// CommitList
+// swagger:response CommitList
+type swaggerCommitList struct {
+ // The current page
+ Page int `json:"X-Page"`
+
+ // Commits per page
+ PerPage int `json:"X-PerPage"`
+
+ // Total commit count
+ Total int `json:"X-Total"`
+
+ // Total number of pages
+ PageCount int `json:"X-PageCount"`
+
+ // True if there is another page
+ HasMore bool `json:"X-HasMore"`
+
+ //in: body
+ Body []api.Commit `json:"body"`
+}
+
+// EmptyRepository
+// swagger:response EmptyRepository
+type swaggerEmptyRepository struct {
+ //in: body
+ Body api.APIError `json:"body"`
+}
+
// FileResponse
// swagger:response FileResponse
type swaggerFileResponse struct {