fork of go-git with some jj specific features
at main 4.1 kB view raw
1package git 2 3import ( 4 "bytes" 5 "fmt" 6 "path/filepath" 7 8 mindex "github.com/go-git/go-git/v5/utils/merkletrie/index" 9 "github.com/go-git/go-git/v5/utils/merkletrie/noder" 10) 11 12// Status represents the current status of a Worktree. 13// The key of the map is the path of the file. 14type Status map[string]*FileStatus 15 16// File returns the FileStatus for a given path, if the FileStatus doesn't 17// exists a new FileStatus is added to the map using the path as key. 18func (s Status) File(path string) *FileStatus { 19 if _, ok := (s)[path]; !ok { 20 s[path] = &FileStatus{Worktree: Untracked, Staging: Untracked} 21 } 22 23 return s[path] 24} 25 26// IsUntracked checks if file for given path is 'Untracked' 27func (s Status) IsUntracked(path string) bool { 28 stat, ok := (s)[filepath.ToSlash(path)] 29 return ok && stat.Worktree == Untracked 30} 31 32// IsClean returns true if all the files are in Unmodified status. 33func (s Status) IsClean() bool { 34 for _, status := range s { 35 if status.Worktree != Unmodified || status.Staging != Unmodified { 36 return false 37 } 38 } 39 40 return true 41} 42 43func (s Status) String() string { 44 buf := bytes.NewBuffer(nil) 45 for path, status := range s { 46 if status.Staging == Unmodified && status.Worktree == Unmodified { 47 continue 48 } 49 50 if status.Staging == Renamed { 51 path = fmt.Sprintf("%s -> %s", path, status.Extra) 52 } 53 54 fmt.Fprintf(buf, "%c%c %s\n", status.Staging, status.Worktree, path) 55 } 56 57 return buf.String() 58} 59 60// FileStatus contains the status of a file in the worktree 61type FileStatus struct { 62 // Staging is the status of a file in the staging area 63 Staging StatusCode 64 // Worktree is the status of a file in the worktree 65 Worktree StatusCode 66 // Extra contains extra information, such as the previous name in a rename 67 Extra string 68} 69 70// StatusCode status code of a file in the Worktree 71type StatusCode byte 72 73const ( 74 Unmodified StatusCode = ' ' 75 Untracked StatusCode = '?' 76 Modified StatusCode = 'M' 77 Added StatusCode = 'A' 78 Deleted StatusCode = 'D' 79 Renamed StatusCode = 'R' 80 Copied StatusCode = 'C' 81 UpdatedButUnmerged StatusCode = 'U' 82) 83 84// StatusStrategy defines the different types of strategies when processing 85// the worktree status. 86type StatusStrategy int 87 88const ( 89 // TODO: (V6) Review the default status strategy. 90 // TODO: (V6) Review the type used to represent Status, to enable lazy 91 // processing of statuses going direct to the backing filesystem. 92 defaultStatusStrategy = Empty 93 94 // Empty starts its status map from empty. Missing entries for a given 95 // path means that the file is untracked. This causes a known issue (#119) 96 // whereby unmodified files can be incorrectly reported as untracked. 97 // 98 // This can be used when returning the changed state within a modified Worktree. 99 // For example, to check whether the current worktree is clean. 100 Empty StatusStrategy = 0 101 // Preload goes through all existing nodes from the index and add them to the 102 // status map as unmodified. This is currently the most reliable strategy 103 // although it comes at a performance cost in large repositories. 104 // 105 // This method is recommended when fetching the status of unmodified files. 106 // For example, to confirm the status of a specific file that is either 107 // untracked or unmodified. 108 Preload StatusStrategy = 1 109) 110 111func (s StatusStrategy) new(w *Worktree) (Status, error) { 112 switch s { 113 case Preload: 114 return preloadStatus(w) 115 case Empty: 116 return make(Status), nil 117 } 118 return nil, fmt.Errorf("%w: %+v", ErrUnsupportedStatusStrategy, s) 119} 120 121func preloadStatus(w *Worktree) (Status, error) { 122 idx, err := w.r.Storer.Index() 123 if err != nil { 124 return nil, err 125 } 126 127 idxRoot := mindex.NewRootNode(idx) 128 nodes := []noder.Noder{idxRoot} 129 130 status := make(Status) 131 for len(nodes) > 0 { 132 var node noder.Noder 133 node, nodes = nodes[0], nodes[1:] 134 if node.IsDir() { 135 children, err := node.Children() 136 if err != nil { 137 return nil, err 138 } 139 nodes = append(nodes, children...) 140 continue 141 } 142 fs := status.File(node.Name()) 143 fs.Worktree = Unmodified 144 fs.Staging = Unmodified 145 } 146 147 return status, nil 148}