fork of go-git with some jj specific features
at v4.1.1 4.3 kB view raw
1package git 2 3import ( 4 "path" 5 "strings" 6 7 "gopkg.in/src-d/go-git.v4/plumbing" 8 "gopkg.in/src-d/go-git.v4/plumbing/filemode" 9 "gopkg.in/src-d/go-git.v4/plumbing/format/index" 10 "gopkg.in/src-d/go-git.v4/plumbing/object" 11 "gopkg.in/src-d/go-git.v4/storage" 12 13 "gopkg.in/src-d/go-billy.v4" 14) 15 16// Commit stores the current contents of the index in a new commit along with 17// a log message from the user describing the changes. 18func (w *Worktree) Commit(msg string, opts *CommitOptions) (plumbing.Hash, error) { 19 if err := opts.Validate(w.r); err != nil { 20 return plumbing.ZeroHash, err 21 } 22 23 if opts.All { 24 if err := w.autoAddModifiedAndDeleted(); err != nil { 25 return plumbing.ZeroHash, err 26 } 27 } 28 29 idx, err := w.r.Storer.Index() 30 if err != nil { 31 return plumbing.ZeroHash, err 32 } 33 34 h := &buildTreeHelper{ 35 fs: w.Filesystem, 36 s: w.r.Storer, 37 } 38 39 tree, err := h.BuildTree(idx) 40 if err != nil { 41 return plumbing.ZeroHash, err 42 } 43 44 commit, err := w.buildCommitObject(msg, opts, tree) 45 if err != nil { 46 return plumbing.ZeroHash, err 47 } 48 49 return commit, w.updateHEAD(commit) 50} 51 52func (w *Worktree) autoAddModifiedAndDeleted() error { 53 s, err := w.Status() 54 if err != nil { 55 return err 56 } 57 58 for path, fs := range s { 59 if fs.Worktree != Modified && fs.Worktree != Deleted { 60 continue 61 } 62 63 if _, err := w.Add(path); err != nil { 64 return err 65 } 66 67 } 68 69 return nil 70} 71 72func (w *Worktree) updateHEAD(commit plumbing.Hash) error { 73 head, err := w.r.Storer.Reference(plumbing.HEAD) 74 if err != nil { 75 return err 76 } 77 78 name := plumbing.HEAD 79 if head.Type() != plumbing.HashReference { 80 name = head.Target() 81 } 82 83 ref := plumbing.NewHashReference(name, commit) 84 return w.r.Storer.SetReference(ref) 85} 86 87func (w *Worktree) buildCommitObject(msg string, opts *CommitOptions, tree plumbing.Hash) (plumbing.Hash, error) { 88 commit := &object.Commit{ 89 Author: *opts.Author, 90 Committer: *opts.Committer, 91 Message: msg, 92 TreeHash: tree, 93 ParentHashes: opts.Parents, 94 } 95 96 obj := w.r.Storer.NewEncodedObject() 97 if err := commit.Encode(obj); err != nil { 98 return plumbing.ZeroHash, err 99 } 100 return w.r.Storer.SetEncodedObject(obj) 101} 102 103// buildTreeHelper converts a given index.Index file into multiple git objects 104// reading the blobs from the given filesystem and creating the trees from the 105// index structure. The created objects are pushed to a given Storer. 106type buildTreeHelper struct { 107 fs billy.Filesystem 108 s storage.Storer 109 110 trees map[string]*object.Tree 111 entries map[string]*object.TreeEntry 112} 113 114// BuildTree builds the tree objects and push its to the storer, the hash 115// of the root tree is returned. 116func (h *buildTreeHelper) BuildTree(idx *index.Index) (plumbing.Hash, error) { 117 const rootNode = "" 118 h.trees = map[string]*object.Tree{rootNode: {}} 119 h.entries = map[string]*object.TreeEntry{} 120 121 for _, e := range idx.Entries { 122 if err := h.commitIndexEntry(e); err != nil { 123 return plumbing.ZeroHash, err 124 } 125 } 126 127 return h.copyTreeToStorageRecursive(rootNode, h.trees[rootNode]) 128} 129 130func (h *buildTreeHelper) commitIndexEntry(e *index.Entry) error { 131 parts := strings.Split(e.Name, "/") 132 133 var fullpath string 134 for _, part := range parts { 135 parent := fullpath 136 fullpath = path.Join(fullpath, part) 137 138 h.doBuildTree(e, parent, fullpath) 139 } 140 141 return nil 142} 143 144func (h *buildTreeHelper) doBuildTree(e *index.Entry, parent, fullpath string) { 145 if _, ok := h.trees[fullpath]; ok { 146 return 147 } 148 149 if _, ok := h.entries[fullpath]; ok { 150 return 151 } 152 153 te := object.TreeEntry{Name: path.Base(fullpath)} 154 155 if fullpath == e.Name { 156 te.Mode = e.Mode 157 te.Hash = e.Hash 158 } else { 159 te.Mode = filemode.Dir 160 h.trees[fullpath] = &object.Tree{} 161 } 162 163 h.trees[parent].Entries = append(h.trees[parent].Entries, te) 164} 165 166func (h *buildTreeHelper) copyTreeToStorageRecursive(parent string, t *object.Tree) (plumbing.Hash, error) { 167 for i, e := range t.Entries { 168 if e.Mode != filemode.Dir && !e.Hash.IsZero() { 169 continue 170 } 171 172 path := path.Join(parent, e.Name) 173 174 var err error 175 e.Hash, err = h.copyTreeToStorageRecursive(path, h.trees[path]) 176 if err != nil { 177 return plumbing.ZeroHash, err 178 } 179 180 t.Entries[i] = e 181 } 182 183 o := h.s.NewEncodedObject() 184 if err := t.Encode(o); err != nil { 185 return plumbing.ZeroHash, err 186 } 187 188 return h.s.SetEncodedObject(o) 189}