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.0KB

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