gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package git
  4. import (
  5. "bytes"
  6. "context"
  7. "os"
  8. "path/filepath"
  9. "strings"
  10. "code.gitea.io/gitea/modules/git/gitcmd"
  11. "code.gitea.io/gitea/modules/setting"
  12. )
  13. // ReadTreeToIndex reads a treeish to the index
  14. func (repo *Repository) ReadTreeToIndex(treeish string, indexFilename ...string) error {
  15. objectFormat, err := repo.GetObjectFormat()
  16. if err != nil {
  17. return err
  18. }
  19. if len(treeish) != objectFormat.FullLength() {
  20. res, _, err := gitcmd.NewCommand("rev-parse", "--verify").AddDynamicArguments(treeish).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
  21. if err != nil {
  22. return err
  23. }
  24. if len(res) > 0 {
  25. treeish = res[:len(res)-1]
  26. }
  27. }
  28. id, err := NewIDFromString(treeish)
  29. if err != nil {
  30. return err
  31. }
  32. return repo.readTreeToIndex(id, indexFilename...)
  33. }
  34. func (repo *Repository) readTreeToIndex(id ObjectID, indexFilename ...string) error {
  35. var env []string
  36. if len(indexFilename) > 0 {
  37. env = append(os.Environ(), "GIT_INDEX_FILE="+indexFilename[0])
  38. }
  39. _, _, err := gitcmd.NewCommand("read-tree").AddDynamicArguments(id.String()).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path, Env: env})
  40. if err != nil {
  41. return err
  42. }
  43. return nil
  44. }
  45. // ReadTreeToTemporaryIndex reads a treeish to a temporary index file
  46. func (repo *Repository) ReadTreeToTemporaryIndex(treeish string) (tmpIndexFilename, tmpDir string, cancel context.CancelFunc, err error) {
  47. defer func() {
  48. // if error happens and there is a cancel function, do clean up
  49. if err != nil && cancel != nil {
  50. cancel()
  51. cancel = nil
  52. }
  53. }()
  54. tmpDir, cancel, err = setting.AppDataTempDir("git-repo-content").MkdirTempRandom("index")
  55. if err != nil {
  56. return "", "", nil, err
  57. }
  58. tmpIndexFilename = filepath.Join(tmpDir, ".tmp-index")
  59. err = repo.ReadTreeToIndex(treeish, tmpIndexFilename)
  60. if err != nil {
  61. return "", "", cancel, err
  62. }
  63. return tmpIndexFilename, tmpDir, cancel, nil
  64. }
  65. // EmptyIndex empties the index
  66. func (repo *Repository) EmptyIndex() error {
  67. _, _, err := gitcmd.NewCommand("read-tree", "--empty").RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
  68. return err
  69. }
  70. // LsFiles checks if the given filenames are in the index
  71. func (repo *Repository) LsFiles(filenames ...string) ([]string, error) {
  72. cmd := gitcmd.NewCommand("ls-files", "-z").AddDashesAndList(filenames...)
  73. res, _, err := cmd.RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
  74. if err != nil {
  75. return nil, err
  76. }
  77. filelist := make([]string, 0, len(filenames))
  78. for line := range bytes.SplitSeq(res, []byte{'\000'}) {
  79. filelist = append(filelist, string(line))
  80. }
  81. return filelist, err
  82. }
  83. // RemoveFilesFromIndex removes given filenames from the index - it does not check whether they are present.
  84. func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error {
  85. objectFormat, err := repo.GetObjectFormat()
  86. if err != nil {
  87. return err
  88. }
  89. cmd := gitcmd.NewCommand("update-index", "--remove", "-z", "--index-info")
  90. stdout := new(bytes.Buffer)
  91. stderr := new(bytes.Buffer)
  92. buffer := new(bytes.Buffer)
  93. for _, file := range filenames {
  94. if file != "" {
  95. // using format: mode SP type SP sha1 TAB path
  96. buffer.WriteString("0 blob " + objectFormat.EmptyObjectID().String() + "\t" + file + "\000")
  97. }
  98. }
  99. return cmd.Run(repo.Ctx, &gitcmd.RunOpts{
  100. Dir: repo.Path,
  101. Stdin: bytes.NewReader(buffer.Bytes()),
  102. Stdout: stdout,
  103. Stderr: stderr,
  104. })
  105. }
  106. type IndexObjectInfo struct {
  107. Mode string
  108. Object ObjectID
  109. Filename string
  110. }
  111. // AddObjectsToIndex adds the provided object hashes to the index at the provided filenames
  112. func (repo *Repository) AddObjectsToIndex(objects ...IndexObjectInfo) error {
  113. cmd := gitcmd.NewCommand("update-index", "--add", "--replace", "-z", "--index-info")
  114. stdout := new(bytes.Buffer)
  115. stderr := new(bytes.Buffer)
  116. buffer := new(bytes.Buffer)
  117. for _, object := range objects {
  118. // using format: mode SP type SP sha1 TAB path
  119. buffer.WriteString(object.Mode + " blob " + object.Object.String() + "\t" + object.Filename + "\000")
  120. }
  121. return cmd.Run(repo.Ctx, &gitcmd.RunOpts{
  122. Dir: repo.Path,
  123. Stdin: bytes.NewReader(buffer.Bytes()),
  124. Stdout: stdout,
  125. Stderr: stderr,
  126. })
  127. }
  128. // AddObjectToIndex adds the provided object hash to the index at the provided filename
  129. func (repo *Repository) AddObjectToIndex(mode string, object ObjectID, filename string) error {
  130. return repo.AddObjectsToIndex(IndexObjectInfo{Mode: mode, Object: object, Filename: filename})
  131. }
  132. // WriteTree writes the current index as a tree to the object db and returns its hash
  133. func (repo *Repository) WriteTree() (*Tree, error) {
  134. stdout, _, runErr := gitcmd.NewCommand("write-tree").RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
  135. if runErr != nil {
  136. return nil, runErr
  137. }
  138. id, err := NewIDFromString(strings.TrimSpace(stdout))
  139. if err != nil {
  140. return nil, err
  141. }
  142. return NewTree(repo, id), nil
  143. }