gitea源码

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