Add a caching downloader to avoid double clonse - fixes #22
diff --git a/downloader.go b/downloader.go
new file mode 100644
index 0000000..3fb76aa
--- /dev/null
+++ b/downloader.go
@@ -0,0 +1,69 @@
+package main
+
+import (
+	"sync"
+
+	"github.com/FiloSottile/gvt/gbvendor"
+)
+
+type cacheKey struct {
+	url, repoType         string
+	branch, tag, revision string
+}
+
+type cacheEntry struct {
+	wg  sync.WaitGroup
+	v   vendor.WorkingCopy
+	err error
+}
+
+// Downloader acts as a cache for downloaded repositories
+type Downloader struct {
+	mu sync.Mutex
+	m  map[cacheKey]*cacheEntry
+}
+
+var GlobalDownloader = Downloader{}
+
+func init() {
+	GlobalDownloader.m = make(map[cacheKey]*cacheEntry)
+}
+
+// Get returns a cached WorkingCopy, or runs RemoteRepo.Checkout
+func (d *Downloader) Get(repo vendor.RemoteRepo, branch, tag, revision string) (vendor.WorkingCopy, error) {
+	key := cacheKey{
+		url: repo.URL(), repoType: repo.Type(),
+		branch: branch, tag: tag, revision: revision,
+	}
+	d.mu.Lock()
+	if entry, ok := d.m[key]; ok {
+		d.mu.Unlock()
+		entry.wg.Wait()
+		return entry.v, entry.err
+	}
+
+	entry := &cacheEntry{}
+	entry.wg.Add(1)
+	d.m[key] = entry
+	d.mu.Unlock()
+
+	entry.v, entry.err = repo.Checkout(branch, tag, revision)
+	entry.wg.Done()
+	return entry.v, entry.err
+}
+
+func (d *Downloader) Flush() error {
+	d.mu.Lock()
+	defer d.mu.Unlock()
+
+	for _, entry := range d.m {
+		entry.wg.Wait()
+		if entry.err != nil {
+			continue
+		}
+		if err := entry.v.Destroy(); err != nil {
+			return err
+		}
+	}
+	return nil
+}
diff --git a/fetch.go b/fetch.go
index 5797bef..b21f3b4 100644
--- a/fetch.go
+++ b/fetch.go
@@ -94,7 +94,7 @@
 		return fmt.Errorf("%s is already vendored", path)
 	}
 
-	wc, err := repo.Checkout(branch, tag, revision)
+	wc, err := GlobalDownloader.Get(repo, branch, tag, revision)
 
 	if err != nil {
 		return err
@@ -138,10 +138,6 @@
 		return err
 	}
 
-	if err := wc.Destroy(); err != nil {
-		return err
-	}
-
 	if !recurse {
 		return nil
 	}
diff --git a/gbvendor/repo.go b/gbvendor/repo.go
index 916dbe8..9b970f3 100644
--- a/gbvendor/repo.go
+++ b/gbvendor/repo.go
@@ -24,9 +24,11 @@
 	// specific.
 	Checkout(branch, tag, revision string) (WorkingCopy, error)
 
-	// URL returns the URL the clone was taken from. It should
-	// only be called after Clone.
+	// URL returns the URL the clone was/will be taken from.
 	URL() string
+
+	// Type returns the repository type (git, hg, ...)
+	Type() string
 }
 
 // WorkingCopy represents a local copy of a remote dvcs repository.
@@ -274,6 +276,10 @@
 	return g.url
 }
 
+func (g *gitrepo) Type() string {
+	return "git"
+}
+
 // Checkout fetchs the remote branch, tag, or revision. If the branch is blank,
 // then the default remote branch will be used. If the branch is "HEAD" and
 // revision is empty, an impossible update is assumed.
@@ -380,7 +386,8 @@
 	url string
 }
 
-func (h *hgrepo) URL() string { return h.url }
+func (h *hgrepo) URL() string  { return h.url }
+func (h *hgrepo) Type() string { return "hg" }
 
 func (h *hgrepo) Checkout(branch, tag, revision string) (WorkingCopy, error) {
 	if !atMostOne(tag, revision) {
@@ -454,6 +461,10 @@
 	return b.url
 }
 
+func (b *bzrrepo) Type() string {
+	return "bzr"
+}
+
 func (b *bzrrepo) Checkout(branch, tag, revision string) (WorkingCopy, error) {
 	if !atMostOne(tag, revision) {
 		return nil, fmt.Errorf("only one of tag or revision may be supplied")
diff --git a/main.go b/main.go
index 3f435b8..129b95b 100644
--- a/main.go
+++ b/main.go
@@ -67,6 +67,9 @@
 			if err := command.Run(fs.Args()); err != nil {
 				log.Fatalf("command %q failed: %v", command.Name, err)
 			}
+			if err := GlobalDownloader.Flush(); err != nil {
+				log.Fatalf("failed to delete tempdirs: %v", err)
+			}
 			return
 		}
 	}
diff --git a/restore.go b/restore.go
index 27eb024..dfd078a 100644
--- a/restore.go
+++ b/restore.go
@@ -107,7 +107,7 @@
 	}
 	// We can't pass the branch here, and benefit from narrow clones, as the
 	// revision might not be in the branch tree anymore. Thanks rebase.
-	wc, err := repo.Checkout("", "", dep.Revision)
+	wc, err := GlobalDownloader.Get(repo, "", "", dep.Revision)
 	if err != nil {
 		return fmt.Errorf("dependency could not be fetched: %s", err)
 	}
@@ -128,10 +128,6 @@
 		return err
 	}
 
-	if err := wc.Destroy(); err != nil {
-		return err
-	}
-
 	// Check for for manifests in dependencies
 	man := filepath.Join(dst, "vendor", "manifest")
 	venDir := filepath.Join(dst, "vendor")
diff --git a/update.go b/update.go
index 17f0358..f7d38d6 100644
--- a/update.go
+++ b/update.go
@@ -75,7 +75,7 @@
 				return fmt.Errorf("could not determine repository for import %q", d.Importpath)
 			}
 
-			wc, err := repo.Checkout(d.Branch, "", "")
+			wc, err := GlobalDownloader.Get(repo, d.Branch, "", "")
 			if err != nil {
 				return err
 			}
@@ -122,10 +122,6 @@
 			if err := vendor.WriteManifest(manifestFile(), m); err != nil {
 				return err
 			}
-
-			if err := wc.Destroy(); err != nil {
-				return err
-			}
 		}
 
 		return nil