Merge pull request #293 from mcuelenaere/import-gomfiles

Add support for importing Gomfile's
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5acb358..3ccad6f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,17 @@
+# Release 0.9.2 (2016-03-08)
+
+- Fixed issue on #317: Some windows calls had the improper path separator.
+- Issue #315: Track updated packages to avoid duplicated work (in part by
+  thockin, thanks).
+- Fixed #312: Don't double-print SetVersion() failure (thanks thockin).
+- Fixed #311: Don't process deps if 'get' was a non-operation (thanks thockin).
+- Issue #307: Moving 'already set' to a debug message to cleanup output
+  (thanks thockin).
+- Fixed #306: Don't call SetVersion twice. There was a place where it was called
+  twice in a logical row (thanks thockin).
+- Fixed #304: Glide tries to update ignored packages.
+- Fixed #302: Force update can cause a panic.
+
 # Release 0.9.1 (2016-02-24)
 
 - Fixed #272: Handling appengine special package case.
diff --git a/action/get.go b/action/get.go
index 22e0652..b1c0a46 100644
--- a/action/get.go
+++ b/action/get.go
@@ -26,8 +26,11 @@
 	}
 
 	// Add the packages to the config.
-	if err := addPkgsToConfig(conf, names, insecure); err != nil {
+	if count, err := addPkgsToConfig(conf, names, insecure); err != nil {
 		msg.Die("Failed to get new packages: %s", err)
+	} else if count == 0 {
+		msg.Warn("Nothing to do")
+		return
 	}
 
 	// Fetch the new packages. Can't resolve versions via installer.Update if
@@ -96,10 +99,11 @@
 // - separates repo from packages
 // - sets up insecure repo URLs where necessary
 // - generates a list of subpackages
-func addPkgsToConfig(conf *cfg.Config, names []string, insecure bool) error {
+func addPkgsToConfig(conf *cfg.Config, names []string, insecure bool) (int, error) {
 
 	msg.Info("Preparing to install %d package.", len(names))
 
+	numAdded := 0
 	for _, name := range names {
 		var version string
 		parts := strings.Split(name, "#")
@@ -110,7 +114,7 @@
 
 		root, subpkg := util.NormalizeName(name)
 		if len(root) == 0 {
-			return fmt.Errorf("Package name is required for %q.", name)
+			return 0, fmt.Errorf("Package name is required for %q.", name)
 		}
 
 		if conf.HasDependency(root) {
@@ -123,6 +127,7 @@
 				} else {
 					dep.Subpackages = append(dep.Subpackages, subpkg)
 					msg.Info("Adding sub-package %s to existing import %s", subpkg, root)
+					numAdded++
 				}
 			} else {
 				msg.Warn("Package %q is already in glide.yaml. Skipping", root)
@@ -160,6 +165,7 @@
 		}
 
 		conf.Imports = append(conf.Imports, dep)
+		numAdded++
 	}
-	return nil
+	return numAdded, nil
 }
diff --git a/dependency/resolver.go b/dependency/resolver.go
index 1d40cfa..6c00a41 100644
--- a/dependency/resolver.go
+++ b/dependency/resolver.go
@@ -460,7 +460,6 @@
 					} else {
 						msg.Warn("Error updating %s: %s", imp, err)
 					}
-					r.VersionHandler.SetVersion(imp)
 				}
 			case LocUnknown:
 				msg.Debug("Missing %s. Trying to resolve.", imp)
@@ -498,6 +497,12 @@
 		t := r.stripv(e.Value.(string))
 		root, sp := util.NormalizeName(t)
 
+		// Skip ignored packages
+		if r.Config.HasIgnore(e.Value.(string)) {
+			msg.Info("Ignoring: %s", e.Value.(string))
+			continue
+		}
+
 		// TODO(mattfarina): Need to eventually support devImport
 		existing := r.Config.Imports.Get(root)
 		if existing != nil {
diff --git a/glide.go b/glide.go
index 4d889cc..6d4bed5 100644
--- a/glide.go
+++ b/glide.go
@@ -219,14 +219,13 @@
 					msg.Warn("Only resolving dependencies for the current OS/Arch")
 				}
 
-				inst := &repo.Installer{
-					Force:           c.Bool("force"),
-					UseCache:        c.Bool("cache"),
-					UseGopath:       c.Bool("use-gopath"),
-					UseCacheGopath:  c.Bool("cache-gopath"),
-					UpdateVendored:  c.Bool("update-vendored"),
-					ResolveAllFiles: c.Bool("all-dependencies"),
-				}
+				inst := repo.NewInstaller()
+				inst.Force = c.Bool("force")
+				inst.UseCache = c.Bool("cache")
+				inst.UseGopath = c.Bool("use-gopath")
+				inst.UseCacheGopath = c.Bool("cache-gopath")
+				inst.UpdateVendored = c.Bool("update-vendored")
+				inst.ResolveAllFiles = c.Bool("all-dependencies")
 				packages := []string(c.Args())
 				insecure := c.Bool("insecure")
 				action.Get(packages, inst, insecure, c.Bool("no-recursive"))
@@ -259,10 +258,8 @@
 					// FIXME: Implement this in the installer.
 					fmt.Println("Delete is not currently implemented.")
 				}
-
-				inst := &repo.Installer{
-					Force: c.Bool("force"),
-				}
+				inst := repo.NewInstaller()
+				inst.Force = c.Bool("force")
 				packages := []string(c.Args())
 				action.Remove(packages, inst)
 			},
@@ -409,15 +406,14 @@
 				},
 			},
 			Action: func(c *cli.Context) {
-				installer := &repo.Installer{
-					DeleteUnused:   c.Bool("deleteOptIn"),
-					UpdateVendored: c.Bool("update-vendored"),
-					Force:          c.Bool("force"),
-					UseCache:       c.Bool("cache"),
-					UseCacheGopath: c.Bool("cache-gopath"),
-					UseGopath:      c.Bool("use-gopath"),
-					Home:           gpath.Home(),
-				}
+				installer := repo.NewInstaller()
+				installer.Force = c.Bool("force")
+				installer.UseCache = c.Bool("cache")
+				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")
 
 				action.Install(installer)
 			},
@@ -500,16 +496,15 @@
 					msg.Warn("Only resolving dependencies for the current OS/Arch")
 				}
 
-				installer := &repo.Installer{
-					DeleteUnused:    c.Bool("deleteOptIn"),
-					UpdateVendored:  c.Bool("update-vendored"),
-					ResolveAllFiles: c.Bool("all-dependencies"),
-					Force:           c.Bool("force"),
-					UseCache:        c.Bool("cache"),
-					UseCacheGopath:  c.Bool("cache-gopath"),
-					UseGopath:       c.Bool("use-gopath"),
-					Home:            gpath.Home(),
-				}
+				installer := repo.NewInstaller()
+				installer.Force = c.Bool("force")
+				installer.UseCache = c.Bool("cache")
+				installer.UseGopath = c.Bool("use-gopath")
+				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")
 
 				action.Update(installer, c.Bool("no-recursive"))
 			},
diff --git a/glide.lock b/glide.lock
index 8655963..8087d1c 100644
--- a/glide.lock
+++ b/glide.lock
@@ -1,12 +1,12 @@
-hash: 9057b90e0cd3fac45ad563ea3d62a6e57c845e5c07e68e87b6fc14818574eb4f
-updated: 2016-02-17T09:11:15.318323635-05:00
+hash: 1b76070c56de9f698ebbd32b2f36d2c972888fc0793f7843b0925e4faca65d4b
+updated: 2016-03-07T15:05:34.774169565-05:00
 imports:
 - name: github.com/codegangsta/cli
   version: c31a7975863e7810c92e2e288a9ab074f9a88f29
 - name: github.com/Masterminds/semver
   version: 513f3dcb3ecfb1248831fb5cb06a23a3cd5935dc
 - name: github.com/Masterminds/vcs
-  version: 9c0db6583837118d5df7c2ae38ab1c194e434b35
+  version: 242477a09d9db06a848c5305525168f042d96871
 - name: gopkg.in/yaml.v2
   version: f7716cbe52baa25d2e9b0d0da546fcf909fc16b4
 devImports: []
diff --git a/glide.yaml b/glide.yaml
index 59e169d..c270097 100644
--- a/glide.yaml
+++ b/glide.yaml
@@ -11,7 +11,7 @@
 import:
 - package: gopkg.in/yaml.v2
 - package: github.com/Masterminds/vcs
-  version: ^1.4.0
+  version: ^1.4.1
 - package: github.com/codegangsta/cli
 - package: github.com/Masterminds/semver
   version: ^1.0.0
diff --git a/repo/installer.go b/repo/installer.go
index 1ceaec1..bebc3df 100644
--- a/repo/installer.go
+++ b/repo/installer.go
@@ -47,6 +47,15 @@
 	// of every file of every package, rather than only following imported
 	// packages.
 	ResolveAllFiles bool
+
+	// Updated tracks the packages that have been remotely fetched.
+	Updated *UpdateTracker
+}
+
+func NewInstaller() *Installer {
+	i := &Installer{}
+	i.Updated = NewUpdateTracker()
+	return i
 }
 
 // VendorPath returns the path to the location to put vendor packages
@@ -157,6 +166,7 @@
 		updateVendored: i.UpdateVendored,
 		Config:         conf,
 		Use:            ic,
+		updated:        i.Updated,
 	}
 
 	v := &VersionHandler{
@@ -244,7 +254,7 @@
 				select {
 				case dep := <-ch:
 					dest := filepath.Join(i.VendorPath(), dep.Name)
-					if err := VcsUpdate(dep, dest, i.Home, i.UseCache, i.UseCacheGopath, i.UseGopath, i.Force, i.UpdateVendored); err != nil {
+					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)
 						// Capture the error while making sure the concurrent
 						// operations don't step on each other.
@@ -312,6 +322,7 @@
 	cache, cacheGopath, useGopath, force, updateVendored bool
 	Config                                               *cfg.Config
 	Use                                                  *importCache
+	updated                                              *UpdateTracker
 }
 
 // NotFound attempts to retrieve a package when not found in the local vendor/
@@ -429,7 +440,7 @@
 		m.Config.Imports = append(m.Config.Imports, d)
 	}
 
-	if err := VcsUpdate(d, dest, m.home, m.cache, m.cacheGopath, m.useGopath, m.force, m.updateVendored); err != nil {
+	if err := VcsUpdate(d, dest, m.home, m.cache, m.cacheGopath, m.useGopath, m.force, m.updateVendored, m.updated); err != nil {
 		return err
 	}
 
diff --git a/repo/tracker.go b/repo/tracker.go
new file mode 100644
index 0000000..c017ffd
--- /dev/null
+++ b/repo/tracker.go
@@ -0,0 +1,42 @@
+package repo
+
+import (
+	"sync"
+)
+
+// UpdateTracker holds a list of all the packages that have been updated from
+// an external source. This is a concurrency safe implementation.
+type UpdateTracker struct {
+	sync.RWMutex
+
+	updated map[string]bool
+}
+
+// NewUpdateTracker creates a new instance of UpdateTracker ready for use.
+func NewUpdateTracker() *UpdateTracker {
+	u := &UpdateTracker{}
+	u.updated = map[string]bool{}
+	return u
+}
+
+// Add adds a name to the list of items being tracked.
+func (u *UpdateTracker) Add(name string) {
+	u.Lock()
+	u.updated[name] = true
+	u.Unlock()
+}
+
+// Check returns if an item is on the list or not.
+func (u *UpdateTracker) Check(name string) bool {
+	u.RLock()
+	_, f := u.updated[name]
+	u.RUnlock()
+	return f
+}
+
+// Remove takes a package off the list
+func (u *UpdateTracker) Remove(name string) {
+	u.Lock()
+	delete(u.updated, name)
+	u.Unlock()
+}
diff --git a/repo/tracker_test.go b/repo/tracker_test.go
new file mode 100644
index 0000000..94150f2
--- /dev/null
+++ b/repo/tracker_test.go
@@ -0,0 +1,23 @@
+package repo
+
+import "testing"
+
+func TestUpdateTracker(t *testing.T) {
+	tr := NewUpdateTracker()
+
+	if f := tr.Check("github.com/foo/bar"); f != false {
+		t.Error("Error, package Check passed on empty tracker")
+	}
+
+	tr.Add("github.com/foo/bar")
+
+	if f := tr.Check("github.com/foo/bar"); f != true {
+		t.Error("Error, failed to add package to tracker")
+	}
+
+	tr.Remove("github.com/foo/bar")
+
+	if f := tr.Check("github.com/foo/bar"); f != false {
+		t.Error("Error, failed to remove package from tracker")
+	}
+}
diff --git a/repo/vcs.go b/repo/vcs.go
index 9ff76c7..5cc0f42 100644
--- a/repo/vcs.go
+++ b/repo/vcs.go
@@ -21,7 +21,7 @@
 )
 
 // VcsUpdate updates to a particular checkout based on the VCS setting.
-func VcsUpdate(dep *cfg.Dependency, dest, home string, cache, cacheGopath, useGopath, force, updateVendored bool) error {
+func VcsUpdate(dep *cfg.Dependency, dest, home string, cache, cacheGopath, useGopath, force, updateVendored bool, updated *UpdateTracker) error {
 
 	// If the dependency has already been pinned we can skip it. This is a
 	// faster path so we don't need to resolve it again.
@@ -30,6 +30,12 @@
 		return nil
 	}
 
+	if updated.Check(dep.Name) {
+		msg.Debug("%s was already updated, skipping.", dep.Name)
+		return nil
+	}
+	updated.Add(dep.Name)
+
 	msg.Info("Fetching updates for %s.\n", dep.Name)
 
 	if filterArchOs(dep) {
@@ -111,6 +117,11 @@
 					msg.Warn("Unable to checkout %s\n", dep.Name)
 					return err
 				}
+
+				repo, err = dep.GetRepo(dest)
+				if err != nil {
+					return err
+				}
 			} else if err != nil {
 				return err
 			} else if repo.IsDirty() {
@@ -135,7 +146,7 @@
 				// branch it's a tag or commit id so we can skip
 				// performing an update.
 				if version == dep.Reference && !ib {
-					msg.Info("%s is already set to version %s. Skipping update.", dep.Name, dep.Reference)
+					msg.Debug("%s is already set to version %s. Skipping update.", dep.Name, dep.Reference)
 					return nil
 				}
 			}
@@ -237,7 +248,6 @@
 			}
 		}
 		if err := repo.UpdateVersion(ver); err != nil {
-			msg.Err("Failed to set version to %s: %s\n", dep.Reference, err)
 			return err
 		}
 		dep.Pin, err = repo.Version()
diff --git a/util/util.go b/util/util.go
index d73001a..5cdc0f4 100644
--- a/util/util.go
+++ b/util/util.go
@@ -35,6 +35,7 @@
 // the package github.com/Masterminds/cookoo/io has a root repo
 // at github.com/Masterminds/cookoo
 func GetRootFromPackage(pkg string) string {
+	pkg = filepath.ToSlash(pkg)
 	for _, v := range vcsList {
 		m := v.regex.FindStringSubmatch(pkg)
 		if m == nil {
@@ -306,7 +307,6 @@
 		}
 	}
 
-	name = filepath.ToSlash(name)
 	root := GetRootFromPackage(name)
 	extra := strings.TrimPrefix(name, root)
 	if len(extra) > 0 && extra != "/" {