mirror of
https://github.com/go-gitea/gitea.git
synced 2025-04-24 18:38:46 +03:00

When list commits, some of the commits authors are the same at many situations. But current logic will always fetch the same GPG keys from database. This PR will cache the GPG keys, emails and users for the context so that reducing the database queries. --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
176 lines
5.1 KiB
Go
176 lines
5.1 KiB
Go
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package repository
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/url"
|
|
"time"
|
|
|
|
"code.gitea.io/gitea/models/avatars"
|
|
repo_model "code.gitea.io/gitea/models/repo"
|
|
user_model "code.gitea.io/gitea/models/user"
|
|
"code.gitea.io/gitea/modules/cache"
|
|
"code.gitea.io/gitea/modules/cachegroup"
|
|
"code.gitea.io/gitea/modules/git"
|
|
"code.gitea.io/gitea/modules/log"
|
|
"code.gitea.io/gitea/modules/setting"
|
|
api "code.gitea.io/gitea/modules/structs"
|
|
)
|
|
|
|
// PushCommit represents a commit in a push operation.
|
|
type PushCommit struct {
|
|
Sha1 string
|
|
Message string
|
|
AuthorEmail string
|
|
AuthorName string
|
|
CommitterEmail string
|
|
CommitterName string
|
|
Timestamp time.Time
|
|
}
|
|
|
|
// PushCommits represents list of commits in a push operation.
|
|
type PushCommits struct {
|
|
Commits []*PushCommit
|
|
HeadCommit *PushCommit
|
|
CompareURL string
|
|
Len int
|
|
}
|
|
|
|
// NewPushCommits creates a new PushCommits object.
|
|
func NewPushCommits() *PushCommits {
|
|
return &PushCommits{}
|
|
}
|
|
|
|
// ToAPIPayloadCommit converts a single PushCommit to an api.PayloadCommit object.
|
|
func ToAPIPayloadCommit(ctx context.Context, emailUsers map[string]*user_model.User, repo *repo_model.Repository, commit *PushCommit) (*api.PayloadCommit, error) {
|
|
var err error
|
|
authorUsername := ""
|
|
author, ok := emailUsers[commit.AuthorEmail]
|
|
if !ok {
|
|
author, err = user_model.GetUserByEmail(ctx, commit.AuthorEmail)
|
|
if err == nil {
|
|
authorUsername = author.Name
|
|
emailUsers[commit.AuthorEmail] = author
|
|
}
|
|
} else {
|
|
authorUsername = author.Name
|
|
}
|
|
|
|
committerUsername := ""
|
|
committer, ok := emailUsers[commit.CommitterEmail]
|
|
if !ok {
|
|
committer, err = user_model.GetUserByEmail(ctx, commit.CommitterEmail)
|
|
if err == nil {
|
|
// TODO: check errors other than email not found.
|
|
committerUsername = committer.Name
|
|
emailUsers[commit.CommitterEmail] = committer
|
|
}
|
|
} else {
|
|
committerUsername = committer.Name
|
|
}
|
|
|
|
fileStatus, err := git.GetCommitFileStatus(ctx, repo.RepoPath(), commit.Sha1)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("FileStatus [commit_sha1: %s]: %w", commit.Sha1, err)
|
|
}
|
|
|
|
return &api.PayloadCommit{
|
|
ID: commit.Sha1,
|
|
Message: commit.Message,
|
|
URL: fmt.Sprintf("%s/commit/%s", repo.HTMLURL(), url.PathEscape(commit.Sha1)),
|
|
Author: &api.PayloadUser{
|
|
Name: commit.AuthorName,
|
|
Email: commit.AuthorEmail,
|
|
UserName: authorUsername,
|
|
},
|
|
Committer: &api.PayloadUser{
|
|
Name: commit.CommitterName,
|
|
Email: commit.CommitterEmail,
|
|
UserName: committerUsername,
|
|
},
|
|
Added: fileStatus.Added,
|
|
Removed: fileStatus.Removed,
|
|
Modified: fileStatus.Modified,
|
|
Timestamp: commit.Timestamp,
|
|
}, nil
|
|
}
|
|
|
|
// ToAPIPayloadCommits converts a PushCommits object to api.PayloadCommit format.
|
|
// It returns all converted commits and, if provided, the head commit or an error otherwise.
|
|
func (pc *PushCommits) ToAPIPayloadCommits(ctx context.Context, repo *repo_model.Repository) ([]*api.PayloadCommit, *api.PayloadCommit, error) {
|
|
commits := make([]*api.PayloadCommit, len(pc.Commits))
|
|
var headCommit *api.PayloadCommit
|
|
|
|
emailUsers := make(map[string]*user_model.User)
|
|
|
|
for i, commit := range pc.Commits {
|
|
apiCommit, err := ToAPIPayloadCommit(ctx, emailUsers, repo, commit)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
commits[i] = apiCommit
|
|
if pc.HeadCommit != nil && pc.HeadCommit.Sha1 == commits[i].ID {
|
|
headCommit = apiCommit
|
|
}
|
|
}
|
|
if pc.HeadCommit != nil && headCommit == nil {
|
|
var err error
|
|
headCommit, err = ToAPIPayloadCommit(ctx, emailUsers, repo, pc.HeadCommit)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
}
|
|
return commits, headCommit, nil
|
|
}
|
|
|
|
// AvatarLink tries to match user in database with e-mail
|
|
// in order to show custom avatar, and falls back to general avatar link.
|
|
func (pc *PushCommits) AvatarLink(ctx context.Context, email string) string {
|
|
size := avatars.DefaultAvatarPixelSize * setting.Avatar.RenderedSizeFactor
|
|
|
|
v, _ := cache.GetWithContextCache(ctx, cachegroup.EmailAvatarLink, email, func(ctx context.Context, email string) (string, error) {
|
|
u, err := user_model.GetUserByEmail(ctx, email)
|
|
if err != nil {
|
|
if !user_model.IsErrUserNotExist(err) {
|
|
log.Error("GetUserByEmail: %v", err)
|
|
return "", err
|
|
}
|
|
return avatars.GenerateEmailAvatarFastLink(ctx, email, size), nil
|
|
}
|
|
return u.AvatarLinkWithSize(ctx, size), nil
|
|
})
|
|
|
|
return v
|
|
}
|
|
|
|
// CommitToPushCommit transforms a git.Commit to PushCommit type.
|
|
func CommitToPushCommit(commit *git.Commit) *PushCommit {
|
|
return &PushCommit{
|
|
Sha1: commit.ID.String(),
|
|
Message: commit.Message(),
|
|
AuthorEmail: commit.Author.Email,
|
|
AuthorName: commit.Author.Name,
|
|
CommitterEmail: commit.Committer.Email,
|
|
CommitterName: commit.Committer.Name,
|
|
Timestamp: commit.Author.When,
|
|
}
|
|
}
|
|
|
|
// GitToPushCommits transforms a list of git.Commits to PushCommits type.
|
|
func GitToPushCommits(gitCommits []*git.Commit) *PushCommits {
|
|
commits := make([]*PushCommit, 0, len(gitCommits))
|
|
for _, commit := range gitCommits {
|
|
commits = append(commits, CommitToPushCommit(commit))
|
|
}
|
|
return &PushCommits{
|
|
Commits: commits,
|
|
HeadCommit: nil,
|
|
CompareURL: "",
|
|
Len: len(commits),
|
|
}
|
|
}
|