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  }