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