github.com/driusan/dgit@v0.0.0-20221118233547-f39f0c15edbb/git/updateindex.go (about) 1 package git 2 3 import ( 4 "bufio" 5 "fmt" 6 "io" 7 "strings" 8 ) 9 10 // A BitSetter denotes an option for a flag which sets or unsets 11 // a bit. It determines both whether the bit should be set, and 12 // the value it should be set to. 13 type BitSetter struct { 14 // If true, the bit should be modified 15 Modify bool 16 17 // Value to set the bit to if Modify is true. 18 Value bool 19 } 20 21 type CacheInfo struct { 22 Mode EntryMode 23 Sha1 Sha1 24 Path IndexPath 25 } 26 27 type UpdateIndexOptions struct { 28 Add, Remove bool 29 Refresh, ReallyRefresh bool 30 Quiet bool 31 IgnoreSubmodules bool 32 33 IndexVersion int 34 35 // Read the index information from IndexInfo 36 IndexInfo io.Reader 37 38 Unmerged bool 39 IgnoreMissing bool 40 41 Again bool 42 Unresolve bool 43 InfoOnly bool 44 ForceRemove bool 45 Replace bool 46 Verbose bool 47 NullTerminate bool 48 Chmod BitSetter 49 SplitIndex BitSetter 50 UntrackedCache BitSetter 51 SkipWorktree BitSetter 52 AssumeUnchanged BitSetter 53 54 CacheInfo CacheInfo 55 56 Stdin io.Reader 57 58 // With the official git client "git add -v" saysing "remove 'foo'" 59 // while "git update-index --verbose" says "add 'foo'" when removing 60 // a file. This is a hack so that we can have the same behaviour without 61 // having to duplicate code. 62 correctRemoveMsg bool 63 } 64 65 // This implements the git update-index command. It updates the index 66 // passed as a parameter, and returns it. It does *not* write it to 67 // disk, that's the responsibility of the caller. 68 func UpdateIndex(c *Client, idx *Index, opts UpdateIndexOptions, files []File) (*Index, error) { 69 if opts.IndexInfo != nil { 70 return UpdateIndexFromReader(c, opts, opts.IndexInfo) 71 } 72 if opts.Refresh { 73 return UpdateIndexRefresh(c, idx, opts) 74 } 75 if opts.CacheInfo != (CacheInfo{}) { 76 return UpdateIndexCacheInfo(c, idx, opts) 77 } 78 for _, file := range files { 79 ipath, err := file.IndexPath(c) 80 if err != nil { 81 return nil, err 82 } 83 if file.IsDir() { 84 if opts.Remove || opts.ForceRemove { 85 entries, err := LsFiles(c, LsFilesOptions{Cached: true}, []File{file}) 86 if err != nil { 87 return nil, err 88 } 89 for _, i := range entries { 90 idx.RemoveFile(i.PathName) 91 } 92 continue 93 } else if opts.Add { 94 return nil, fmt.Errorf("%v is a directory - add files inside instead", file) 95 } 96 } 97 if !file.Exists() || opts.ForceRemove { 98 if opts.Remove || opts.ForceRemove { 99 idx.RemoveFile(ipath) 100 if opts.Verbose { 101 if opts.correctRemoveMsg { 102 fmt.Printf("remove '%v'\n", file.String()) 103 } else { 104 fmt.Printf("add '%v'\n", file.String()) 105 } 106 } 107 } else { 108 return nil, fmt.Errorf("%v does not exist and --remove not passed", file) 109 } 110 } else if err := idx.AddFile(c, file, opts); err != nil { 111 if !opts.Add { 112 // This is making invalid assumptions that the only 113 // thing that might go wrong is that createEntry was 114 // false and the file isn't in the index 115 return nil, fmt.Errorf("Can not add %v to index. Missing --add?", file) 116 } 117 // If add was true and something went else went wrong, 118 // return the error 119 return nil, err 120 } 121 if opts.Verbose { 122 fmt.Printf("add '%v'\n", file) 123 } 124 if opts.SkipWorktree.Modify { 125 idx.SetSkipWorktree(c, ipath, opts.SkipWorktree.Value) 126 } 127 } 128 return idx, nil 129 } 130 131 func UpdateIndexFromReader(c *Client, opts UpdateIndexOptions, r io.Reader) (*Index, error) { 132 idx := NewIndex() 133 scanner := bufio.NewScanner(r) 134 for scanner.Scan() { 135 line := strings.TrimSpace(scanner.Text()) 136 if line == "" { 137 continue 138 } 139 tab := strings.Split(line, "\t") 140 if len(tab) != 2 { 141 return nil, fmt.Errorf("Invalid line in index-info: %v", line) 142 } 143 path := tab[1] 144 spaces := strings.Split(tab[0], " ") 145 switch len(spaces) { 146 case 2: 147 // mode SP sha1 TAB path 148 sha1, err := Sha1FromString(spaces[1]) 149 if err != nil { 150 return nil, err 151 } 152 if spaces[0] == "0" { 153 // Mode "0" means remove index 154 idx.RemoveFile(IndexPath(path)) 155 } else { 156 mode, err := ModeFromString(spaces[0]) 157 if err != nil { 158 return nil, err 159 } 160 if err := idx.AddStage(c, IndexPath(path), mode, sha1, Stage0, 0, 0, UpdateIndexOptions{Add: true}); err != nil { 161 return nil, err 162 } 163 } 164 case 3: 165 switch len(spaces[1]) { 166 case 40: 167 // mode SP sha1 SP stage TAB path 168 mode, err := ModeFromString(spaces[0]) 169 if err != nil { 170 return nil, err 171 } 172 sha1, err := Sha1FromString(spaces[1]) 173 if err != nil { 174 return nil, err 175 } 176 var stage Stage 177 switch spaces[2] { 178 case "0": 179 stage = Stage0 180 case "1": 181 stage = Stage1 182 case "2": 183 stage = Stage2 184 case "3": 185 stage = Stage3 186 default: 187 return nil, fmt.Errorf("Invalid stage: %v", spaces[2]) 188 } 189 if err := idx.AddStage(c, IndexPath(path), mode, sha1, stage, 0, 0, UpdateIndexOptions{Add: true}); err != nil { 190 return nil, err 191 } 192 default: 193 // mode SP type SP sha1 TAB path 194 mode, err := ModeFromString(spaces[0]) 195 if err != nil { 196 return nil, err 197 } 198 if mode.TreeType() != spaces[1] { 199 return nil, fmt.Errorf("Mode does not match type: %v", line) 200 } 201 sha1, err := Sha1FromString(spaces[2]) 202 if err != nil { 203 return nil, err 204 } 205 if err := idx.AddStage(c, IndexPath(path), mode, sha1, Stage0, 0, 0, UpdateIndexOptions{Add: true}); err != nil { 206 return nil, err 207 } 208 } 209 default: 210 return nil, fmt.Errorf("Invalid line in index-info: %v", line) 211 } 212 } 213 if err := scanner.Err(); err != nil { 214 return nil, err 215 } 216 return idx, nil 217 } 218 219 func UpdateIndexRefresh(c *Client, idx *Index, opts UpdateIndexOptions) (*Index, error) { 220 for _, entry := range idx.Objects { 221 f, err := entry.PathName.FilePath(c) 222 if err != nil { 223 return nil, err 224 } 225 if !f.Exists() { 226 // Nothing to refresh 227 continue 228 } 229 if err := entry.RefreshStat(c); err != nil { 230 return nil, err 231 } 232 } 233 return idx, nil 234 } 235 236 func UpdateIndexCacheInfo(c *Client, idx *Index, opts UpdateIndexOptions) (*Index, error) { 237 err := idx.AddStage(c, opts.CacheInfo.Path, opts.CacheInfo.Mode, opts.CacheInfo.Sha1, Stage0, 0, 0, opts) 238 return idx, err 239 }