Catch back up to master again
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9c1187a..68828a2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,25 @@
+# Unreleased
+
+## Added
+- #533: Log VCS output with debug (`--debug` switch) when there was a VCS error (thanks @atombender)
+
+## Changed
+- #521: Sort subpackages for glide.yaml and glide.lock to avoid spurious diffs
+- #487: Skip lookup of subpackage location when parent repo is already known.
+  This skips unnecessary network requests (thanks @hori-ryota)
+
+## Fixed
+- #517: Fixed failure to install testImport from lock when no imports present
+  or when same dependency on both import and testImport
+- #440: Fixed panic in `glide tree` when walking the filesystem (thanks @abhin4v)
+- #529: --delete flag deleted and re-downloaded transitive dependencies
+- #535: Resolve vendor directory symlinks (thanks @Fugiman)
+
+# Release 0.11.1 (2016-07-21)
+
+## Fixed
+- #505: Ignored dependency showing up in testImport
+
 # Release 0.11.0 (2016-07-05)
 
 ## Added
diff --git a/action/get.go b/action/get.go
index ff9ed7d..8b29cdb 100644
--- a/action/get.go
+++ b/action/get.go
@@ -116,7 +116,10 @@
 	if err != nil {
 		msg.Die("Failed to generate config hash. Unable to generate lock file.")
 	}
-	lock := cfg.NewLockfile(confcopy.Imports, confcopy.DevImports, hash)
+	lock, err := cfg.NewLockfile(confcopy.Imports, confcopy.DevImports, hash)
+	if err != nil {
+		msg.Die("Failed to generate lock file: %s", err)
+	}
 	if err := lock.WriteFile(filepath.Join(base, gpath.LockFile)); err != nil {
 		msg.Die("Failed to write glide lock file: %s", err)
 	}
diff --git a/cfg/config.go b/cfg/config.go
index 3046a74..a9e6930 100644
--- a/cfg/config.go
+++ b/cfg/config.go
@@ -6,6 +6,7 @@
 	"fmt"
 	"io/ioutil"
 	"reflect"
+	"sort"
 	"strings"
 
 	"github.com/Masterminds/glide/util"
@@ -693,6 +694,7 @@
 			s = append(s, item)
 		}
 	}
+	sort.Strings(s)
 	return s
 }
 
diff --git a/cfg/lock.go b/cfg/lock.go
index ac03f91..b822f29 100644
--- a/cfg/lock.go
+++ b/cfg/lock.go
@@ -3,6 +3,7 @@
 import (
 	"crypto/sha256"
 	"encoding/hex"
+	"fmt"
 	"io/ioutil"
 	"sort"
 	"strings"
@@ -76,6 +77,39 @@
 	return yml, nil
 }
 
+// MarshalYAML is a hook for gopkg.in/yaml.v2.
+// It sorts import subpackages lexicographically for reproducibility.
+func (lf *Lockfile) MarshalYAML() (interface{}, error) {
+	for _, imp := range lf.Imports {
+		sort.Strings(imp.Subpackages)
+	}
+
+	// Ensure elements on testImport don't already exist on import.
+	var newDI Locks
+	var found bool
+	for _, imp := range lf.DevImports {
+		found = false
+		for i := 0; i < len(lf.Imports); i++ {
+			if lf.Imports[i].Name == imp.Name {
+				found = true
+				if lf.Imports[i].Version != imp.Version {
+					return lf, fmt.Errorf("Generating lock YAML produced conflicting versions of %s. import (%s), testImport (%s)", imp.Name, lf.Imports[i].Version, imp.Version)
+				}
+			}
+		}
+
+		if !found {
+			newDI = append(newDI, imp)
+		}
+	}
+	lf.DevImports = newDI
+
+	for _, imp := range lf.DevImports {
+		sort.Strings(imp.Subpackages)
+	}
+	return lf, nil
+}
+
 // WriteFile writes a Glide lock file.
 //
 // This is a convenience function that marshals the YAML and then writes it to
@@ -238,7 +272,7 @@
 }
 
 // NewLockfile is used to create an instance of Lockfile.
-func NewLockfile(ds, tds Dependencies, hash string) *Lockfile {
+func NewLockfile(ds, tds Dependencies, hash string) (*Lockfile, error) {
 	lf := &Lockfile{
 		Hash:       hash,
 		Updated:    time.Now(),
@@ -252,13 +286,26 @@
 
 	sort.Sort(lf.Imports)
 
+	var found bool
 	for i := 0; i < len(tds); i++ {
-		lf.DevImports[i] = LockFromDependency(tds[i])
+		found = false
+		for ii := 0; ii < len(ds); ii++ {
+			if ds[ii].Name == tds[i].Name {
+				found = true
+				if ds[ii].Reference != tds[i].Reference {
+					return &Lockfile{}, fmt.Errorf("Generating lock produced conflicting versions of %s. import (%s), testImport (%s)", tds[i].Name, ds[ii].Reference, tds[i].Reference)
+				}
+				break
+			}
+		}
+		if !found {
+			lf.DevImports[i] = LockFromDependency(tds[i])
+		}
 	}
 
 	sort.Sort(lf.DevImports)
 
-	return lf
+	return lf, nil
 }
 
 // LockfileFromMap takes a map of dependencies and generates a lock Lockfile instance.
diff --git a/cfg/lock_test.go b/cfg/lock_test.go
index 78dcee3..81edcd0 100644
--- a/cfg/lock_test.go
+++ b/cfg/lock_test.go
@@ -2,6 +2,7 @@
 
 import (
 	"sort"
+	"strings"
 	"testing"
 )
 
@@ -32,3 +33,80 @@
 		t.Error("Sorting of dependencies failed")
 	}
 }
+
+const inputSubpkgYaml = `
+imports:
+- name: github.com/gogo/protobuf
+  version: 82d16f734d6d871204a3feb1a73cb220cc92574c
+  subpackages:
+  - plugin/equal
+  - sortkeys
+  - plugin/face
+  - plugin/gostring
+  - vanity
+  - plugin/grpc
+  - plugin/marshalto
+  - plugin/populate
+  - plugin/oneofcheck
+  - plugin/size
+  - plugin/stringer
+  - plugin/defaultcheck
+  - plugin/embedcheck
+  - plugin/description
+  - plugin/enumstringer
+  - gogoproto
+  - plugin/testgen
+  - plugin/union
+  - plugin/unmarshal
+  - protoc-gen-gogo/generator
+  - protoc-gen-gogo/plugin
+  - vanity/command
+  - protoc-gen-gogo/descriptor
+  - proto
+`
+const expectSubpkgYaml = `
+imports:
+- name: github.com/gogo/protobuf
+  version: 82d16f734d6d871204a3feb1a73cb220cc92574c
+  subpackages:
+  - gogoproto
+  - plugin/defaultcheck
+  - plugin/description
+  - plugin/embedcheck
+  - plugin/enumstringer
+  - plugin/equal
+  - plugin/face
+  - plugin/gostring
+  - plugin/grpc
+  - plugin/marshalto
+  - plugin/oneofcheck
+  - plugin/populate
+  - plugin/size
+  - plugin/stringer
+  - plugin/testgen
+  - plugin/union
+  - plugin/unmarshal
+  - proto
+  - protoc-gen-gogo/descriptor
+  - protoc-gen-gogo/generator
+  - protoc-gen-gogo/plugin
+  - sortkeys
+  - vanity
+  - vanity/command
+`
+
+func TestSortSubpackages(t *testing.T) {
+	lf, err := LockfileFromYaml([]byte(inputSubpkgYaml))
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	out, err := lf.Marshal()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if !strings.Contains(string(out), expectSubpkgYaml) {
+		t.Errorf("Expected %q\nto contain\n%q")
+	}
+}
diff --git a/dependency/delete.go b/dependency/delete.go
index 39a6367..6456370 100644
--- a/dependency/delete.go
+++ b/dependency/delete.go
@@ -28,6 +28,9 @@
 	for _, dep := range conf.Imports {
 		pkgList = append(pkgList, dep.Name)
 	}
+	for _, dep := range conf.DevImports {
+		pkgList = append(pkgList, dep.Name)
+	}
 
 	var searchPath string
 	var markForDelete []string
diff --git a/godep/strip/strip.go b/godep/strip/strip.go
index e70d607..39d54e1 100644
--- a/godep/strip/strip.go
+++ b/godep/strip/strip.go
@@ -82,11 +82,11 @@
 
 func rewriteGodepfilesHandler(path string, info os.FileInfo, err error) error {
 	name := info.Name()
-	if name == "testdata" || name == "vendor" {
-		return filepath.SkipDir
-	}
 
 	if info.IsDir() {
+		if name == "testdata" || name == "vendor" {
+			return filepath.SkipDir
+		}
 		return nil
 	}
 
diff --git a/msg/msg.go b/msg/msg.go
index 34213fd..8b0f077 100644
--- a/msg/msg.go
+++ b/msg/msg.go
@@ -7,6 +7,8 @@
 	"os"
 	"strings"
 	"sync"
+
+	"github.com/Masterminds/vcs"
 )
 
 // Messenger provides the underlying implementation that displays output to
@@ -176,7 +178,6 @@
 	// locked to avoid displaying one message in the middle of another one.
 	m.Lock()
 	defer m.Unlock()
-
 	// Get rid of the annoying fact that messages need \n at the end, but do
 	// it in a backward compatible way.
 	if !strings.HasSuffix(msg, "\n") {
@@ -188,6 +189,21 @@
 	} else {
 		fmt.Fprintf(m.Stderr, msg, args...)
 	}
+
+	// If an arg is a vcs error print the output if in debug mode. This is
+	// capured here rather than calling Debug because concurrent operations
+	// could cause other messages to appear between the initial error and the
+	// debug output by unlocking and calling Debug.
+	if len(args) != 0 && !m.Quiet && m.IsDebugging {
+		if err, ok := args[len(args)-1].(error); ok {
+			switch t := err.(type) {
+			case *vcs.LocalError:
+				fmt.Fprintf(m.Stderr, "[DEBUG]\tOutput was: %s", strings.TrimSpace(t.Out()))
+			case *vcs.RemoteError:
+				fmt.Fprintf(m.Stderr, "[DEBUG]\tOutput was: %s", strings.TrimSpace(t.Out()))
+			}
+		}
+	}
 }
 
 // Msg prints a message with optional arguments, that can be printed, of
diff --git a/path/path.go b/path/path.go
index 0b19442..0c3cee1 100644
--- a/path/path.go
+++ b/path/path.go
@@ -80,6 +80,29 @@
 
 	gopath := filepath.Join(yamldir, VendorDir)
 
+	// Resolve symlinks
+	info, err := os.Lstat(gopath)
+	if err != nil {
+		return gopath, nil
+	}
+	for i := 0; IsLink(info) && i < 255; i++ {
+		p, err := os.Readlink(gopath)
+		if err != nil {
+			return gopath, nil
+		}
+
+		if filepath.IsAbs(p) {
+			gopath = p
+		} else {
+			gopath = filepath.Join(filepath.Dir(gopath), p)
+		}
+
+		info, err = os.Lstat(gopath)
+		if err != nil {
+			return gopath, nil
+		}
+	}
+
 	return gopath, nil
 }
 
diff --git a/path/path_test.go b/path/path_test.go
index 0c8a221..77a0276 100644
--- a/path/path_test.go
+++ b/path/path_test.go
@@ -34,6 +34,7 @@
 		t.Fatal(err)
 	}
 	wd, _ := os.Getwd()
+
 	os.Chdir(filepath.Join(td, "a/b/c"))
 	res, err := Vendor()
 	if err != nil {
@@ -43,6 +44,17 @@
 	if res != expect {
 		t.Errorf("Failed to find vendor: expected %s got %s", expect, res)
 	}
+
+	os.Chdir(filepath.Join(td, "x/y/z"))
+	res, err = Vendor()
+	if err != nil {
+		t.Errorf("Failed to resolve vendor directory: %s", err)
+	}
+	expect = filepath.Join(td, "x", "symlinked_vendor")
+	if res != expect {
+		t.Errorf("Failed to find vendor: expected %s got %s", expect, res)
+	}
+
 	os.Chdir(wd)
 }
 func TestGlide(t *testing.T) {
diff --git a/path/strip.go b/path/strip.go
index 1162f38..be00be9 100644
--- a/path/strip.go
+++ b/path/strip.go
@@ -11,36 +11,36 @@
 // StripVcs removes VCS metadata (.git, .hg, .bzr, .svn) from the vendor/
 // directory.
 func StripVcs() error {
-	if _, err := os.Stat(VendorDir); err != nil {
+	searchPath, _ := Vendor()
+	if _, err := os.Stat(searchPath); err != nil {
 		if os.IsNotExist(err) {
 			msg.Debug("Vendor directory does not exist.")
 		}
 
 		return err
 	}
-	return filepath.Walk(VendorDir, stripHandler)
-}
 
-func stripHandler(path string, info os.FileInfo, err error) error {
+	return filepath.Walk(searchPath, func(path string, info os.FileInfo, err error) error {
+		name := info.Name()
+		if name == ".git" || name == ".bzr" || name == ".svn" || name == ".hg" {
+			if _, err := os.Stat(path); err == nil {
+				if info.IsDir() {
+					msg.Info("Removing: %s", path)
+					return os.RemoveAll(path)
+				}
 
-	name := info.Name()
-	if name == ".git" || name == ".bzr" || name == ".svn" || name == ".hg" {
-		if _, err := os.Stat(path); err == nil {
-			if info.IsDir() {
-				msg.Info("Removing: %s", path)
-				return os.RemoveAll(path)
+				msg.Debug("%s is not a directory. Skipping removal", path)
+				return nil
 			}
-
-			msg.Debug("%s is not a directory. Skipping removal", path)
-			return nil
 		}
-	}
-	return nil
+		return nil
+	})
 }
 
 // StripVendor removes nested vendor and Godeps/_workspace/ directories.
 func StripVendor() error {
-	if _, err := os.Stat(VendorDir); err != nil {
+	searchPath, _ := Vendor()
+	if _, err := os.Stat(searchPath); err != nil {
 		if os.IsNotExist(err) {
 			msg.Debug("Vendor directory does not exist.")
 		}
@@ -48,33 +48,29 @@
 		return err
 	}
 
-	err := filepath.Walk(VendorDir, stripVendorHandler)
+	err := filepath.Walk(searchPath, func(path string, info os.FileInfo, err error) error {
+		// Skip the base vendor directory
+		if path == searchPath {
+			return nil
+		}
+
+		name := info.Name()
+		if name == "vendor" {
+			if _, err := os.Stat(path); err == nil {
+				if info.IsDir() {
+					msg.Info("Removing: %s", path)
+					return os.RemoveAll(path)
+				}
+
+				msg.Debug("%s is not a directory. Skipping removal", path)
+				return nil
+			}
+		}
+		return nil
+	})
 	if err != nil {
 		return err
 	}
 
-	err = strip.GodepWorkspace(VendorDir)
-
-	return err
-}
-
-func stripVendorHandler(path string, info os.FileInfo, err error) error {
-	// Skip the base vendor directory
-	if path == VendorDir {
-		return nil
-	}
-
-	name := info.Name()
-	if name == "vendor" {
-		if _, err := os.Stat(path); err == nil {
-			if info.IsDir() {
-				msg.Info("Removing: %s", path)
-				return os.RemoveAll(path)
-			}
-
-			msg.Debug("%s is not a directory. Skipping removal", path)
-			return nil
-		}
-	}
-	return nil
+	return strip.GodepWorkspace(searchPath)
 }
diff --git a/path/strip_test.go b/path/strip_test.go
index 6c11194..23e9d94 100644
--- a/path/strip_test.go
+++ b/path/strip_test.go
@@ -21,36 +21,44 @@
 	}()
 
 	// Make VCS directories.
-	gp := filepath.Join(tempDir, ".git")
+	v := filepath.Join(tempDir, VendorDir)
+	err = os.Mkdir(v, 0755)
+	if err != nil {
+		t.Error(err)
+	}
+
+	gp := filepath.Join(tempDir, VendorDir, ".git")
 	err = os.Mkdir(gp, 0755)
 	if err != nil {
 		t.Error(err)
 	}
 
-	bp := filepath.Join(tempDir, ".bzr")
+	bp := filepath.Join(tempDir, VendorDir, ".bzr")
 	err = os.Mkdir(bp, 0755)
 	if err != nil {
 		t.Error(err)
 	}
 
-	hp := filepath.Join(tempDir, ".hg")
+	hp := filepath.Join(tempDir, VendorDir, ".hg")
 	err = os.Mkdir(hp, 0755)
 	if err != nil {
 		t.Error(err)
 	}
 
-	sp := filepath.Join(tempDir, ".svn")
+	sp := filepath.Join(tempDir, VendorDir, ".svn")
 	err = os.Mkdir(sp, 0755)
 	if err != nil {
 		t.Error(err)
 	}
 
-	ov := VendorDir
-	VendorDir = tempDir
+	wd, _ := os.Getwd()
+	os.Chdir(tempDir)
 
-	StripVcs()
+	if err := StripVcs(); err != nil {
+		t.Errorf("Failed to strip vcs: %s", err)
+	}
 
-	VendorDir = ov
+	os.Chdir(wd)
 
 	if _, err := os.Stat(gp); !os.IsNotExist(err) {
 		t.Error(".git directory not deleted")
diff --git a/repo/installer.go b/repo/installer.go
index cce24ef..0d6a335 100644
--- a/repo/installer.go
+++ b/repo/installer.go
@@ -104,8 +104,8 @@
 
 	newConf.DeDupe()
 
-	if len(newConf.Imports) == 0 {
-		msg.Info("No dependencies found. Nothing installed.\n")
+	if len(newConf.Imports) == 0 && len(newConf.DevImports) == 0 {
+		msg.Info("No dependencies found. Nothing installed.")
 		return newConf, nil
 	}
 
@@ -192,6 +192,9 @@
 	var tdeps cfg.Dependencies
 	for _, v := range imps {
 		n := res.Stripv(v)
+		if conf.HasIgnore(n) {
+			continue
+		}
 		rt, sub := util.NormalizeName(n)
 		if sub == "" {
 			sub = "."
@@ -210,6 +213,9 @@
 	if i.ResolveTest {
 		for _, v := range timps {
 			n := res.Stripv(v)
+			if conf.HasIgnore(n) {
+				continue
+			}
 			rt, sub := util.NormalizeName(n)
 			if sub == "" {
 				sub = "."
diff --git a/repo/set_reference.go b/repo/set_reference.go
index 4363459..f5891ac 100644
--- a/repo/set_reference.go
+++ b/repo/set_reference.go
@@ -3,9 +3,11 @@
 import (
 	"sync"
 
+	"github.com/Masterminds/glide/cache"
 	"github.com/Masterminds/glide/cfg"
 	"github.com/Masterminds/glide/msg"
 	gpath "github.com/Masterminds/glide/path"
+	"github.com/codegangsta/cli"
 )
 
 // SetReference is a command to set the VCS reference (commit id, tag, etc) for
@@ -25,15 +27,39 @@
 	done := make(chan struct{}, concurrentWorkers)
 	in := make(chan *cfg.Dependency, concurrentWorkers)
 	var wg sync.WaitGroup
+	var lock sync.Mutex
+	var returnErr error
 
 	for i := 0; i < concurrentWorkers; i++ {
 		go func(ch <-chan *cfg.Dependency) {
 			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)
 					if err := VcsVersion(dep, cwd); err != nil {
 						msg.Err("Failed to set version on %s to %s: %s\n", dep.Name, dep.Reference, err)
+
+						// Capture the error while making sure the concurrent
+						// operations don't step on each other.
+						lock.Lock()
+						if returnErr == nil {
+							returnErr = err
+						} else {
+							returnErr = cli.NewMultiError(returnErr, err)
+						}
+						lock.Unlock()
 					}
+					cache.Unlock(key)
 					wg.Done()
 				case <-done:
 					return
@@ -66,5 +92,5 @@
 	// close(done)
 	// close(in)
 
-	return nil
+	return returnErr
 }
diff --git a/testdata/path/x/glide.yaml b/testdata/path/x/glide.yaml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testdata/path/x/glide.yaml
diff --git a/testdata/path/x/vendor b/testdata/path/x/vendor
new file mode 120000
index 0000000..5b13353
--- /dev/null
+++ b/testdata/path/x/vendor
@@ -0,0 +1 @@
+symlinked_vendor
\ No newline at end of file
diff --git a/testdata/path/x/y/z/placeholder.empty b/testdata/path/x/y/z/placeholder.empty
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testdata/path/x/y/z/placeholder.empty
diff --git a/tree/tree.go b/tree/tree.go
index 0dc3df7..e16478c 100644
--- a/tree/tree.go
+++ b/tree/tree.go
@@ -45,6 +45,10 @@
 func walkDeps(b *util.BuildCtxt, base, myName string) []string {
 	externalDeps := []string{}
 	filepath.Walk(base, func(path string, fi os.FileInfo, err error) error {
+		if err != nil {
+			return err
+		}
+
 		if !dependency.IsSrcDir(fi) {
 			if fi.IsDir() {
 				return filepath.SkipDir
diff --git a/util/normalizename_test.go b/util/normalizename_test.go
index c6f4d02..d5a454b 100644
--- a/util/normalizename_test.go
+++ b/util/normalizename_test.go
@@ -31,11 +31,18 @@
 			extra: "",
 		},
 		{
+			input: "otherurl/example/root/sub",
+			root:  "otherurl/example/root",
+			extra: "sub",
+		},
+		{
 			input: "net",
 			root:  "net",
 			extra: "",
 		},
 	}
+	remotePackageCache["otherurl/example/root"] = "otherurl/example/root"
+
 	for _, test := range packages {
 		root, extra := NormalizeName(test.input)
 		if root != test.root {
diff --git a/util/util.go b/util/util.go
index a5b346f..f0854d2 100644
--- a/util/util.go
+++ b/util/util.go
@@ -110,7 +110,7 @@
 
 func checkRemotePackageCache(pkg string) (string, bool) {
 	for k, v := range remotePackageCache {
-		if pkg == k {
+		if pkg == k || strings.HasPrefix(pkg, k + "/") {
 			return v, true
 		}
 	}
@@ -290,7 +290,7 @@
 		out, err := exec.Command(goExecutable, "env", "GOROOT").Output()
 		if goRoot = strings.TrimSpace(string(out)); len(goRoot) == 0 || err != nil {
 			return nil, fmt.Errorf("Please set the $GOROOT environment " +
-				"variable to use this command\n")
+			"variable to use this command\n")
 		}
 		buildContext.GOROOT = goRoot
 	}