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