| package git |
| |
| import ( |
| "bufio" |
| "io" |
| "os" |
| "path/filepath" |
| "strconv" |
| |
| "gopkg.in/src-d/go-git.v2/core" |
| ) |
| |
| // Tree is basically like a directory - it references a bunch of other trees |
| // and/or blobs (i.e. files and sub-directories) |
| type Tree struct { |
| Entries []TreeEntry |
| Hash core.Hash |
| |
| r *Repository |
| } |
| |
| // TreeEntry represents a file |
| type TreeEntry struct { |
| Name string |
| Mode os.FileMode |
| Hash core.Hash |
| } |
| |
| func (t *Tree) Files() chan *File { |
| ch := make(chan *File, 1) |
| |
| go func() { |
| defer func() { close(ch) }() |
| t.walkEntries("", ch) |
| }() |
| |
| return ch |
| } |
| |
| func (t *Tree) walkEntries(base string, ch chan *File) { |
| for _, entry := range t.Entries { |
| obj, ok := t.r.Storage.Get(entry.Hash) |
| if !ok { |
| continue // ignore entries without hash (= submodule dirs) |
| } |
| |
| if obj.Type() == core.TreeObject { |
| tree := &Tree{r: t.r} |
| tree.Decode(obj) |
| tree.walkEntries(filepath.Join(base, entry.Name), ch) |
| continue |
| } |
| |
| blob := &Blob{} |
| blob.Decode(obj) |
| |
| ch <- &File{Name: filepath.Join(base, entry.Name), Reader: blob.Reader(), Hash: entry.Hash} |
| } |
| } |
| |
| // Decode transform an core.Object into a Tree struct |
| func (t *Tree) Decode(o core.Object) error { |
| t.Hash = o.Hash() |
| if o.Size() == 0 { |
| return nil |
| } |
| |
| r := bufio.NewReader(o.Reader()) |
| for { |
| mode, err := r.ReadString(' ') |
| if err != nil { |
| if err == io.EOF { |
| break |
| } |
| |
| return err |
| } |
| |
| fm, err := strconv.ParseInt(mode[:len(mode)-1], 8, 32) |
| if err != nil && err != io.EOF { |
| return err |
| } |
| |
| name, err := r.ReadString(0) |
| if err != nil && err != io.EOF { |
| return err |
| } |
| |
| var hash core.Hash |
| _, err = r.Read(hash[:]) |
| if err != nil && err != io.EOF { |
| return err |
| } |
| |
| t.Entries = append(t.Entries, TreeEntry{ |
| Hash: hash, |
| Mode: os.FileMode(fm), |
| Name: name[:len(name)-1], |
| }) |
| } |
| |
| return nil |
| } |
| |
| type TreeIter struct { |
| iter |
| } |
| |
| func NewTreeIter(r *Repository) *TreeIter { |
| return &TreeIter{newIter(r)} |
| } |
| |
| func (i *TreeIter) Next() (*Tree, error) { |
| obj := <-i.ch |
| if obj == nil { |
| return nil, io.EOF |
| } |
| |
| tree := &Tree{r: i.r} |
| return tree, tree.Decode(obj) |
| } |