From 07e4c4368921f73048578f22a14a1c671ec3ba46 Mon Sep 17 00:00:00 2001 From: Alberto Cortés Date: Thu, 21 Jan 2016 03:00:41 +0100 Subject: Fix commit.File() gorutine leak Commit.File() was leaking a goroutine because it was looping over an iterator without closing its channel. Now commit.File() calls the new Tree.File() method that searches the file in the repository by trasversing the dir tree instead of using the tree.Files() iterator. This not only prevent the goroutine leak, but also speeds up file searching. --- tree.go | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) (limited to 'tree.go') diff --git a/tree.go b/tree.go index 7b988a1..232bfc2 100644 --- a/tree.go +++ b/tree.go @@ -2,10 +2,12 @@ package git import ( "bufio" + "errors" "io" "os" "path/filepath" "strconv" + "strings" "gopkg.in/src-d/go-git.v2/core" ) @@ -26,6 +28,84 @@ type TreeEntry struct { Hash core.Hash } +// New errors defined by this package. +var ErrFileNotFound = errors.New("file not found") + +func (t *Tree) File(path string) (*File, error) { + hash, err := t.hashOf(path) + if err != nil { + return nil, ErrFileNotFound + } + + obj, ok := t.r.Storage.Get(*hash) + if !ok { + return nil, ErrFileNotFound // a git submodule + } + + if obj.Type() != core.BlobObject { + return nil, ErrFileNotFound // a directory + } + + blob := &Blob{} + blob.Decode(obj) + + return &File{Name: path, Reader: blob.Reader(), Hash: *hash}, nil +} + +func (t *Tree) hashOf(path string) (*core.Hash, error) { + pathParts := strings.Split(path, "/") + + var tree *Tree + var err error + for tree = t; len(pathParts) > 1; pathParts = pathParts[1:] { + if tree, err = tree.dir(pathParts[0]); err != nil { + return nil, err + } + } + + entry, err := tree.entry(pathParts[0]) + if err != nil { + return nil, err + } + + return &entry.Hash, nil +} + +var errDirNotFound = errors.New("directory not found") + +func (t *Tree) dir(baseName string) (*Tree, error) { + entry, err := t.entry(baseName) + if err != nil { + return nil, errDirNotFound + } + + obj, ok := t.r.Storage.Get(entry.Hash) + if !ok { // git submodule + return nil, errDirNotFound + } + + if obj.Type() != core.TreeObject { + return nil, errDirNotFound // a file + } + + tree := &Tree{r: t.r} + tree.Decode(obj) + + return tree, nil +} + +var errEntryNotFound = errors.New("entry not found") + +func (t *Tree) entry(baseName string) (*TreeEntry, error) { + for _, entry := range t.Entries { + if entry.Name == baseName { + return &entry, nil + } + } + + return nil, errEntryNotFound +} + func (t *Tree) Files() chan *File { ch := make(chan *File, 1) -- cgit