You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							104 lines
						
					
					
						
							2.8 KiB
						
					
					
				
			
		
		
	
	
							104 lines
						
					
					
						
							2.8 KiB
						
					
					
				| package git
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 
 | |
| 	"gopkg.in/src-d/go-git.v4/plumbing"
 | |
| 	"gopkg.in/src-d/go-git.v4/plumbing/filemode"
 | |
| 	"gopkg.in/src-d/go-git.v4/plumbing/object"
 | |
| 	"gopkg.in/src-d/go-git.v4/storage"
 | |
| )
 | |
| 
 | |
| type objectWalker struct {
 | |
| 	Storer storage.Storer
 | |
| 	// seen is the set of objects seen in the repo.
 | |
| 	// seen map can become huge if walking over large
 | |
| 	// repos. Thus using struct{} as the value type.
 | |
| 	seen map[plumbing.Hash]struct{}
 | |
| }
 | |
| 
 | |
| func newObjectWalker(s storage.Storer) *objectWalker {
 | |
| 	return &objectWalker{s, map[plumbing.Hash]struct{}{}}
 | |
| }
 | |
| 
 | |
| // walkAllRefs walks all (hash) refererences from the repo.
 | |
| func (p *objectWalker) walkAllRefs() error {
 | |
| 	// Walk over all the references in the repo.
 | |
| 	it, err := p.Storer.IterReferences()
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	defer it.Close()
 | |
| 	err = it.ForEach(func(ref *plumbing.Reference) error {
 | |
| 		// Exit this iteration early for non-hash references.
 | |
| 		if ref.Type() != plumbing.HashReference {
 | |
| 			return nil
 | |
| 		}
 | |
| 		return p.walkObjectTree(ref.Hash())
 | |
| 	})
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| func (p *objectWalker) isSeen(hash plumbing.Hash) bool {
 | |
| 	_, seen := p.seen[hash]
 | |
| 	return seen
 | |
| }
 | |
| 
 | |
| func (p *objectWalker) add(hash plumbing.Hash) {
 | |
| 	p.seen[hash] = struct{}{}
 | |
| }
 | |
| 
 | |
| // walkObjectTree walks over all objects and remembers references
 | |
| // to them in the objectWalker. This is used instead of the revlist
 | |
| // walks because memory usage is tight with huge repos.
 | |
| func (p *objectWalker) walkObjectTree(hash plumbing.Hash) error {
 | |
| 	// Check if we have already seen, and mark this object
 | |
| 	if p.isSeen(hash) {
 | |
| 		return nil
 | |
| 	}
 | |
| 	p.add(hash)
 | |
| 	// Fetch the object.
 | |
| 	obj, err := object.GetObject(p.Storer, hash)
 | |
| 	if err != nil {
 | |
| 		return fmt.Errorf("Getting object %s failed: %v", hash, err)
 | |
| 	}
 | |
| 	// Walk all children depending on object type.
 | |
| 	switch obj := obj.(type) {
 | |
| 	case *object.Commit:
 | |
| 		err = p.walkObjectTree(obj.TreeHash)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		for _, h := range obj.ParentHashes {
 | |
| 			err = p.walkObjectTree(h)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 		}
 | |
| 	case *object.Tree:
 | |
| 		for i := range obj.Entries {
 | |
| 			// Shortcut for blob objects:
 | |
| 			// 'or' the lower bits of a mode and check that it
 | |
| 			// it matches a filemode.Executable. The type information
 | |
| 			// is in the higher bits, but this is the cleanest way
 | |
| 			// to handle plain files with different modes.
 | |
| 			// Other non-tree objects are somewhat rare, so they
 | |
| 			// are not special-cased.
 | |
| 			if obj.Entries[i].Mode|0755 == filemode.Executable {
 | |
| 				p.add(obj.Entries[i].Hash)
 | |
| 				continue
 | |
| 			}
 | |
| 			// Normal walk for sub-trees (and symlinks etc).
 | |
| 			err = p.walkObjectTree(obj.Entries[i].Hash)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 		}
 | |
| 	case *object.Tag:
 | |
| 		return p.walkObjectTree(obj.Target)
 | |
| 	default:
 | |
| 		// Error out on unhandled object types.
 | |
| 		return fmt.Errorf("Unknown object %X %s %T\n", obj.ID(), obj.Type(), obj)
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 |