repo_commits.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. package v1
  2. import (
  3. "net/http"
  4. "strings"
  5. "time"
  6. "github.com/gogs/git-module"
  7. "gogs.io/gogs/internal/conf"
  8. "gogs.io/gogs/internal/context"
  9. "gogs.io/gogs/internal/database"
  10. "gogs.io/gogs/internal/gitutil"
  11. "gogs.io/gogs/internal/route/api/v1/types"
  12. )
  13. const mediaApplicationSHA = "application/vnd.gogs.sha"
  14. // getAllCommits returns a slice of commits starting from HEAD.
  15. func getAllCommits(c *context.APIContext) {
  16. // Get pagesize, set default if it is not specified.
  17. pageSize := c.QueryInt("pageSize")
  18. if pageSize == 0 {
  19. pageSize = 30
  20. }
  21. gitRepo, err := git.Open(c.Repo.Repository.RepoPath())
  22. if err != nil {
  23. c.Error(err, "open repository")
  24. return
  25. }
  26. // The response object returned as JSON
  27. result := make([]*types.Commit, 0, pageSize)
  28. commits, err := gitRepo.Log("HEAD", git.LogOptions{MaxCount: pageSize})
  29. if err != nil {
  30. c.Error(err, "git log")
  31. }
  32. for _, commit := range commits {
  33. apiCommit, err := gitCommitToAPICommit(commit, c)
  34. if err != nil {
  35. c.Error(err, "convert git commit to api commit")
  36. return
  37. }
  38. result = append(result, apiCommit)
  39. }
  40. c.JSONSuccess(result)
  41. }
  42. // getSingleCommit will return a single Commit object based on the specified SHA.
  43. func getSingleCommit(c *context.APIContext) {
  44. if strings.Contains(c.Req.Header.Get("Accept"), mediaApplicationSHA) {
  45. c.SetParams("*", c.Params(":sha"))
  46. getReferenceSHA(c)
  47. return
  48. }
  49. gitRepo, err := git.Open(c.Repo.Repository.RepoPath())
  50. if err != nil {
  51. c.Error(err, "open repository")
  52. return
  53. }
  54. commit, err := gitRepo.CatFileCommit(c.Params(":sha"))
  55. if err != nil {
  56. c.NotFoundOrError(gitutil.NewError(err), "get commit")
  57. return
  58. }
  59. apiCommit, err := gitCommitToAPICommit(commit, c)
  60. if err != nil {
  61. c.Error(err, "convert git commit to api commit")
  62. }
  63. c.JSONSuccess(apiCommit)
  64. }
  65. func getReferenceSHA(c *context.APIContext) {
  66. gitRepo, err := git.Open(c.Repo.Repository.RepoPath())
  67. if err != nil {
  68. c.Error(err, "open repository")
  69. return
  70. }
  71. ref := c.Params("*")
  72. refType := 0 // 0-unknown, 1-branch, 2-tag
  73. if after, ok := strings.CutPrefix(ref, git.RefsHeads); ok {
  74. ref = after
  75. refType = 1
  76. } else if after, ok := strings.CutPrefix(ref, git.RefsTags); ok {
  77. ref = after
  78. refType = 2
  79. } else {
  80. if gitRepo.HasBranch(ref) {
  81. refType = 1
  82. } else if gitRepo.HasTag(ref) {
  83. refType = 2
  84. } else {
  85. c.NotFound()
  86. return
  87. }
  88. }
  89. var sha string
  90. switch refType {
  91. case 1:
  92. sha, err = gitRepo.BranchCommitID(ref)
  93. case 2:
  94. sha, err = gitRepo.TagCommitID(ref)
  95. }
  96. if err != nil {
  97. c.NotFoundOrError(gitutil.NewError(err), "get reference commit ID")
  98. return
  99. }
  100. c.PlainText(http.StatusOK, sha)
  101. }
  102. // gitCommitToApiCommit is a helper function to convert git commit object to API commit.
  103. func gitCommitToAPICommit(commit *git.Commit, c *context.APIContext) (*types.Commit, error) {
  104. // Retrieve author and committer information
  105. var apiAuthor, apiCommitter *types.User
  106. author, err := database.Handle.Users().GetByEmail(c.Req.Context(), commit.Author.Email)
  107. if err != nil && !database.IsErrUserNotExist(err) {
  108. return nil, err
  109. } else if err == nil {
  110. apiAuthor = toUser(author)
  111. }
  112. // Save one query if the author is also the committer
  113. if commit.Committer.Email == commit.Author.Email {
  114. apiCommitter = apiAuthor
  115. } else {
  116. committer, err := database.Handle.Users().GetByEmail(c.Req.Context(), commit.Committer.Email)
  117. if err != nil && !database.IsErrUserNotExist(err) {
  118. return nil, err
  119. } else if err == nil {
  120. apiCommitter = toUser(committer)
  121. }
  122. }
  123. // Retrieve parent(s) of the commit
  124. apiParents := make([]*types.CommitMeta, commit.ParentsCount())
  125. for i := 0; i < commit.ParentsCount(); i++ {
  126. sha, _ := commit.ParentID(i)
  127. apiParents[i] = &types.CommitMeta{
  128. URL: c.BaseURL + "/repos/" + c.Repo.Repository.FullName() + "/commits/" + sha.String(),
  129. SHA: sha.String(),
  130. }
  131. }
  132. return &types.Commit{
  133. CommitMeta: &types.CommitMeta{
  134. URL: conf.Server.ExternalURL + c.Link[1:],
  135. SHA: commit.ID.String(),
  136. },
  137. HTMLURL: c.Repo.Repository.HTMLURL() + "/commits/" + commit.ID.String(),
  138. RepoCommit: &types.RepoCommit{
  139. URL: conf.Server.ExternalURL + c.Link[1:],
  140. Author: &types.CommitUser{
  141. Name: commit.Author.Name,
  142. Email: commit.Author.Email,
  143. Date: commit.Author.When.Format(time.RFC3339),
  144. },
  145. Committer: &types.CommitUser{
  146. Name: commit.Committer.Name,
  147. Email: commit.Committer.Email,
  148. Date: commit.Committer.When.Format(time.RFC3339),
  149. },
  150. Message: commit.Summary(),
  151. Tree: &types.CommitMeta{
  152. URL: c.BaseURL + "/repos/" + c.Repo.Repository.FullName() + "/tree/" + commit.ID.String(),
  153. SHA: commit.ID.String(),
  154. },
  155. },
  156. Author: apiAuthor,
  157. Committer: apiCommitter,
  158. Parents: apiParents,
  159. }, nil
  160. }