code.gitea.io/gitea@v1.21.7/services/convert/git_commit.go (about) 1 // Copyright 2020 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package convert 5 6 import ( 7 "context" 8 "net/url" 9 "time" 10 11 repo_model "code.gitea.io/gitea/models/repo" 12 user_model "code.gitea.io/gitea/models/user" 13 ctx "code.gitea.io/gitea/modules/context" 14 "code.gitea.io/gitea/modules/git" 15 "code.gitea.io/gitea/modules/log" 16 api "code.gitea.io/gitea/modules/structs" 17 "code.gitea.io/gitea/modules/util" 18 "code.gitea.io/gitea/services/gitdiff" 19 ) 20 21 // ToCommitUser convert a git.Signature to an api.CommitUser 22 func ToCommitUser(sig *git.Signature) *api.CommitUser { 23 return &api.CommitUser{ 24 Identity: api.Identity{ 25 Name: sig.Name, 26 Email: sig.Email, 27 }, 28 Date: sig.When.UTC().Format(time.RFC3339), 29 } 30 } 31 32 // ToCommitMeta convert a git.Tag to an api.CommitMeta 33 func ToCommitMeta(repo *repo_model.Repository, tag *git.Tag) *api.CommitMeta { 34 return &api.CommitMeta{ 35 SHA: tag.Object.String(), 36 URL: util.URLJoin(repo.APIURL(), "git/commits", tag.ID.String()), 37 Created: tag.Tagger.When, 38 } 39 } 40 41 // ToPayloadCommit convert a git.Commit to api.PayloadCommit 42 func ToPayloadCommit(ctx context.Context, repo *repo_model.Repository, c *git.Commit) *api.PayloadCommit { 43 authorUsername := "" 44 if author, err := user_model.GetUserByEmail(ctx, c.Author.Email); err == nil { 45 authorUsername = author.Name 46 } else if !user_model.IsErrUserNotExist(err) { 47 log.Error("GetUserByEmail: %v", err) 48 } 49 50 committerUsername := "" 51 if committer, err := user_model.GetUserByEmail(ctx, c.Committer.Email); err == nil { 52 committerUsername = committer.Name 53 } else if !user_model.IsErrUserNotExist(err) { 54 log.Error("GetUserByEmail: %v", err) 55 } 56 57 return &api.PayloadCommit{ 58 ID: c.ID.String(), 59 Message: c.Message(), 60 URL: util.URLJoin(repo.HTMLURL(), "commit", c.ID.String()), 61 Author: &api.PayloadUser{ 62 Name: c.Author.Name, 63 Email: c.Author.Email, 64 UserName: authorUsername, 65 }, 66 Committer: &api.PayloadUser{ 67 Name: c.Committer.Name, 68 Email: c.Committer.Email, 69 UserName: committerUsername, 70 }, 71 Timestamp: c.Author.When, 72 Verification: ToVerification(ctx, c), 73 } 74 } 75 76 type ToCommitOptions struct { 77 Stat bool 78 Verification bool 79 Files bool 80 } 81 82 func ParseCommitOptions(ctx *ctx.APIContext) ToCommitOptions { 83 return ToCommitOptions{ 84 Stat: ctx.FormString("stat") == "" || ctx.FormBool("stat"), 85 Files: ctx.FormString("files") == "" || ctx.FormBool("files"), 86 Verification: ctx.FormString("verification") == "" || ctx.FormBool("verification"), 87 } 88 } 89 90 // ToCommit convert a git.Commit to api.Commit 91 func ToCommit(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, commit *git.Commit, userCache map[string]*user_model.User, opts ToCommitOptions) (*api.Commit, error) { 92 var apiAuthor, apiCommitter *api.User 93 94 // Retrieve author and committer information 95 96 var cacheAuthor *user_model.User 97 var ok bool 98 if userCache == nil { 99 cacheAuthor = (*user_model.User)(nil) 100 ok = false 101 } else { 102 cacheAuthor, ok = userCache[commit.Author.Email] 103 } 104 105 if ok { 106 apiAuthor = ToUser(ctx, cacheAuthor, nil) 107 } else { 108 author, err := user_model.GetUserByEmail(ctx, commit.Author.Email) 109 if err != nil && !user_model.IsErrUserNotExist(err) { 110 return nil, err 111 } else if err == nil { 112 apiAuthor = ToUser(ctx, author, nil) 113 if userCache != nil { 114 userCache[commit.Author.Email] = author 115 } 116 } 117 } 118 119 var cacheCommitter *user_model.User 120 if userCache == nil { 121 cacheCommitter = (*user_model.User)(nil) 122 ok = false 123 } else { 124 cacheCommitter, ok = userCache[commit.Committer.Email] 125 } 126 127 if ok { 128 apiCommitter = ToUser(ctx, cacheCommitter, nil) 129 } else { 130 committer, err := user_model.GetUserByEmail(ctx, commit.Committer.Email) 131 if err != nil && !user_model.IsErrUserNotExist(err) { 132 return nil, err 133 } else if err == nil { 134 apiCommitter = ToUser(ctx, committer, nil) 135 if userCache != nil { 136 userCache[commit.Committer.Email] = committer 137 } 138 } 139 } 140 141 // Retrieve parent(s) of the commit 142 apiParents := make([]*api.CommitMeta, commit.ParentCount()) 143 for i := 0; i < commit.ParentCount(); i++ { 144 sha, _ := commit.ParentID(i) 145 apiParents[i] = &api.CommitMeta{ 146 URL: repo.APIURL() + "/git/commits/" + url.PathEscape(sha.String()), 147 SHA: sha.String(), 148 } 149 } 150 151 res := &api.Commit{ 152 CommitMeta: &api.CommitMeta{ 153 URL: repo.APIURL() + "/git/commits/" + url.PathEscape(commit.ID.String()), 154 SHA: commit.ID.String(), 155 Created: commit.Committer.When, 156 }, 157 HTMLURL: repo.HTMLURL() + "/commit/" + url.PathEscape(commit.ID.String()), 158 RepoCommit: &api.RepoCommit{ 159 URL: repo.APIURL() + "/git/commits/" + url.PathEscape(commit.ID.String()), 160 Author: &api.CommitUser{ 161 Identity: api.Identity{ 162 Name: commit.Author.Name, 163 Email: commit.Author.Email, 164 }, 165 Date: commit.Author.When.Format(time.RFC3339), 166 }, 167 Committer: &api.CommitUser{ 168 Identity: api.Identity{ 169 Name: commit.Committer.Name, 170 Email: commit.Committer.Email, 171 }, 172 Date: commit.Committer.When.Format(time.RFC3339), 173 }, 174 Message: commit.Message(), 175 Tree: &api.CommitMeta{ 176 URL: repo.APIURL() + "/git/trees/" + url.PathEscape(commit.ID.String()), 177 SHA: commit.ID.String(), 178 Created: commit.Committer.When, 179 }, 180 }, 181 Author: apiAuthor, 182 Committer: apiCommitter, 183 Parents: apiParents, 184 } 185 186 // Retrieve verification for commit 187 if opts.Verification { 188 res.RepoCommit.Verification = ToVerification(ctx, commit) 189 } 190 191 // Retrieve files affected by the commit 192 if opts.Files { 193 fileStatus, err := git.GetCommitFileStatus(gitRepo.Ctx, repo.RepoPath(), commit.ID.String()) 194 if err != nil { 195 return nil, err 196 } 197 198 affectedFileList := make([]*api.CommitAffectedFiles, 0, len(fileStatus.Added)+len(fileStatus.Removed)+len(fileStatus.Modified)) 199 for filestatus, files := range map[string][]string{"added": fileStatus.Added, "removed": fileStatus.Removed, "modified": fileStatus.Modified} { 200 for _, filename := range files { 201 affectedFileList = append(affectedFileList, &api.CommitAffectedFiles{ 202 Filename: filename, 203 Status: filestatus, 204 }) 205 } 206 } 207 208 res.Files = affectedFileList 209 } 210 211 // Get diff stats for commit 212 if opts.Stat { 213 diff, err := gitdiff.GetDiff(gitRepo, &gitdiff.DiffOptions{ 214 AfterCommitID: commit.ID.String(), 215 }) 216 if err != nil { 217 return nil, err 218 } 219 220 res.Stats = &api.CommitStats{ 221 Total: diff.TotalAddition + diff.TotalDeletion, 222 Additions: diff.TotalAddition, 223 Deletions: diff.TotalDeletion, 224 } 225 } 226 227 return res, nil 228 }