summaryrefslogtreecommitdiffstats
path: root/modules/repository/commits.go
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--modules/repository/commits.go173
1 files changed, 173 insertions, 0 deletions
diff --git a/modules/repository/commits.go b/modules/repository/commits.go
new file mode 100644
index 0000000..ede6042
--- /dev/null
+++ b/modules/repository/commits.go
@@ -0,0 +1,173 @@
+// 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"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/cache"
+ "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 (pc *PushCommits) toAPIPayloadCommit(ctx context.Context, emailUsers map[string]*user_model.User, repoPath, repoLink string, 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, 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", repoLink, 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, repoPath, repoLink string) ([]*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 := pc.toAPIPayloadCommit(ctx, emailUsers, repoPath, repoLink, 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 = pc.toAPIPayloadCommit(ctx, emailUsers, repoPath, repoLink, 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, "push_commits", email, func() (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),
+ }
+}