package packfile import "fmt" type Packfile struct { Version uint32 Size int64 ObjectCount int Checksum []byte Commits map[Hash]*Commit Trees map[Hash]*Tree Blobs map[Hash]*Blob } func NewPackfile() *Packfile { return &Packfile{ Commits: make(map[Hash]*Commit, 0), Trees: make(map[Hash]*Tree, 0), Blobs: make(map[Hash]*Blob, 0), } } type BlobEntry struct { path string *Blob } type SubtreeEntry struct { path string *Tree TreeCh } type treeEntry interface { isTreeEntry() Path() string } func (b BlobEntry) isTreeEntry() {} func (b BlobEntry) Path() string { return b.path } func (b SubtreeEntry) isTreeEntry() {} func (b SubtreeEntry) Path() string { return b.path } type TreeCh <-chan treeEntry func (p *Packfile) WalkCommit(commitHash Hash) (TreeCh, error) { commit, ok := p.Commits[commitHash] if !ok { return nil, fmt.Errorf("Unable to find %q commit", commitHash) } return p.WalkTree(p.Trees[commit.Tree]), nil } func (p *Packfile) WalkTree(tree *Tree) TreeCh { return p.walkTree(tree, "") } func (p *Packfile) walkTree(tree *Tree, pathPrefix string) TreeCh { ch := make(chan treeEntry) if tree == nil { close(ch) return ch } go func() { defer func() { close(ch) }() for _, e := range tree.Entries { path := pathPrefix + e.Name if blob, ok := p.Blobs[e.Hash]; ok { ch <- BlobEntry{path, blob} } else if subtree, ok := p.Trees[e.Hash]; ok { ch <- SubtreeEntry{path, subtree, p.walkTree(subtree, path+"/")} } } }() return ch }