fork of go-git with some jj specific features
at v4.8.1 2.8 kB view raw
1package git 2 3import ( 4 "fmt" 5 6 "gopkg.in/src-d/go-git.v4/plumbing" 7 "gopkg.in/src-d/go-git.v4/plumbing/filemode" 8 "gopkg.in/src-d/go-git.v4/plumbing/object" 9 "gopkg.in/src-d/go-git.v4/storage" 10) 11 12type objectWalker struct { 13 Storer storage.Storer 14 // seen is the set of objects seen in the repo. 15 // seen map can become huge if walking over large 16 // repos. Thus using struct{} as the value type. 17 seen map[plumbing.Hash]struct{} 18} 19 20func newObjectWalker(s storage.Storer) *objectWalker { 21 return &objectWalker{s, map[plumbing.Hash]struct{}{}} 22} 23 24// walkAllRefs walks all (hash) refererences from the repo. 25func (p *objectWalker) walkAllRefs() error { 26 // Walk over all the references in the repo. 27 it, err := p.Storer.IterReferences() 28 if err != nil { 29 return err 30 } 31 defer it.Close() 32 err = it.ForEach(func(ref *plumbing.Reference) error { 33 // Exit this iteration early for non-hash references. 34 if ref.Type() != plumbing.HashReference { 35 return nil 36 } 37 return p.walkObjectTree(ref.Hash()) 38 }) 39 return err 40} 41 42func (p *objectWalker) isSeen(hash plumbing.Hash) bool { 43 _, seen := p.seen[hash] 44 return seen 45} 46 47func (p *objectWalker) add(hash plumbing.Hash) { 48 p.seen[hash] = struct{}{} 49} 50 51// walkObjectTree walks over all objects and remembers references 52// to them in the objectWalker. This is used instead of the revlist 53// walks because memory usage is tight with huge repos. 54func (p *objectWalker) walkObjectTree(hash plumbing.Hash) error { 55 // Check if we have already seen, and mark this object 56 if p.isSeen(hash) { 57 return nil 58 } 59 p.add(hash) 60 // Fetch the object. 61 obj, err := object.GetObject(p.Storer, hash) 62 if err != nil { 63 return fmt.Errorf("Getting object %s failed: %v", hash, err) 64 } 65 // Walk all children depending on object type. 66 switch obj := obj.(type) { 67 case *object.Commit: 68 err = p.walkObjectTree(obj.TreeHash) 69 if err != nil { 70 return err 71 } 72 for _, h := range obj.ParentHashes { 73 err = p.walkObjectTree(h) 74 if err != nil { 75 return err 76 } 77 } 78 case *object.Tree: 79 for i := range obj.Entries { 80 // Shortcut for blob objects: 81 // 'or' the lower bits of a mode and check that it 82 // it matches a filemode.Executable. The type information 83 // is in the higher bits, but this is the cleanest way 84 // to handle plain files with different modes. 85 // Other non-tree objects are somewhat rare, so they 86 // are not special-cased. 87 if obj.Entries[i].Mode|0755 == filemode.Executable { 88 p.add(obj.Entries[i].Hash) 89 continue 90 } 91 // Normal walk for sub-trees (and symlinks etc). 92 err = p.walkObjectTree(obj.Entries[i].Hash) 93 if err != nil { 94 return err 95 } 96 } 97 case *object.Tag: 98 return p.walkObjectTree(obj.Target) 99 default: 100 // Error out on unhandled object types. 101 return fmt.Errorf("Unknown object %X %s %T\n", obj.ID(), obj.Type(), obj) 102 } 103 return nil 104}