gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. // Copyright 2021 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package files
  4. import (
  5. "context"
  6. "fmt"
  7. "strings"
  8. git_model "code.gitea.io/gitea/models/git"
  9. repo_model "code.gitea.io/gitea/models/repo"
  10. user_model "code.gitea.io/gitea/models/user"
  11. "code.gitea.io/gitea/modules/git"
  12. "code.gitea.io/gitea/modules/git/gitcmd"
  13. "code.gitea.io/gitea/modules/log"
  14. "code.gitea.io/gitea/modules/structs"
  15. "code.gitea.io/gitea/modules/util"
  16. asymkey_service "code.gitea.io/gitea/services/asymkey"
  17. )
  18. // ErrUserCannotCommit represents "UserCannotCommit" kind of error.
  19. type ErrUserCannotCommit struct {
  20. UserName string
  21. }
  22. // IsErrUserCannotCommit checks if an error is an ErrUserCannotCommit.
  23. func IsErrUserCannotCommit(err error) bool {
  24. _, ok := err.(ErrUserCannotCommit)
  25. return ok
  26. }
  27. func (err ErrUserCannotCommit) Error() string {
  28. return fmt.Sprintf("user cannot commit to repo [user: %s]", err.UserName)
  29. }
  30. func (err ErrUserCannotCommit) Unwrap() error {
  31. return util.ErrPermissionDenied
  32. }
  33. // ApplyDiffPatchOptions holds the repository diff patch update options
  34. type ApplyDiffPatchOptions struct {
  35. LastCommitID string
  36. OldBranch string
  37. NewBranch string
  38. Message string
  39. Content string
  40. Author *IdentityOptions
  41. Committer *IdentityOptions
  42. Dates *CommitDateOptions
  43. Signoff bool
  44. }
  45. // Validate validates the provided options
  46. func (opts *ApplyDiffPatchOptions) Validate(ctx context.Context, repo *repo_model.Repository, doer *user_model.User) error {
  47. // If no branch name is set, assume master
  48. if opts.OldBranch == "" {
  49. opts.OldBranch = repo.DefaultBranch
  50. }
  51. if opts.NewBranch == "" {
  52. opts.NewBranch = opts.OldBranch
  53. }
  54. // oldBranch must exist for this operation
  55. if exist, err := git_model.IsBranchExist(ctx, repo.ID, opts.OldBranch); err != nil {
  56. return err
  57. } else if !exist {
  58. return git_model.ErrBranchNotExist{
  59. BranchName: opts.OldBranch,
  60. }
  61. }
  62. // A NewBranch can be specified for the patch to be applied to.
  63. // Check to make sure the branch does not already exist, otherwise we can't proceed.
  64. // If we aren't branching to a new branch, make sure user can commit to the given branch
  65. if opts.NewBranch != opts.OldBranch {
  66. exist, err := git_model.IsBranchExist(ctx, repo.ID, opts.NewBranch)
  67. if err != nil {
  68. return err
  69. } else if exist {
  70. return git_model.ErrBranchAlreadyExists{
  71. BranchName: opts.NewBranch,
  72. }
  73. }
  74. } else {
  75. protectedBranch, err := git_model.GetFirstMatchProtectedBranchRule(ctx, repo.ID, opts.OldBranch)
  76. if err != nil {
  77. return err
  78. }
  79. if protectedBranch != nil {
  80. protectedBranch.Repo = repo
  81. if !protectedBranch.CanUserPush(ctx, doer) {
  82. return ErrUserCannotCommit{
  83. UserName: doer.LowerName,
  84. }
  85. }
  86. }
  87. if protectedBranch != nil && protectedBranch.RequireSignedCommits {
  88. _, _, _, err := asymkey_service.SignCRUDAction(ctx, repo.RepoPath(), doer, repo.RepoPath(), opts.OldBranch)
  89. if err != nil {
  90. if !asymkey_service.IsErrWontSign(err) {
  91. return err
  92. }
  93. return ErrUserCannotCommit{
  94. UserName: doer.LowerName,
  95. }
  96. }
  97. }
  98. }
  99. return nil
  100. }
  101. // ApplyDiffPatch applies a patch to the given repository
  102. func ApplyDiffPatch(ctx context.Context, repo *repo_model.Repository, doer *user_model.User, opts *ApplyDiffPatchOptions) (*structs.FileResponse, error) {
  103. err := repo.MustNotBeArchived()
  104. if err != nil {
  105. return nil, err
  106. }
  107. if err := opts.Validate(ctx, repo, doer); err != nil {
  108. return nil, err
  109. }
  110. message := strings.TrimSpace(opts.Message)
  111. t, err := NewTemporaryUploadRepository(repo)
  112. if err != nil {
  113. log.Error("NewTemporaryUploadRepository failed: %v", err)
  114. }
  115. defer t.Close()
  116. if err := t.Clone(ctx, opts.OldBranch, true); err != nil {
  117. return nil, err
  118. }
  119. if err := t.SetDefaultIndex(ctx); err != nil {
  120. return nil, err
  121. }
  122. // Get the commit of the original branch
  123. commit, err := t.GetBranchCommit(opts.OldBranch)
  124. if err != nil {
  125. return nil, err // Couldn't get a commit for the branch
  126. }
  127. // Assigned LastCommitID in opts if it hasn't been set
  128. if opts.LastCommitID == "" {
  129. opts.LastCommitID = commit.ID.String()
  130. } else {
  131. lastCommitID, err := t.gitRepo.ConvertToGitID(opts.LastCommitID)
  132. if err != nil {
  133. return nil, fmt.Errorf("ApplyPatch: Invalid last commit ID: %w", err)
  134. }
  135. opts.LastCommitID = lastCommitID.String()
  136. if commit.ID.String() != opts.LastCommitID {
  137. return nil, ErrCommitIDDoesNotMatch{
  138. GivenCommitID: opts.LastCommitID,
  139. CurrentCommitID: opts.LastCommitID,
  140. }
  141. }
  142. }
  143. stdout := &strings.Builder{}
  144. stderr := &strings.Builder{}
  145. cmdApply := gitcmd.NewCommand("apply", "--index", "--recount", "--cached", "--ignore-whitespace", "--whitespace=fix", "--binary")
  146. if git.DefaultFeatures().CheckVersionAtLeast("2.32") {
  147. cmdApply.AddArguments("-3")
  148. }
  149. if err := cmdApply.Run(ctx, &gitcmd.RunOpts{
  150. Dir: t.basePath,
  151. Stdout: stdout,
  152. Stderr: stderr,
  153. Stdin: strings.NewReader(opts.Content),
  154. }); err != nil {
  155. return nil, fmt.Errorf("Error: Stdout: %s\nStderr: %s\nErr: %w", stdout.String(), stderr.String(), err)
  156. }
  157. // Now write the tree
  158. treeHash, err := t.WriteTree(ctx)
  159. if err != nil {
  160. return nil, err
  161. }
  162. // Now commit the tree
  163. commitOpts := &CommitTreeUserOptions{
  164. ParentCommitID: "HEAD",
  165. TreeHash: treeHash,
  166. CommitMessage: message,
  167. SignOff: opts.Signoff,
  168. DoerUser: doer,
  169. AuthorIdentity: opts.Author,
  170. AuthorTime: nil,
  171. CommitterIdentity: opts.Committer,
  172. CommitterTime: nil,
  173. }
  174. if opts.Dates != nil {
  175. commitOpts.AuthorTime, commitOpts.CommitterTime = &opts.Dates.Author, &opts.Dates.Committer
  176. }
  177. commitHash, err := t.CommitTree(ctx, commitOpts)
  178. if err != nil {
  179. return nil, err
  180. }
  181. // Then push this tree to NewBranch
  182. if err := t.Push(ctx, doer, commitHash, opts.NewBranch); err != nil {
  183. return nil, err
  184. }
  185. commit, err = t.GetCommit(commitHash)
  186. if err != nil {
  187. return nil, err
  188. }
  189. fileCommitResponse, _ := GetFileCommitResponse(repo, commit) // ok if fails, then will be nil
  190. verification := GetPayloadCommitVerification(ctx, commit)
  191. fileResponse := &structs.FileResponse{
  192. Commit: fileCommitResponse,
  193. Verification: verification,
  194. }
  195. return fileResponse, nil
  196. }