Add a cache for DeduceRemoteRepo

This skips the network ping, and speeds up for example
	gvt fetch camlistore.org/pkg/client
by many minutes, since all fetches from camlistore.org after the first
become local.
diff --git a/downloader.go b/downloader.go
index 3fb76aa..c9081d6 100644
--- a/downloader.go
+++ b/downloader.go
@@ -1,6 +1,7 @@
 package main
 
 import (
+	"strings"
 	"sync"
 
 	"github.com/FiloSottile/gvt/gbvendor"
@@ -19,14 +20,20 @@
 
 // Downloader acts as a cache for downloaded repositories
 type Downloader struct {
-	mu sync.Mutex
-	m  map[cacheKey]*cacheEntry
+	wcsMu sync.Mutex
+	wcs   map[cacheKey]*cacheEntry
+
+	reposMu sync.RWMutex
+	repos   map[string]vendor.RemoteRepo
+	reposI  map[string]vendor.RemoteRepo
 }
 
 var GlobalDownloader = Downloader{}
 
 func init() {
-	GlobalDownloader.m = make(map[cacheKey]*cacheEntry)
+	GlobalDownloader.wcs = make(map[cacheKey]*cacheEntry)
+	GlobalDownloader.repos = make(map[string]vendor.RemoteRepo)
+	GlobalDownloader.reposI = make(map[string]vendor.RemoteRepo)
 }
 
 // Get returns a cached WorkingCopy, or runs RemoteRepo.Checkout
@@ -35,17 +42,17 @@
 		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()
+	d.wcsMu.Lock()
+	if entry, ok := d.wcs[key]; ok {
+		d.wcsMu.Unlock()
 		entry.wg.Wait()
 		return entry.v, entry.err
 	}
 
 	entry := &cacheEntry{}
 	entry.wg.Add(1)
-	d.m[key] = entry
-	d.mu.Unlock()
+	d.wcs[key] = entry
+	d.wcsMu.Unlock()
 
 	entry.v, entry.err = repo.Checkout(branch, tag, revision)
 	entry.wg.Done()
@@ -53,10 +60,10 @@
 }
 
 func (d *Downloader) Flush() error {
-	d.mu.Lock()
-	defer d.mu.Unlock()
+	d.wcsMu.Lock()
+	defer d.wcsMu.Unlock()
 
-	for _, entry := range d.m {
+	for _, entry := range d.wcs {
 		entry.wg.Wait()
 		if entry.err != nil {
 			continue
@@ -67,3 +74,37 @@
 	}
 	return nil
 }
+
+// DeduceRemoteRepo is a cached version of vendor.DeduceRemoteRepo
+func (d *Downloader) DeduceRemoteRepo(path string, insecure bool) (vendor.RemoteRepo, string, error) {
+	cache := d.repos
+	if insecure {
+		cache = d.reposI
+	}
+
+	d.reposMu.RLock()
+	for p, repo := range cache {
+		if path == p || strings.HasPrefix(path, p+"/") {
+			d.reposMu.RUnlock()
+			extra := strings.Trim(strings.TrimPrefix(path, p), "/")
+			return repo, extra, nil
+		}
+	}
+	d.reposMu.RUnlock()
+
+	repo, extra, err := vendor.DeduceRemoteRepo(path, insecure)
+	if err != nil {
+		return repo, extra, err
+	}
+
+	if !strings.HasSuffix(path, extra) {
+		// Shouldn't happen, but in case just bypass the cache
+		return repo, extra, err
+	}
+	basePath := strings.Trim(strings.TrimSuffix(path, extra), "/")
+	d.reposMu.Lock()
+	cache[basePath] = repo
+	d.reposMu.Unlock()
+
+	return repo, extra, err
+}
diff --git a/fetch.go b/fetch.go
index cb9814c..089a1ac 100644
--- a/fetch.go
+++ b/fetch.go
@@ -159,7 +159,7 @@
 
 	// Find and download the repository
 
-	repo, extra, err := vendor.DeduceRemoteRepo(fullPath, insecure)
+	repo, extra, err := GlobalDownloader.DeduceRemoteRepo(fullPath, insecure)
 	if err != nil {
 		return err
 	}