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  }