Use local repo to get tree data if available.

Fix some git2go memory leaks.

Change-Id: If0f4bd865436f92f7e40dc482428ac812666f74e
diff --git a/cache/gitcache.go b/cache/gitcache.go
index a24cba4..b79c9f1 100644
--- a/cache/gitcache.go
+++ b/cache/gitcache.go
@@ -119,6 +119,20 @@
 	return runErr
 }
 
+// OpenLocal returns an opened repository for the given URL, if it is available locally.
+func (c *gitCache) OpenLocal(url string) *git.Repository {
+	p, err := c.gitPath(url)
+	if err != nil {
+		return nil
+	}
+
+	repo, err := git.OpenRepository(p)
+	if err != nil {
+		return nil
+	}
+	return repo
+}
+
 // Open returns an opened repository for the given URL. If necessary,
 // the repository is cloned.
 func (c *gitCache) Open(url string) (*git.Repository, error) {
diff --git a/cache/gitcache_test.go b/cache/gitcache_test.go
index 6d74ca4..8d4a99f 100644
--- a/cache/gitcache_test.go
+++ b/cache/gitcache_test.go
@@ -1,7 +1,22 @@
+// Copyright 2016 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package cache
 
 import (
 	"io/ioutil"
+	"os"
 	"testing"
 	"time"
 )
@@ -17,6 +32,7 @@
 	if err != nil {
 		t.Fatalf("TempDir: %v", err)
 	}
+	defer os.RemoveAll(dir)
 
 	cache, err := newGitCache(dir)
 	if err != nil {
@@ -24,6 +40,9 @@
 	}
 
 	url := "file://" + testRepo.dir
+	if r := cache.OpenLocal(url); r != nil {
+		t.Errorf("OpenLocal(%s) succeeded", url)
+	}
 
 	lazy := newLazyRepo(url, cache)
 	if r := lazy.Repository(); r != nil {
@@ -44,4 +63,8 @@
 	if repo := lazy.Repository(); repo == nil {
 		t.Errorf("lazyRepo still not loaded after %s.", dt)
 	}
+
+	if r := cache.OpenLocal(url); r == nil {
+		t.Errorf("OpenLocal(%s) failed", url)
+	}
 }
diff --git a/cache/treecache.go b/cache/treecache.go
index 2ea5f8a..1435f13 100644
--- a/cache/treecache.go
+++ b/cache/treecache.go
@@ -108,13 +108,15 @@
 	if err != nil {
 		return nil, err
 	}
+	defer obj.Free()
 
-	obj, err = obj.Peel(git.ObjectTree)
+	peeledObj, err := obj.Peel(git.ObjectTree)
 	if err != nil {
 		return nil, err
 	}
+	defer peeledObj.Free()
 
-	asTree, err := obj.AsTree()
+	asTree, err := peeledObj.AsTree()
 	if err != nil {
 		return nil, err
 	}
@@ -123,10 +125,10 @@
 	tree.ID = obj.Id().String()
 
 	odb, err := repo.Odb()
-
 	if err != nil {
 		return nil, err
 	}
+	defer odb.Free()
 
 	cb := func(n string, e *git.TreeEntry) int {
 		t := ""
@@ -163,6 +165,7 @@
 				err = lookErr
 				return -1
 			}
+			defer obj.Free()
 
 			blob, blobErr := obj.AsBlob()
 			if blobErr != nil {
diff --git a/fs/gitilesfs.go b/fs/gitilesfs.go
index 616b9f7..f9d5d1e 100644
--- a/fs/gitilesfs.go
+++ b/fs/gitilesfs.go
@@ -179,6 +179,9 @@
 		if clone && repo == nil {
 			r.lazyRepo.Clone()
 		}
+		if repo != nil {
+			defer repo.Free()
+		}
 
 		var content []byte
 		if repo != nil {
@@ -187,6 +190,7 @@
 				log.Println("LookupBlob: %v", err)
 				return nil, syscall.ESPIPE
 			}
+			defer blob.Free()
 			content = blob.Contents()
 		} else {
 			path := r.shaMap[id]
diff --git a/fs/manifestfs.go b/fs/manifestfs.go
index a6c8898..7eae4b0 100644
--- a/fs/manifestfs.go
+++ b/fs/manifestfs.go
@@ -70,7 +70,7 @@
 		}
 	}
 
-	root.trees, err = fetchTreeMap(cache.Tree, service, opts.Manifest)
+	root.trees, err = fetchTreeMap(cache, service, opts.Manifest)
 	if err != nil {
 		return nil, err
 	}
@@ -201,17 +201,13 @@
 	return nil
 }
 
-func fetchTreeMap(treeCache *cache.TreeCache, service *gitiles.Service, mf *manifest.Manifest) (map[string]*gitiles.Tree, error) {
+func fetchTreeMap(c *cache.Cache, service *gitiles.Service, mf *manifest.Manifest) (map[string]*gitiles.Tree, error) {
 	type resultT struct {
 		path string
 		resp *gitiles.Tree
 		err  error
 	}
 
-	// TODO(hanwen): if we have the repository, and commit
-	// locally, we should use cache.GetTree() instead of putting
-	// load on the remote Gitiles.
-
 	// Fetch all the trees in parallel.
 	out := make(chan resultT, len(mf.Project))
 	for _, p := range mf.Project {
@@ -222,13 +218,20 @@
 				return
 			}
 
-			tree, err := treeCache.Get(revID)
+			tree, err := c.Tree.Get(revID)
+			if err != nil {
+				if repo := c.Git.OpenLocal(p.CloneURL); repo != nil {
+					defer repo.Free()
+					tree, err = cache.GetTree(repo, revID)
+				}
+			}
+
 			if err != nil {
 				repoService := service.NewRepoService(p.Name)
 
 				tree, err = repoService.GetTree(p.Revision, "", true)
 				if err == nil {
-					if err := treeCache.Add(revID, tree); err != nil {
+					if err := c.Tree.Add(revID, tree); err != nil {
 						log.Printf("treeCache.Add: %v", err)
 					}
 				}