Merge pull request #438 from klnusbaum/check_basedir_symlink
diff --git a/.gitignore b/.gitignore
index 3b111ff..2bd0e51 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,4 @@
 *.sublime-workspace
 dist/
 .DS_Store
+.idea
diff --git a/README.md b/README.md
index ec6f5af..566bd30 100644
--- a/README.md
+++ b/README.md
@@ -95,8 +95,9 @@
 
 1. Clone this repository into `$GOPATH/src/github.com/Masterminds/glide` and
    change directory into it
-2. Ensure that the environment variable GO15VENDOREXPERIMENT is set, for
-   example by running `export GO15VENDOREXPERIMENT=1`
+2. If you are using Go 1.5 ensure the environment variable GO15VENDOREXPERIMENT is set, for
+   example by running `export GO15VENDOREXPERIMENT=1`. In Go 1.6 it is enabled by default and
+   in Go 1.7 it is always enabled without the ability to turn it off.
 3. Run `make build`
 
 This will leave you with `./glide`, which you can put in your `$PATH` if
diff --git a/action/cache.go b/action/cache.go
new file mode 100644
index 0000000..9896f21
--- /dev/null
+++ b/action/cache.go
@@ -0,0 +1,29 @@
+package action
+
+import (
+	"os"
+
+	"github.com/Masterminds/glide/cache"
+	"github.com/Masterminds/glide/msg"
+)
+
+// CacheClear clears the Glide cache
+func CacheClear() {
+	l, err := cache.Location()
+	if err != nil {
+		msg.Die("Unable to clear the cache: %s", err)
+	}
+
+	err = os.RemoveAll(l)
+	if err != nil {
+		msg.Die("Unable to clear the cache: %s", err)
+	}
+
+	cache.SetupReset()
+	err = cache.Setup()
+	if err != nil {
+		msg.Die("Unable to clear the cache: %s", err)
+	}
+
+	msg.Info("Glide cache has been cleared.")
+}
diff --git a/action/create.go b/action/create.go
index b55a116..b66257c 100644
--- a/action/create.go
+++ b/action/create.go
@@ -73,29 +73,7 @@
 
 	// Attempt to import from other package managers.
 	if !skipImport {
-		msg.Info("Attempting to import from other package managers (use --skip-import to skip)")
-		deps := []*cfg.Dependency{}
-		absBase, err := filepath.Abs(base)
-		if err != nil {
-			msg.Die("Failed to resolve location of %s: %s", base, err)
-		}
-
-		if d, ok := guessImportGodep(absBase); ok {
-			msg.Info("Importing Godep configuration")
-			msg.Warn("Godep uses commit id versions. Consider using Semantic Versions with Glide")
-			deps = d
-		} else if d, ok := guessImportGPM(absBase); ok {
-			msg.Info("Importing GPM configuration")
-			deps = d
-		} else if d, ok := guessImportGB(absBase); ok {
-			msg.Info("Importing GB configuration")
-			deps = d
-		}
-
-		for _, i := range deps {
-			msg.Info("Found imported reference to %s\n", i.Name)
-			config.Imports = append(config.Imports, i)
-		}
+		guessImportDeps(base, config)
 	}
 
 	// Resolve dependencies by looking at the tree.
@@ -147,6 +125,32 @@
 	return config
 }
 
+func guessImportDeps(base string, config *cfg.Config) {
+	msg.Info("Attempting to import from other package managers (use --skip-import to skip)")
+	deps := []*cfg.Dependency{}
+	absBase, err := filepath.Abs(base)
+	if err != nil {
+		msg.Die("Failed to resolve location of %s: %s", base, err)
+	}
+
+	if d, ok := guessImportGodep(absBase); ok {
+		msg.Info("Importing Godep configuration")
+		msg.Warn("Godep uses commit id versions. Consider using Semantic Versions with Glide")
+		deps = d
+	} else if d, ok := guessImportGPM(absBase); ok {
+		msg.Info("Importing GPM configuration")
+		deps = d
+	} else if d, ok := guessImportGB(absBase); ok {
+		msg.Info("Importing GB configuration")
+		deps = d
+	}
+
+	for _, i := range deps {
+		msg.Info("Found imported reference to %s\n", i.Name)
+		config.Imports = append(config.Imports, i)
+	}
+}
+
 func guessImportGodep(dir string) ([]*cfg.Dependency, bool) {
 	d, err := godep.Parse(dir)
 	if err != nil || len(d) == 0 {
diff --git a/action/ensure.go b/action/ensure.go
index 041cfce..2f5f5af 100644
--- a/action/ensure.go
+++ b/action/ensure.go
@@ -59,11 +59,6 @@
 	return conf
 }
 
-// EnsureCacheDir ensures the existence of the cache directory
-func EnsureCacheDir() {
-	msg.Warn("ensure.go: ensureCacheDir is not implemented.")
-}
-
 // EnsureGoVendor ensures that the Go version is correct.
 func EnsureGoVendor() {
 	// 6l was removed in 1.5, when vendoring was introduced.
@@ -148,7 +143,6 @@
 	return ""
 }
 
-
 // goExecutable checks for a set environment variable of GLIDE_GO_EXECUTABLE
 // for the go executable name. The Google App Engine SDK ships with a python
 // wrapper called goapp
diff --git a/action/get.go b/action/get.go
index 5665ef6..58384de 100644
--- a/action/get.go
+++ b/action/get.go
@@ -5,6 +5,7 @@
 	"path/filepath"
 	"strings"
 
+	"github.com/Masterminds/glide/cache"
 	"github.com/Masterminds/glide/cfg"
 	"github.com/Masterminds/glide/godep"
 	"github.com/Masterminds/glide/msg"
@@ -17,6 +18,10 @@
 //
 // This includes resolving dependency resolution and re-generating the lock file.
 func Get(names []string, installer *repo.Installer, insecure, skipRecursive, strip, stripVendor bool) {
+	if installer.UseCache {
+		cache.SystemLock()
+	}
+
 	base := gpath.Basepath()
 	EnsureGopath()
 	EnsureVendorDir()
diff --git a/action/init.go b/action/init.go
index b0c29da..2094902 100644
--- a/action/init.go
+++ b/action/init.go
@@ -7,5 +7,5 @@
 // Init initializes the action subsystem for handling one or more subesequent actions.
 func Init(yaml, home string) {
 	gpath.GlideFile = yaml
-	gpath.HomeDir = home
+	gpath.SetHome(home)
 }
diff --git a/action/install.go b/action/install.go
index 3ae39c0..25a5f08 100644
--- a/action/install.go
+++ b/action/install.go
@@ -4,6 +4,7 @@
 	"io/ioutil"
 	"path/filepath"
 
+	"github.com/Masterminds/glide/cache"
 	"github.com/Masterminds/glide/cfg"
 	"github.com/Masterminds/glide/dependency"
 	"github.com/Masterminds/glide/msg"
@@ -13,6 +14,10 @@
 
 // Install installs a vendor directory based on an existing Glide configuration.
 func Install(installer *repo.Installer, strip, stripVendor bool) {
+	if installer.UseCache {
+		cache.SystemLock()
+	}
+
 	base := "."
 	// Ensure GOPATH
 	EnsureGopath()
diff --git a/action/update.go b/action/update.go
index defaf07..23d3f4f 100644
--- a/action/update.go
+++ b/action/update.go
@@ -4,6 +4,7 @@
 	"io/ioutil"
 	"path/filepath"
 
+	"github.com/Masterminds/glide/cache"
 	"github.com/Masterminds/glide/cfg"
 	"github.com/Masterminds/glide/dependency"
 	"github.com/Masterminds/glide/godep"
@@ -14,6 +15,10 @@
 
 // Update updates repos and the lock file from the main glide yaml.
 func Update(installer *repo.Installer, skipRecursive, strip, stripVendor bool) {
+	if installer.UseCache {
+		cache.SystemLock()
+	}
+
 	base := "."
 	EnsureGopath()
 	EnsureVendorDir()
diff --git a/cache/cache.go b/cache/cache.go
new file mode 100644
index 0000000..5a8538d
--- /dev/null
+++ b/cache/cache.go
@@ -0,0 +1,231 @@
+// Package cache provides an interface for interfacing with the Glide local cache
+//
+// Glide has a local cache of metadata and repositories similar to the GOPATH.
+// To store the cache Glide creates a .glide directory with a cache subdirectory.
+// This is usually in the users home directory unless there is no accessible
+// home directory in which case the .glide directory is in the root of the
+// repository.
+//
+// To get the cache location use the `cache.Location()` function. This will
+// return the proper base location in your environment.
+//
+// Within the cache directory there are two subdirectories. They are the src
+// and info directories. The src directory contains version control checkouts
+// of the packages. The info direcory contains metadata. The metadata maps to
+// the RepoInfo struct. Both stores are happed to keys.
+//
+// Using the `cache.Key()` function you can get a key for a repo. Pass in a
+// location such as `https://github.com/foo/bar` or `git@example.com:foo.git`
+// and a key will be returned that can be used for caching operations.
+//
+// Note, the caching is based on repo rather than package. This is important
+// for a couple reasons.
+//
+// 1. Forks or package replacements are supported in Glide. Where a different
+//    repo maps to a package.
+// 2. Permissions enable different access. For example `https://example.com/foo.git`
+//    and `git@example.com:foo.git` may have access to different branches or tags.
+package cache
+
+import (
+	"encoding/json"
+	"errors"
+	"io/ioutil"
+	"net/url"
+	"os"
+	"path/filepath"
+	"regexp"
+	"strings"
+	"sync"
+	"time"
+
+	"github.com/Masterminds/glide/msg"
+	gpath "github.com/Masterminds/glide/path"
+)
+
+// Enabled sets if the cache is globally enabled. Defaults to true.
+var Enabled = true
+
+// ErrCacheDisabled is returned with the cache is disabled.
+var ErrCacheDisabled = errors.New("Cache disabled")
+
+var isSetup bool
+
+var setupMutex sync.Mutex
+
+// Setup creates the cache location.
+func Setup() error {
+	setupMutex.Lock()
+	defer setupMutex.Unlock()
+
+	if isSetup {
+		return nil
+	}
+	msg.Debug("Setting up the cache directory")
+	pths := []string{
+		"cache",
+		filepath.Join("cache", "src"),
+		filepath.Join("cache", "info"),
+	}
+
+	for _, l := range pths {
+		err := os.MkdirAll(filepath.Join(gpath.Home(), l), 0755)
+		if err != nil {
+			return err
+		}
+	}
+
+	isSetup = true
+	return nil
+}
+
+// SetupReset resets if setup has been completed. The next time setup is run
+// it will attempt a full setup.
+func SetupReset() {
+	isSetup = false
+}
+
+// Location returns the location of the cache.
+func Location() (string, error) {
+	p := filepath.Join(gpath.Home(), "cache")
+	err := Setup()
+
+	return p, err
+}
+
+// scpSyntaxRe matches the SCP-like addresses used to access repos over SSH.
+var scpSyntaxRe = regexp.MustCompile(`^([a-zA-Z0-9_]+)@([a-zA-Z0-9._-]+):(.*)$`)
+
+// Key generates a cache key based on a url or scp string. The key is file
+// system safe.
+func Key(repo string) (string, error) {
+
+	var u *url.URL
+	var err error
+	var strip bool
+	if m := scpSyntaxRe.FindStringSubmatch(repo); m != nil {
+		// Match SCP-like syntax and convert it to a URL.
+		// Eg, "git@github.com:user/repo" becomes
+		// "ssh://git@github.com/user/repo".
+		u = &url.URL{
+			Scheme: "ssh",
+			User:   url.User(m[1]),
+			Host:   m[2],
+			Path:   "/" + m[3],
+		}
+		strip = true
+	} else {
+		u, err = url.Parse(repo)
+		if err != nil {
+			return "", err
+		}
+	}
+
+	if strip {
+		u.Scheme = ""
+	}
+
+	var key string
+	if u.Scheme != "" {
+		key = u.Scheme + "-"
+	}
+	if u.User != nil && u.User.Username() != "" {
+		key = key + u.User.Username() + "-"
+	}
+	key = key + u.Host
+	if u.Path != "" {
+		key = key + strings.Replace(u.Path, "/", "-", -1)
+	}
+
+	key = strings.Replace(key, ":", "-", -1)
+
+	return key, nil
+}
+
+// RepoInfo holds information about a repo.
+type RepoInfo struct {
+	DefaultBranch string `json:"default-branch"`
+	LastUpdate    string `json:"last-update"`
+}
+
+// SaveRepoData stores data about a repo in the Glide cache
+func SaveRepoData(key string, data RepoInfo) error {
+	if !Enabled {
+		return ErrCacheDisabled
+	}
+	location, err := Location()
+	if err != nil {
+		return err
+	}
+	data.LastUpdate = time.Now().String()
+	d, err := json.Marshal(data)
+	if err != nil {
+		return err
+	}
+
+	pp := filepath.Join(location, "info")
+	err = os.MkdirAll(pp, 0755)
+	if err != nil {
+		return err
+	}
+
+	p := filepath.Join(pp, key+".json")
+	f, err := os.Create(p)
+	if err != nil {
+		return err
+	}
+	defer f.Close()
+
+	_, err = f.Write(d)
+	return err
+}
+
+// RepoData retrieves cached information about a repo.
+func RepoData(key string) (*RepoInfo, error) {
+	if !Enabled {
+		return &RepoInfo{}, ErrCacheDisabled
+	}
+	location, err := Location()
+	if err != nil {
+		return &RepoInfo{}, err
+	}
+	c := &RepoInfo{}
+	p := filepath.Join(location, "info", key+".json")
+	f, err := ioutil.ReadFile(p)
+	if err != nil {
+		return &RepoInfo{}, err
+	}
+	err = json.Unmarshal(f, c)
+	if err != nil {
+		return &RepoInfo{}, err
+	}
+	return c, nil
+}
+
+var lockSync sync.Mutex
+
+var lockData = make(map[string]*sync.Mutex)
+
+// Lock locks a particular key name
+func Lock(name string) {
+	lockSync.Lock()
+	m, ok := lockData[name]
+	if !ok {
+		m = &sync.Mutex{}
+		lockData[name] = m
+	}
+	lockSync.Unlock()
+	msg.Debug("Locking %s", name)
+	m.Lock()
+}
+
+// Unlock unlocks a particular key name
+func Unlock(name string) {
+	msg.Debug("Unlocking %s", name)
+	lockSync.Lock()
+	if m, ok := lockData[name]; ok {
+		m.Unlock()
+	}
+
+	lockSync.Unlock()
+}
diff --git a/cache/cache_test.go b/cache/cache_test.go
new file mode 100644
index 0000000..ce6f5f5
--- /dev/null
+++ b/cache/cache_test.go
@@ -0,0 +1,22 @@
+package cache
+
+import "testing"
+
+func TestKey(t *testing.T) {
+	tests := map[string]string{
+		"https://github.com/foo/bar":     "https-github.com-foo-bar",
+		"git@github.com:foo/bar":         "git-github.com-foo-bar",
+		"https://github.com:123/foo/bar": "https-github.com-123-foo-bar",
+	}
+
+	for k, v := range tests {
+		key, err := Key(k)
+		if err != nil {
+			t.Errorf("Cache key generation err: %s", err)
+			continue
+		}
+		if key != v {
+			t.Errorf("Expected cache key %s for %s but got %s", v, k, key)
+		}
+	}
+}
diff --git a/cache/global_lock.go b/cache/global_lock.go
new file mode 100644
index 0000000..52cf841
--- /dev/null
+++ b/cache/global_lock.go
@@ -0,0 +1,111 @@
+package cache
+
+import (
+	"encoding/json"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"time"
+
+	"github.com/Masterminds/glide/msg"
+	gpath "github.com/Masterminds/glide/path"
+)
+
+var isStarted bool
+
+// SystemLock starts a system rather than application lock. This way multiple
+// app instances don't cause race conditions when working in the cache.
+func SystemLock() error {
+	if isStarted {
+		return nil
+	}
+	err := waitOnLock()
+	if err != nil {
+		return err
+	}
+	err = startLock()
+	isStarted = true
+	return err
+}
+
+// SystemUnlock removes the system wide Glide cache lock.
+func SystemUnlock() {
+	lockdone <- struct{}{}
+	os.Remove(lockFileName)
+}
+
+var lockdone = make(chan struct{}, 1)
+
+type lockdata struct {
+	Comment string `json:"comment"`
+	Pid     int    `json:"pid"`
+	Time    string `json:"time"`
+}
+
+var lockFileName = filepath.Join(gpath.Home(), "lock.json")
+
+// Write a lock for now.
+func writeLock() error {
+	ld := &lockdata{
+		Comment: "File managed by Glide (https://glide.sh)",
+		Pid:     os.Getpid(),
+		Time:    time.Now().Format(time.RFC3339Nano),
+	}
+
+	out, err := json.Marshal(ld)
+	if err != nil {
+		return err
+	}
+	err = ioutil.WriteFile(lockFileName, out, 0755)
+	return err
+}
+
+func startLock() error {
+	err := writeLock()
+	if err != nil {
+		return err
+	}
+
+	go func() {
+		for {
+			select {
+			case <-lockdone:
+				return
+			default:
+				time.Sleep(10 * time.Second)
+				err := writeLock()
+				if err != nil {
+					msg.Die("Error using Glide lock: %s", err)
+				}
+			}
+		}
+	}()
+
+	return nil
+}
+
+func waitOnLock() error {
+	var announced bool
+	for {
+		fi, err := os.Stat(lockFileName)
+		if err != nil && os.IsNotExist(err) {
+			return nil
+		} else if err != nil {
+			return err
+		}
+
+		diff := time.Now().Sub(fi.ModTime())
+		if diff.Seconds() > 15 {
+			return nil
+		}
+
+		if !announced {
+			announced = true
+			msg.Info("Waiting on Glide global cache access")
+		}
+
+		// Check on the lock file every 15 seconds.
+		// TODO(mattfarina): should this be a different length?
+		time.Sleep(time.Second)
+	}
+}
diff --git a/dependency/resolver.go b/dependency/resolver.go
index a9fa5ae..300ca4d 100644
--- a/dependency/resolver.go
+++ b/dependency/resolver.go
@@ -901,10 +901,11 @@
 		// https://blog.golang.org/the-app-engine-sdk-and-workspaces-gopath
 		info.Loc = LocAppengine
 		r.findCache[name] = info
-	} else if name == "context" {
-		// context is a package being added to the Go 1.7 standard library. Some
-		// packages, such as golang.org/x/net are importing it with build flags
-		// in files for go1.7. Need to detect this and handle it.
+	} else if name == "context" || name == "net/http/httptrace" {
+		// context and net/http/httptrace are packages being added to
+		// the Go 1.7 standard library. Some packages, such as golang.org/x/net
+		// are importing it with build flags in files for go1.7. Need to detect
+		// this and handle it.
 		info.Loc = LocGoroot
 		r.findCache[name] = info
 	}
diff --git a/docs/example-glide.yaml b/docs/example-glide.yaml
index 556df8b..241787f 100644
--- a/docs/example-glide.yaml
+++ b/docs/example-glide.yaml
@@ -38,21 +38,6 @@
     vcs: git
     repo: git@github.com:technosophos/goamz.git
 
-  # This will fetch the entire technosophos/luge package, but only
-  # 'go build' the technosophos/luge/foo and technosophos/luge/bar packages.
-  - package: github.com/technosophos/luge
-    vcs: git
-    subpackages:
-      - foo
-      - bar
-      # Shell globs are also supported:
-      - pa?e
-      - [wc]*
-
-  # Use '...' to tell glide to build ALL of the subpackages.
-  - package: github.com/Masterminds/blarg
-    subpackages: ...
-
   - package: bzr.example.com/foo/bar/trunk
     vcs: bzr
     repo: bzr://bzr.example.com/foo/bar/trunk
diff --git a/docs/glide.yaml.md b/docs/glide.yaml.md
index 2216bee..2943853 100644
--- a/docs/glide.yaml.md
+++ b/docs/glide.yaml.md
@@ -41,6 +41,7 @@
     - `version`: A semantic version, semantic version range, branch, tag, or commit id to use. For more information see the [versioning documentation](versions.md).
     - `repo`: If the package name isn't the repo location or this is a private repository it can go here. The package will be checked out from the repo and put where the package name specifies. This allows using forks.
     - `vcs`: A VCS to use such as git, hg, bzr, or svn. This is only needed when the type cannot be detected from the name. For example, a repo ending in .git or on GitHub can be detected to be Git. For a repo on Bitbucket we can contact the API to discover the type.
+    - `subpackages`: A record of packages being used within a repository. This does not include all packages within a repository but rather those being used.
     - `os`: A list of operating systems used for filtering. If set it will compare the current runtime OS to the one specified and only fetch the dependency if there is a match. If not set filtering is skipped. The names are the same used in build flags and `GOOS` environment variable.
     - `arch`: A list of architectures used for filtering. If set it will compare the current runtime architecture to the one specified and only fetch the dependency if there is a match. If not set filtering is skipped. The names are the same used in build flags and `GOARCH` environment variable.
 - `devImport`: A list of development packages. Each package has the same details as those listed under import.
diff --git a/glide.go b/glide.go
index e205796..0e1e355 100644
--- a/glide.go
+++ b/glide.go
@@ -40,6 +40,7 @@
 	"path/filepath"
 
 	"github.com/Masterminds/glide/action"
+	"github.com/Masterminds/glide/cache"
 	"github.com/Masterminds/glide/msg"
 	gpath "github.com/Masterminds/glide/path"
 	"github.com/Masterminds/glide/repo"
@@ -49,7 +50,6 @@
 
 	"fmt"
 	"os"
-	"os/user"
 )
 
 var version = "0.11.0-dev"
@@ -97,7 +97,7 @@
 		},
 		cli.StringFlag{
 			Name:   "home",
-			Value:  defaultGlideDir(),
+			Value:  gpath.Home(),
 			Usage:  "The location of Glide files",
 			EnvVar: "GLIDE_HOME",
 		},
@@ -111,6 +111,7 @@
 		action.Plugin(command, os.Args)
 	}
 	app.Before = startup
+	app.After = shutdown
 	app.Commands = commands()
 
 	// Detect errors from the Before and After calls and exit on them.
@@ -220,7 +221,7 @@
 				},
 				cli.BoolFlag{
 					Name:  "strip-vcs, s",
-					Usage: "Removes version control metada (e.g, .git directory) from the vendor folder.",
+					Usage: "Removes version control metadata (e.g, .git directory) from the vendor folder.",
 				},
 				cli.BoolFlag{
 					Name:  "strip-vendor, v",
@@ -426,7 +427,7 @@
 				},
 				cli.BoolFlag{
 					Name:  "strip-vcs, s",
-					Usage: "Removes version control metada (e.g, .git directory) from the vendor folder.",
+					Usage: "Removes version control metadata (e.g, .git directory) from the vendor folder.",
 				},
 				cli.BoolFlag{
 					Name:  "strip-vendor, v",
@@ -444,8 +445,8 @@
 				installer.UseGopath = c.Bool("use-gopath")
 				installer.UseCacheGopath = c.Bool("cache-gopath")
 				installer.UpdateVendored = c.Bool("update-vendored")
-				installer.Home = gpath.Home()
-				installer.DeleteUnused = c.Bool("deleteOptIn")
+				installer.Home = c.GlobalString("home")
+				installer.DeleteUnused = c.Bool("delete")
 
 				action.Install(installer, c.Bool("strip-vcs"), c.Bool("strip-vendor"))
 			},
@@ -533,7 +534,7 @@
 				},
 				cli.BoolFlag{
 					Name:  "strip-vcs, s",
-					Usage: "Removes version control metada (e.g, .git directory) from the vendor folder.",
+					Usage: "Removes version control metadata (e.g, .git directory) from the vendor folder.",
 				},
 				cli.BoolFlag{
 					Name:  "strip-vendor, v",
@@ -557,8 +558,8 @@
 				installer.UseCacheGopath = c.Bool("cache-gopath")
 				installer.UpdateVendored = c.Bool("update-vendored")
 				installer.ResolveAllFiles = c.Bool("all-dependencies")
-				installer.Home = gpath.Home()
-				installer.DeleteUnused = c.Bool("deleteOptIn")
+				installer.Home = c.GlobalString("home")
+				installer.DeleteUnused = c.Bool("delete")
 
 				action.Update(installer, c.Bool("no-recursive"), c.Bool("strip-vcs"), c.Bool("strip-vendor"))
 			},
@@ -640,6 +641,14 @@
 			},
 		},
 		{
+			Name:      "cache-clear",
+			ShortName: "cc",
+			Usage:     "Clears the Glide cache.",
+			Action: func(c *cli.Context) {
+				action.CacheClear()
+			},
+		},
+		{
 			Name:  "about",
 			Usage: "Learn about Glide",
 			Action: func(c *cli.Context) {
@@ -649,14 +658,6 @@
 	}
 }
 
-func defaultGlideDir() string {
-	c, err := user.Current()
-	if err != nil {
-		return ""
-	}
-	return filepath.Join(c.HomeDir, ".glide")
-}
-
 // startup sets up the base environment.
 //
 // It does not assume the presence of a Glide.yaml file or vendor/ directory,
@@ -670,6 +671,11 @@
 	return nil
 }
 
+func shutdown(c *cli.Context) error {
+	cache.SystemUnlock()
+	return nil
+}
+
 // Get the path to the glide.yaml file.
 //
 // This returns the name of the path, even if the file does not exist. The value
diff --git a/msg/msg.go b/msg/msg.go
index b2a7bfe..af49ecf 100644
--- a/msg/msg.go
+++ b/msg/msg.go
@@ -61,7 +61,7 @@
 	if m.Quiet {
 		return
 	}
-	prefix := m.Color(Green, "[INFO] ")
+	prefix := m.Color(Green, "[INFO]\t")
 	m.Msg(prefix+msg, args...)
 }
 
@@ -75,7 +75,7 @@
 	if m.Quiet || !m.IsDebugging {
 		return
 	}
-	prefix := "[DEBUG] "
+	prefix := "[DEBUG]\t"
 	Msg(prefix+msg, args...)
 }
 
@@ -86,7 +86,7 @@
 
 // Warn logs a warning
 func (m *Messenger) Warn(msg string, args ...interface{}) {
-	prefix := m.Color(Yellow, "[WARN] ")
+	prefix := m.Color(Yellow, "[WARN]\t")
 	m.Msg(prefix+msg, args...)
 }
 
@@ -97,7 +97,7 @@
 
 // Err logs an error.
 func (m *Messenger) Err(msg string, args ...interface{}) {
-	prefix := m.Color(Red, "[ERROR] ")
+	prefix := m.Color(Red, "[ERROR]\t")
 	m.Msg(prefix+msg, args...)
 	m.hasErrored = true
 }
diff --git a/path/path.go b/path/path.go
index 9e7162f..0b19442 100644
--- a/path/path.go
+++ b/path/path.go
@@ -8,6 +8,7 @@
 	"fmt"
 	"io"
 	"os"
+	"os/user"
 	"path/filepath"
 	"strings"
 )
@@ -20,10 +21,8 @@
 // As of Go 1.5, this is always vendor.
 var VendorDir = "vendor"
 
-// HomeDir is the home directory for Glide.
-//
-// HomeDir is where cache files and other configuration data are stored.
-var HomeDir = "$HOME/.glide"
+// Cache the location of the homedirectory.
+var homeDir = ""
 
 // GlideFile is the name of the Glide file.
 //
@@ -38,12 +37,29 @@
 //
 // This normalizes to an absolute path, and passes through os.ExpandEnv.
 func Home() string {
-	h := os.ExpandEnv(HomeDir)
-	var err error
-	if h, err = filepath.Abs(HomeDir); err != nil {
-		return HomeDir
+	if homeDir != "" {
+		return homeDir
 	}
-	return h
+
+	// Initialize the default user.
+	u, err := user.Current()
+	if err == nil && u.HomeDir != "" {
+		homeDir = filepath.Join(u.HomeDir, ".glide")
+	} else {
+		cwd, err := os.Getwd()
+		if err == nil {
+			homeDir = filepath.Join(cwd, ".glide")
+		} else {
+			homeDir = ".glide"
+		}
+	}
+
+	return homeDir
+}
+
+// SetHome sets the home directory for Glide.
+func SetHome(h string) {
+	homeDir = h
 }
 
 // Vendor calculates the path to the vendor directory.
diff --git a/repo/cache.go b/repo/cache.go
deleted file mode 100644
index babd1c2..0000000
--- a/repo/cache.go
+++ /dev/null
@@ -1,128 +0,0 @@
-package repo
-
-import (
-	"encoding/json"
-	"errors"
-	"io/ioutil"
-	"net/url"
-	"os"
-	"path/filepath"
-	"regexp"
-	"strings"
-	"time"
-	//"github.com/Masterminds/glide/msg"
-)
-
-var cacheEnabled = true
-
-var errCacheDisabled = errors.New("Cache disabled")
-
-// EnsureCacheDir Creates the $HOME/.glide/cache directory (unless home is
-// specified to be different) if it does not exist.
-/*
-func EnsureCacheDir(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
-	home := p.Get("home", "").(string)
-	if home == "" {
-		cacheEnabled = false
-		msg.Warn("Unable to locate home directory")
-		return false, nil
-	}
-	err := os.MkdirAll(filepath.Join(home, "cache", "info"), os.ModeDir|os.ModePerm)
-	if err != nil {
-		cacheEnabled = false
-		Warn("Error creating Glide directory %s", home)
-	}
-	return false, nil
-}
-*/
-
-// scpSyntaxRe matches the SCP-like addresses used to access repos over SSH.
-var scpSyntaxRe = regexp.MustCompile(`^([a-zA-Z0-9_]+)@([a-zA-Z0-9._-]+):(.*)$`)
-
-// Pass in a repo location and get a cache key from it.
-func cacheCreateKey(repo string) (string, error) {
-
-	var u *url.URL
-	var err error
-	var strip bool
-	if m := scpSyntaxRe.FindStringSubmatch(repo); m != nil {
-		// Match SCP-like syntax and convert it to a URL.
-		// Eg, "git@github.com:user/repo" becomes
-		// "ssh://git@github.com/user/repo".
-		u = &url.URL{
-			Scheme: "ssh",
-			User:   url.User(m[1]),
-			Host:   m[2],
-			Path:   "/" + m[3],
-		}
-		strip = true
-	} else {
-		u, err = url.Parse(repo)
-		if err != nil {
-			return "", err
-		}
-	}
-
-	if strip {
-		u.Scheme = ""
-	}
-
-	var key string
-	if u.Scheme != "" {
-		key = u.Scheme + "-"
-	}
-	if u.User != nil && u.User.Username() != "" {
-		key = key + u.User.Username() + "-"
-	}
-	key = key + u.Host
-	if u.Path != "" {
-		key = key + strings.Replace(u.Path, "/", "-", -1)
-	}
-
-	key = strings.Replace(key, ":", "-", -1)
-
-	return key, nil
-}
-
-type cacheRepoInfo struct {
-	DefaultBranch string `json:"default-branch"`
-	LastUpdate    string `json:"last-update"`
-}
-
-func saveCacheRepoData(key string, data cacheRepoInfo, location string) error {
-	if !cacheEnabled {
-		return errCacheDisabled
-	}
-	data.LastUpdate = time.Now().String()
-	d, err := json.Marshal(data)
-	if err != nil {
-		return err
-	}
-
-	p := filepath.Join(location, "cache", "info", key+".json")
-	f, err := os.Create(p)
-	if err != nil {
-		return err
-	}
-	defer f.Close()
-
-	_, err = f.Write(d)
-	return err
-}
-
-func cacheRepoData(key, location string) (*cacheRepoInfo, error) {
-	if !cacheEnabled {
-		return &cacheRepoInfo{}, errCacheDisabled
-	}
-	c := &cacheRepoInfo{}
-	p := filepath.Join(location, "cache", "info", key+".json")
-	f, err := ioutil.ReadFile(p)
-	if err != nil {
-		return &cacheRepoInfo{}, err
-	}
-	err = json.Unmarshal(f, c)
-	if err != nil {
-		return &cacheRepoInfo{}, err
-	}
-	return c, nil
-}
diff --git a/repo/cache_test.go b/repo/cache_test.go
deleted file mode 100644
index d5e1c1d..0000000
--- a/repo/cache_test.go
+++ /dev/null
@@ -1,21 +0,0 @@
-package repo
-
-import "testing"
-
-func TestCacheCreateKey(t *testing.T) {
-	tests := map[string]string{
-		"https://github.com/foo/bar": "https-github.com-foo-bar",
-		"git@github.com:foo/bar":     "git-github.com-foo-bar",
-	}
-
-	for k, v := range tests {
-		key, err := cacheCreateKey(k)
-		if err != nil {
-			t.Errorf("Cache key generation err: %s", err)
-			continue
-		}
-		if key != v {
-			t.Errorf("Expected cache key %s for %s but got %s", v, k, key)
-		}
-	}
-}
diff --git a/repo/installer.go b/repo/installer.go
index e8e4bf6..1ae1901 100644
--- a/repo/installer.go
+++ b/repo/installer.go
@@ -8,6 +8,7 @@
 	"sync"
 	"time"
 
+	"github.com/Masterminds/glide/cache"
 	"github.com/Masterminds/glide/cfg"
 	"github.com/Masterminds/glide/dependency"
 	"github.com/Masterminds/glide/importer"
@@ -255,6 +256,17 @@
 			for {
 				select {
 				case dep := <-ch:
+					var loc string
+					if dep.Repository != "" {
+						loc = dep.Repository
+					} else {
+						loc = "https://" + dep.Name
+					}
+					key, err := cache.Key(loc)
+					if err != nil {
+						msg.Die(err.Error())
+					}
+					cache.Lock(key)
 					dest := filepath.Join(i.VendorPath(), dep.Name)
 					if err := VcsUpdate(dep, dest, i.Home, i.UseCache, i.UseCacheGopath, i.UseGopath, i.Force, i.UpdateVendored, i.Updated); err != nil {
 						msg.Err("Update failed for %s: %s\n", dep.Name, err)
@@ -268,6 +280,7 @@
 						}
 						lock.Unlock()
 					}
+					cache.Unlock(key)
 					wg.Done()
 				case <-done:
 					return
diff --git a/repo/vcs.go b/repo/vcs.go
index 16c4382..b2d471f 100644
--- a/repo/vcs.go
+++ b/repo/vcs.go
@@ -7,12 +7,12 @@
 	"net/http"
 	"net/url"
 	"os"
-	"os/exec"
 	"path/filepath"
 	"runtime"
 	"sort"
 	"strings"
 
+	cp "github.com/Masterminds/glide/cache"
 	"github.com/Masterminds/glide/cfg"
 	"github.com/Masterminds/glide/msg"
 	gpath "github.com/Masterminds/glide/path"
@@ -345,12 +345,12 @@
 					} else {
 						loc = "https://" + dep.Name
 					}
-					key, err := cacheCreateKey(loc)
+					key, err := cp.Key(loc)
 					if err == nil {
 						msg.Debug("Saving default branch for %s", repo.Remote())
-						c := cacheRepoInfo{DefaultBranch: branch}
-						err = saveCacheRepoData(key, c, home)
-						if msg.Default.IsDebugging && err == errCacheDisabled {
+						c := cp.RepoInfo{DefaultBranch: branch}
+						err = cp.SaveRepoData(key, c)
+						if msg.Default.IsDebugging && err == cp.ErrCacheDisabled {
 							msg.Debug("Unable to cache default branch because caching is disabled")
 						}
 					}
@@ -376,9 +376,13 @@
 		} else {
 			loc = "https://" + dep.Name
 		}
-		key, err := cacheCreateKey(loc)
+		key, err := cp.Key(loc)
 		if err == nil {
-			d := filepath.Join(home, "cache", "src", key)
+			location, err := cp.Location()
+			if err != nil {
+				return err
+			}
+			d := filepath.Join(location, "src", key)
 
 			repo, err := dep.GetRepo(d)
 			if err != nil {
@@ -400,12 +404,12 @@
 					} else {
 						loc = "https://" + dep.Name
 					}
-					key, err := cacheCreateKey(loc)
+					key, err := cp.Key(loc)
 					if err == nil {
 						msg.Debug("Saving default branch for %s", repo.Remote())
-						c := cacheRepoInfo{DefaultBranch: branch}
-						err = saveCacheRepoData(key, c, home)
-						if err == errCacheDisabled {
+						c := cp.RepoInfo{DefaultBranch: branch}
+						err = cp.SaveRepoData(key, c)
+						if err == cp.ErrCacheDisabled {
 							msg.Debug("Unable to cache default branch because caching is disabled")
 						} else if err != nil {
 							msg.Debug("Error saving %s to cache. Error: %s", repo.Remote(), err)
@@ -451,12 +455,12 @@
 			} else {
 				loc = "https://" + dep.Name
 			}
-			key, err := cacheCreateKey(loc)
+			key, err := cp.Key(loc)
 			if err == nil {
 				msg.Debug("Saving default branch for %s", repo.Remote())
-				c := cacheRepoInfo{DefaultBranch: branch}
-				err = saveCacheRepoData(key, c, home)
-				if err == errCacheDisabled {
+				c := cp.RepoInfo{DefaultBranch: branch}
+				err = cp.SaveRepoData(key, c)
+				if err == cp.ErrCacheDisabled {
 					msg.Debug("Unable to cache default branch because caching is disabled")
 				} else if err != nil {
 					msg.Debug("Error saving %s to cache - Error: %s", repo.Remote(), err)
@@ -528,10 +532,10 @@
 	}
 
 	// Check the cache for a value.
-	key, kerr := cacheCreateKey(repo.Remote())
-	var d cacheRepoInfo
+	key, kerr := cp.Key(repo.Remote())
+	var d cp.RepoInfo
 	if kerr == nil {
-		d, err := cacheRepoData(key, home)
+		d, err := cp.RepoData(key)
 		if err == nil {
 			if d.DefaultBranch != "" {
 				return d.DefaultBranch
@@ -579,8 +583,8 @@
 		db := gh["default_branch"].(string)
 		if kerr == nil {
 			d.DefaultBranch = db
-			err := saveCacheRepoData(key, d, home)
-			if err == errCacheDisabled {
+			err := cp.SaveRepoData(key, d)
+			if err == cp.ErrCacheDisabled {
 				msg.Debug("Unable to cache default branch because caching is disabled")
 			} else if err != nil {
 				msg.Debug("Error saving %s to cache. Error: %s", repo.Remote(), err)
@@ -613,8 +617,8 @@
 		db := bb["name"].(string)
 		if kerr == nil {
 			d.DefaultBranch = db
-			err := saveCacheRepoData(key, d, home)
-			if err == errCacheDisabled {
+			err := cp.SaveRepoData(key, d)
+			if err == cp.ErrCacheDisabled {
 				msg.Debug("Unable to cache default branch because caching is disabled")
 			} else if err != nil {
 				msg.Debug("Error saving %s to cache. Error: %s", repo.Remote(), err)
@@ -627,6 +631,8 @@
 }
 
 // From a local repo find out the current branch name if there is one.
+// Note, this should only be used right after a fresh clone to get accurate
+// information.
 func findCurrentBranch(repo v.Repo) string {
 	msg.Debug("Attempting to find current branch for %s", repo.Remote())
 	// Svn and Bzr don't have default branches.
@@ -634,28 +640,13 @@
 		return ""
 	}
 
-	if repo.Vcs() == v.Git {
-		c := exec.Command("git", "symbolic-ref", "--short", "HEAD")
-		c.Dir = repo.LocalPath()
-		c.Env = envForDir(c.Dir)
-		out, err := c.CombinedOutput()
+	if repo.Vcs() == v.Git || repo.Vcs() == v.Hg {
+		ver, err := repo.Current()
 		if err != nil {
 			msg.Debug("Unable to find current branch for %s, error: %s", repo.Remote(), err)
 			return ""
 		}
-		return strings.TrimSpace(string(out))
-	}
-
-	if repo.Vcs() == v.Hg {
-		c := exec.Command("hg", "branch")
-		c.Dir = repo.LocalPath()
-		c.Env = envForDir(c.Dir)
-		out, err := c.CombinedOutput()
-		if err != nil {
-			msg.Debug("Unable to find current branch for %s, error: %s", repo.Remote(), err)
-			return ""
-		}
-		return strings.TrimSpace(string(out))
+		return ver
 	}
 
 	return ""
diff --git a/tree/tree.go b/tree/tree.go
index 1d9def2..5b19336 100644
--- a/tree/tree.go
+++ b/tree/tree.go
@@ -172,10 +172,11 @@
 		// where Google products are playing with each other.
 		// https://blog.golang.org/the-app-engine-sdk-and-workspaces-gopath
 		info.Loc = dependency.LocAppengine
-	} else if name == "context" {
-		// context is a package being added to the Go 1.7 standard library. Some
-		// packages, such as golang.org/x/net are importing it with build flags
-		// in files for go1.7. Need to detect this and handle it.
+	} else if name == "context" || name == "net/http/httptrace" {
+		// context and net/http/httptrace are packages being added to
+		// the Go 1.7 standard library. Some packages, such as golang.org/x/net
+		// are importing it with build flags in files for go1.7. Need to detect
+		// this and handle it.
 		info.Loc = dependency.LocGoroot
 	}