gitea源码

file.go 5.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package files
  4. import (
  5. "context"
  6. "errors"
  7. "fmt"
  8. "net/url"
  9. "strings"
  10. "time"
  11. repo_model "code.gitea.io/gitea/models/repo"
  12. "code.gitea.io/gitea/modules/git"
  13. "code.gitea.io/gitea/modules/setting"
  14. api "code.gitea.io/gitea/modules/structs"
  15. "code.gitea.io/gitea/modules/util"
  16. "code.gitea.io/gitea/routers/api/v1/utils"
  17. )
  18. func GetContentsListFromTreePaths(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, refCommit *utils.RefCommit, treePaths []string) (files []*api.ContentsResponse) {
  19. var size int64
  20. for _, treePath := range treePaths {
  21. // ok if fails, then will be nil
  22. fileContents, _ := GetFileContents(ctx, repo, gitRepo, refCommit, GetContentsOrListOptions{
  23. TreePath: treePath,
  24. IncludeSingleFileContent: true,
  25. IncludeCommitMetadata: true,
  26. })
  27. if fileContents != nil && fileContents.Content != nil && *fileContents.Content != "" {
  28. // if content isn't empty (e.g., due to the single blob being too large), add file size to response size
  29. size += int64(len(*fileContents.Content))
  30. }
  31. if size > setting.API.DefaultMaxResponseSize {
  32. break // stop if max response size would be exceeded
  33. }
  34. files = append(files, fileContents)
  35. if len(files) == setting.API.DefaultPagingNum {
  36. break // stop if paging num reached
  37. }
  38. }
  39. return files
  40. }
  41. func GetFilesResponseFromCommit(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, refCommit *utils.RefCommit, treeNames []string) (*api.FilesResponse, error) {
  42. files := GetContentsListFromTreePaths(ctx, repo, gitRepo, refCommit, treeNames)
  43. fileCommitResponse, _ := GetFileCommitResponse(repo, refCommit.Commit) // ok if fails, then will be nil
  44. verification := GetPayloadCommitVerification(ctx, refCommit.Commit)
  45. filesResponse := &api.FilesResponse{
  46. Files: files,
  47. Commit: fileCommitResponse,
  48. Verification: verification,
  49. }
  50. return filesResponse, nil
  51. }
  52. // constructs a FileResponse with the file at the index from FilesResponse
  53. func GetFileResponseFromFilesResponse(filesResponse *api.FilesResponse, index int) *api.FileResponse {
  54. content := &api.ContentsResponse{}
  55. if len(filesResponse.Files) > index {
  56. content = filesResponse.Files[index]
  57. }
  58. fileResponse := &api.FileResponse{
  59. Content: content,
  60. Commit: filesResponse.Commit,
  61. Verification: filesResponse.Verification,
  62. }
  63. return fileResponse
  64. }
  65. // GetFileCommitResponse Constructs a FileCommitResponse from a Commit object
  66. func GetFileCommitResponse(repo *repo_model.Repository, commit *git.Commit) (*api.FileCommitResponse, error) {
  67. if repo == nil {
  68. return nil, errors.New("repo cannot be nil")
  69. }
  70. if commit == nil {
  71. return nil, errors.New("commit cannot be nil")
  72. }
  73. commitURL, _ := url.Parse(repo.APIURL() + "/git/commits/" + url.PathEscape(commit.ID.String()))
  74. commitTreeURL, _ := url.Parse(repo.APIURL() + "/git/trees/" + url.PathEscape(commit.Tree.ID.String()))
  75. parents := make([]*api.CommitMeta, commit.ParentCount())
  76. for i := 0; i <= commit.ParentCount(); i++ {
  77. if parent, err := commit.Parent(i); err == nil && parent != nil {
  78. parentCommitURL, _ := url.Parse(repo.APIURL() + "/git/commits/" + url.PathEscape(parent.ID.String()))
  79. parents[i] = &api.CommitMeta{
  80. SHA: parent.ID.String(),
  81. URL: parentCommitURL.String(),
  82. }
  83. }
  84. }
  85. commitHTMLURL, _ := url.Parse(repo.HTMLURL() + "/commit/" + url.PathEscape(commit.ID.String()))
  86. fileCommit := &api.FileCommitResponse{
  87. CommitMeta: api.CommitMeta{
  88. SHA: commit.ID.String(),
  89. URL: commitURL.String(),
  90. },
  91. HTMLURL: commitHTMLURL.String(),
  92. Author: &api.CommitUser{
  93. Identity: api.Identity{
  94. Name: commit.Author.Name,
  95. Email: commit.Author.Email,
  96. },
  97. Date: commit.Author.When.UTC().Format(time.RFC3339),
  98. },
  99. Committer: &api.CommitUser{
  100. Identity: api.Identity{
  101. Name: commit.Committer.Name,
  102. Email: commit.Committer.Email,
  103. },
  104. Date: commit.Committer.When.UTC().Format(time.RFC3339),
  105. },
  106. Message: commit.Message(),
  107. Tree: &api.CommitMeta{
  108. URL: commitTreeURL.String(),
  109. SHA: commit.Tree.ID.String(),
  110. },
  111. Parents: parents,
  112. }
  113. return fileCommit, nil
  114. }
  115. // ErrFilenameInvalid represents a "FilenameInvalid" kind of error.
  116. type ErrFilenameInvalid struct {
  117. Path string
  118. }
  119. // IsErrFilenameInvalid checks if an error is an ErrFilenameInvalid.
  120. func IsErrFilenameInvalid(err error) bool {
  121. _, ok := err.(ErrFilenameInvalid)
  122. return ok
  123. }
  124. func (err ErrFilenameInvalid) Error() string {
  125. return fmt.Sprintf("path contains a malformed path component [path: %s]", err.Path)
  126. }
  127. func (err ErrFilenameInvalid) Unwrap() error {
  128. return util.ErrInvalidArgument
  129. }
  130. // CleanGitTreePath cleans a tree path for git, it returns an empty string the path is invalid (e.g.: contains ".git" part)
  131. func CleanGitTreePath(name string) string {
  132. name = util.PathJoinRel(name)
  133. // Git disallows any filenames to have a .git directory in them.
  134. for part := range strings.SplitSeq(name, "/") {
  135. if strings.EqualFold(part, ".git") {
  136. return ""
  137. }
  138. }
  139. if name == "." {
  140. name = ""
  141. }
  142. return name
  143. }