You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

commits.go 5.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package repository
  4. import (
  5. "context"
  6. "fmt"
  7. "net/url"
  8. "time"
  9. "code.gitea.io/gitea/models/avatars"
  10. user_model "code.gitea.io/gitea/models/user"
  11. "code.gitea.io/gitea/modules/git"
  12. "code.gitea.io/gitea/modules/log"
  13. "code.gitea.io/gitea/modules/setting"
  14. api "code.gitea.io/gitea/modules/structs"
  15. )
  16. // PushCommit represents a commit in a push operation.
  17. type PushCommit struct {
  18. Sha1 string
  19. Message string
  20. AuthorEmail string
  21. AuthorName string
  22. CommitterEmail string
  23. CommitterName string
  24. Timestamp time.Time
  25. }
  26. // PushCommits represents list of commits in a push operation.
  27. type PushCommits struct {
  28. Commits []*PushCommit
  29. HeadCommit *PushCommit
  30. CompareURL string
  31. Len int
  32. avatars map[string]string
  33. emailUsers map[string]*user_model.User
  34. }
  35. // NewPushCommits creates a new PushCommits object.
  36. func NewPushCommits() *PushCommits {
  37. return &PushCommits{
  38. avatars: make(map[string]string),
  39. emailUsers: make(map[string]*user_model.User),
  40. }
  41. }
  42. // toAPIPayloadCommit converts a single PushCommit to an api.PayloadCommit object.
  43. func (pc *PushCommits) toAPIPayloadCommit(ctx context.Context, repoPath, repoLink string, commit *PushCommit) (*api.PayloadCommit, error) {
  44. var err error
  45. authorUsername := ""
  46. author, ok := pc.emailUsers[commit.AuthorEmail]
  47. if !ok {
  48. author, err = user_model.GetUserByEmail(commit.AuthorEmail)
  49. if err == nil {
  50. authorUsername = author.Name
  51. pc.emailUsers[commit.AuthorEmail] = author
  52. }
  53. } else {
  54. authorUsername = author.Name
  55. }
  56. committerUsername := ""
  57. committer, ok := pc.emailUsers[commit.CommitterEmail]
  58. if !ok {
  59. committer, err = user_model.GetUserByEmail(commit.CommitterEmail)
  60. if err == nil {
  61. // TODO: check errors other than email not found.
  62. committerUsername = committer.Name
  63. pc.emailUsers[commit.CommitterEmail] = committer
  64. }
  65. } else {
  66. committerUsername = committer.Name
  67. }
  68. fileStatus, err := git.GetCommitFileStatus(ctx, repoPath, commit.Sha1)
  69. if err != nil {
  70. return nil, fmt.Errorf("FileStatus [commit_sha1: %s]: %w", commit.Sha1, err)
  71. }
  72. return &api.PayloadCommit{
  73. ID: commit.Sha1,
  74. Message: commit.Message,
  75. URL: fmt.Sprintf("%s/commit/%s", repoLink, url.PathEscape(commit.Sha1)),
  76. Author: &api.PayloadUser{
  77. Name: commit.AuthorName,
  78. Email: commit.AuthorEmail,
  79. UserName: authorUsername,
  80. },
  81. Committer: &api.PayloadUser{
  82. Name: commit.CommitterName,
  83. Email: commit.CommitterEmail,
  84. UserName: committerUsername,
  85. },
  86. Added: fileStatus.Added,
  87. Removed: fileStatus.Removed,
  88. Modified: fileStatus.Modified,
  89. Timestamp: commit.Timestamp,
  90. }, nil
  91. }
  92. // ToAPIPayloadCommits converts a PushCommits object to api.PayloadCommit format.
  93. // It returns all converted commits and, if provided, the head commit or an error otherwise.
  94. func (pc *PushCommits) ToAPIPayloadCommits(ctx context.Context, repoPath, repoLink string) ([]*api.PayloadCommit, *api.PayloadCommit, error) {
  95. commits := make([]*api.PayloadCommit, len(pc.Commits))
  96. var headCommit *api.PayloadCommit
  97. if pc.emailUsers == nil {
  98. pc.emailUsers = make(map[string]*user_model.User)
  99. }
  100. for i, commit := range pc.Commits {
  101. apiCommit, err := pc.toAPIPayloadCommit(ctx, repoPath, repoLink, commit)
  102. if err != nil {
  103. return nil, nil, err
  104. }
  105. commits[i] = apiCommit
  106. if pc.HeadCommit != nil && pc.HeadCommit.Sha1 == commits[i].ID {
  107. headCommit = apiCommit
  108. }
  109. }
  110. if pc.HeadCommit != nil && headCommit == nil {
  111. var err error
  112. headCommit, err = pc.toAPIPayloadCommit(ctx, repoPath, repoLink, pc.HeadCommit)
  113. if err != nil {
  114. return nil, nil, err
  115. }
  116. }
  117. return commits, headCommit, nil
  118. }
  119. // AvatarLink tries to match user in database with e-mail
  120. // in order to show custom avatar, and falls back to general avatar link.
  121. func (pc *PushCommits) AvatarLink(email string) string {
  122. if pc.avatars == nil {
  123. pc.avatars = make(map[string]string)
  124. }
  125. avatar, ok := pc.avatars[email]
  126. if ok {
  127. return avatar
  128. }
  129. size := avatars.DefaultAvatarPixelSize * setting.Avatar.RenderedSizeFactor
  130. u, ok := pc.emailUsers[email]
  131. if !ok {
  132. var err error
  133. u, err = user_model.GetUserByEmail(email)
  134. if err != nil {
  135. pc.avatars[email] = avatars.GenerateEmailAvatarFastLink(email, size)
  136. if !user_model.IsErrUserNotExist(err) {
  137. log.Error("GetUserByEmail: %v", err)
  138. return ""
  139. }
  140. } else {
  141. pc.emailUsers[email] = u
  142. }
  143. }
  144. if u != nil {
  145. pc.avatars[email] = u.AvatarLinkWithSize(size)
  146. }
  147. return pc.avatars[email]
  148. }
  149. // CommitToPushCommit transforms a git.Commit to PushCommit type.
  150. func CommitToPushCommit(commit *git.Commit) *PushCommit {
  151. return &PushCommit{
  152. Sha1: commit.ID.String(),
  153. Message: commit.Message(),
  154. AuthorEmail: commit.Author.Email,
  155. AuthorName: commit.Author.Name,
  156. CommitterEmail: commit.Committer.Email,
  157. CommitterName: commit.Committer.Name,
  158. Timestamp: commit.Author.When,
  159. }
  160. }
  161. // GitToPushCommits transforms a list of git.Commits to PushCommits type.
  162. func GitToPushCommits(gitCommits []*git.Commit) *PushCommits {
  163. commits := make([]*PushCommit, 0, len(gitCommits))
  164. for _, commit := range gitCommits {
  165. commits = append(commits, CommitToPushCommit(commit))
  166. }
  167. return &PushCommits{
  168. Commits: commits,
  169. HeadCommit: nil,
  170. CompareURL: "",
  171. Len: len(commits),
  172. avatars: make(map[string]string),
  173. emailUsers: make(map[string]*user_model.User),
  174. }
  175. }