Back to string storage on Dependency

It's much cleaner to do it this way, and build the Constraint on demand
via a method.
diff --git a/action/config_wizard.go b/action/config_wizard.go
index 1cc6da7..3bb6e14 100644
--- a/action/config_wizard.go
+++ b/action/config_wizard.go
@@ -18,9 +18,6 @@
 
 // ConfigWizard reads configuration from a glide.yaml file and attempts to suggest
 // improvements. The wizard is interactive.
-//
-// TODO(sdboyer) the Dependency.Reference -> Dependency.Constraint conversion is
-// really awkward here and needs to be revisited
 func ConfigWizard(base string) {
 	_, err := gpath.Glide()
 	glidefile := gpath.GlideFile
@@ -80,7 +77,7 @@
 
 		// First check, ask if the tag should be used instead of the commit id for it.
 		cur := cache.MemCurrent(remote)
-		if cur != "" && cur != dep.Constraint.String() {
+		if cur != "" && cur != dep.Version {
 			wizardSugOnce()
 			var dres bool
 			asked, use, val := wizardOnce("current")
@@ -97,16 +94,15 @@
 			}
 
 			if dres {
-				msg.Info("Updating %s to use the tag %s instead of commit id %s", dep.Name, cur, dep.Constraint)
-				// FIXME just wrong; have to disambiguate branches and versions
-				dep.Constraint = gps.NewVersion(cur)
+				msg.Info("Updating %s to use the tag %s instead of commit id %s", dep.Name, cur, dep.Version)
+				dep.Version = cur
 				changes++
 			}
 		}
 
 		// Second check, if no version is being used and there's a semver release ask about latest.
 		memlatest := cache.MemLatest(remote)
-		if dep.Constraint == nil && memlatest != "" {
+		if dep.IsUnconstrained() && memlatest != "" {
 			wizardSugOnce()
 			var dres bool
 			asked, use, val := wizardOnce("latest")
@@ -124,14 +120,14 @@
 
 			if dres {
 				msg.Info("Updating %s to use the release %s instead of no release", dep.Name, memlatest)
-				dep.Constraint = gps.NewVersion(memlatest)
+				dep.Version = memlatest
 				changes++
 			}
 		}
 
 		// Third check, if the version is semver offer to use a range instead.
-		if v, ok := dep.Constraint.(gps.Version); ok && v.Type() == "semver" {
-			sv, _ := semver.NewVersion(v.String())
+		sv, err := semver.NewVersion(dep.Version)
+		if err == nil {
 			wizardSugOnce()
 			var res string
 			asked, use, val := wizardOnce("range")
@@ -149,13 +145,13 @@
 
 			if res == "m" {
 				r := "^" + sv.String()
-				msg.Info("Updating %s to use the range %s instead of commit id %s", dep.Name, r, dep.Constraint)
-				dep.Constraint, _ = gps.NewSemverConstraint(r)
+				msg.Info("Updating %s to use the range %s instead of commit id %s", dep.Name, r, dep.Version)
+				dep.Version = r
 				changes++
 			} else if res == "p" {
 				r := "~" + sv.String()
-				msg.Info("Updating %s to use the range %s instead of commit id %s", dep.Name, r, dep.Constraint)
-				dep.Constraint, _ = gps.NewSemverConstraint(r)
+				msg.Info("Updating %s to use the range %s instead of commit id %s", dep.Name, r, dep.Version)
+				dep.Version = r
 				changes++
 			}
 		}
@@ -234,7 +230,7 @@
 }
 
 func wizardAskCurrent(cur string, d *cfg.Dependency) bool {
-	msg.Info("The package %s is currently set to use the version %s.", d.Name, d.Constraint)
+	msg.Info("The package %s is currently set to use the version %s.", d.Name, d.GetConstraint())
 	msg.Info("There is an equivalent semantic version (http://semver.org) release of %s. Would", cur)
 	msg.Info("you like to use that instead? Yes (Y) or No (N)")
 	return msg.PromptUntilYorN()
@@ -248,7 +244,7 @@
 }
 
 func wizardLookInto(d *cfg.Dependency) bool {
-	_, err := semver.NewConstraint(d.Constraint.String())
+	_, err := semver.NewConstraint(d.Version)
 
 	// The existing version is already a valid semver constraint so we skip suggestions.
 	if err == nil {
@@ -307,7 +303,7 @@
 				if found := createGitParseVersion.FindString(ti); found != "" {
 					tg := strings.TrimPrefix(strings.TrimSuffix(found, "^{}"), "tags/")
 					cache.MemPut(remote, tg)
-					if d.Constraint != nil && strings.HasPrefix(ti, d.Constraint.String()) {
+					if !d.IsUnconstrained() && strings.HasPrefix(ti, d.Version) {
 						cache.MemSetCurrent(remote, tg)
 					}
 				}
@@ -337,8 +333,8 @@
 				cache.MemPut(remote, v)
 			}
 		}
-		if d.Constraint != nil {
-			if rev, ok := d.Constraint.(gps.Revision); ok {
+		if !d.IsUnconstrained() {
+			if rev, ok := d.GetConstraint().(gps.Revision); ok {
 				tgs, err = repo.TagsFromCommit(string(rev))
 				if err != nil {
 					msg.Debug("Problem getting tags for commit: %s", err)
diff --git a/action/create.go b/action/create.go
index e6cf2e4..4abf3f6 100644
--- a/action/create.go
+++ b/action/create.go
@@ -183,10 +183,10 @@
 	}
 
 	for _, i := range deps {
-		if i.Constraint == nil {
+		if i.IsUnconstrained() {
 			msg.Info("--> Found imported reference to %s", i.Name)
 		} else {
-			msg.Info("--> Found imported reference to %s with constraint %s", i.Name, i.Constraint)
+			msg.Info("--> Found imported reference to %s with constraint %s", i.Name, i.GetConstraint())
 		}
 
 		config.Imports = append(config.Imports, i)
diff --git a/action/get.go b/action/get.go
index c7e58ac..8a3a979 100644
--- a/action/get.go
+++ b/action/get.go
@@ -184,8 +184,7 @@
 		}
 
 		dep := &cfg.Dependency{
-			Name:       root,
-			Constraint: gps.Any(),
+			Name: root,
 		}
 
 		// When retriving from an insecure location set the repo to the
@@ -196,13 +195,14 @@
 
 		if version != "" {
 			// TODO(sdboyer) set the right type...what is that here?
-			dep.Constraint = gps.NewVersion(version)
+			dep.Version = version
+			dep.Branch = "" // just to be sure
 		} else if !nonInteract {
 			getWizard(dep)
 		}
 
-		if dep.Constraint != nil {
-			msg.Info("--> Adding %s to your configuration with the version %s", dep.Name, dep.Constraint)
+		if !dep.IsUnconstrained() {
+			msg.Info("--> Adding %s to your configuration with the version %s", dep.Name, dep.GetConstraint())
 		} else {
 			msg.Info("--> Adding %s to your configuration", dep.Name)
 		}
@@ -233,20 +233,15 @@
 	if memlatest != "" {
 		dres := wizardAskLatest(memlatest, dep)
 		if dres {
-			// TODO(sdboyer) set the right type...what is that here?
-			v := gps.NewVersion(memlatest)
-			dep.Constraint = v
+			dep.Version = memlatest
 
-			if v.Type() == "semver" {
-				sv, _ := semver.NewVersion(memlatest)
+			sv, err := semver.NewVersion(memlatest)
+			if err != nil {
 				res := wizardAskRange(sv, dep)
-
 				if res == "m" {
-					// no errors possible here, if init was valid semver version
-					dep.Constraint, _ = gps.NewSemverConstraint("^" + v.String())
+					dep.Version = "^" + memlatest
 				} else if res == "p" {
-					// no errors possible here, if init was valid semver version
-					dep.Constraint, _ = gps.NewSemverConstraint("~" + v.String())
+					dep.Version = "~" + memlatest
 				}
 			}
 		}
diff --git a/cfg/config.go b/cfg/config.go
index 3f8381d..56e147f 100644
--- a/cfg/config.go
+++ b/cfg/config.go
@@ -179,7 +179,7 @@
 				ProjectRoot: gps.ProjectRoot(d.Name),
 				NetworkName: d.Repository,
 			},
-			Constraint: d.Constraint,
+			Constraint: d.GetConstraint(),
 		}
 	}
 
@@ -388,8 +388,8 @@
 			// Make sure the details are the same or return an error.
 			v := imports[val]
 			// Have to do string-based comparison
-			if dep.Constraint.String() != v.Constraint.String() {
-				return d, fmt.Errorf("Import %s repeated with different versions '%s' and '%s'", dep.Name, dep.Constraint, v.Constraint)
+			if dep.ConstraintsEq(*v) {
+				return d, fmt.Errorf("Import %s repeated with different versions '%s' and '%s'", dep.Name, dep.GetConstraint(), v.GetConstraint())
 			}
 			if dep.Repository != v.Repository {
 				return d, fmt.Errorf("Import %s repeated with different Repository details", dep.Name)
@@ -404,14 +404,15 @@
 type Dependency struct {
 	Name       string
 	VcsType    string // TODO remove
-	Constraint gps.Constraint
 	Repository string
+	Branch     string
+	Version    string
 }
 
 // A transitive representation of a dependency for yaml import/export.
 type dep struct {
 	Name       string `yaml:"package"`
-	Reference  string `yaml:"version,omitempty"` // TODO rename
+	Version    string `yaml:"version,omitempty"`
 	Branch     string `yaml:"branch,omitempty"`
 	Repository string `yaml:"repo,omitempty"`
 }
@@ -423,18 +424,61 @@
 		Repository: lock.Repository,
 	}
 
-	r := gps.Revision(lock.Revision)
+	// Because it's not allowed to have both, if we see both, prefer version
+	// over branch
 	if lock.Version != "" {
-		d.Constraint = gps.NewVersion(lock.Version).Is(r)
+		d.Version = lock.Version
 	} else if lock.Branch != "" {
-		d.Constraint = gps.NewBranch(lock.Version).Is(r)
+		d.Branch = lock.Branch
 	} else {
-		d.Constraint = r
+		d.Version = lock.Revision
 	}
 
 	return d
 }
 
+// GetConstraint constructs an appropriate gps.Constraint from the Dependency's
+// string input data.
+func (d Dependency) GetConstraint() gps.Constraint {
+	// If neither or both Version and Branch are set, accept anything
+	if d.IsUnconstrained() {
+		return gps.Any()
+	} else if d.Version != "" {
+		return DeduceConstraint(d.Version)
+	} else {
+		// only case left is a non-empty branch
+		return gps.NewBranch(d.Branch)
+	}
+}
+
+// IsUnconstrained indicates if this dependency has no constraint information,
+// version or branch.
+func (d Dependency) IsUnconstrained() bool {
+	return (d.Version != "" && d.Branch != "") || (d.Version == "" && d.Branch == "")
+}
+
+// ConstraintsEq checks if the constraints on two Dependency are exactly equal.
+func (d Dependency) ConstraintsEq(d2 Dependency) bool {
+	// Having both branch and version set is always an error, so if either have
+	// it, then return false
+	if (d.Version != "" && d.Branch != "") || (d2.Version != "" && d2.Branch != "") {
+		return false
+	}
+	// Neither being set, though, is OK
+	if (d.Version == "" && d.Branch == "") || (d2.Version == "" && d2.Branch == "") {
+		return true
+	}
+
+	// Now, xors
+	if d.Version != "" && d.Version == d2.Version {
+		return true
+	}
+	if d.Branch == d2.Branch {
+		return true
+	}
+	return false
+}
+
 // UnmarshalYAML is a hook for gopkg.in/yaml.v2 in the unmarshaling process
 func (d *Dependency) UnmarshalYAML(unmarshal func(interface{}) error) error {
 	newDep := dep{}
@@ -443,35 +487,14 @@
 		return err
 	}
 
+	if newDep.Version != "" && newDep.Branch != "" {
+		return fmt.Errorf("Cannot set both a both a branch and a version constraint for %q", d.Name)
+	}
+
 	d.Name = newDep.Name
 	d.Repository = newDep.Repository
-
-	if newDep.Reference != "" {
-		r := newDep.Reference
-		// TODO(sdboyer) this covers git & hg; bzr and svn (??) need love
-		if len(r) == 40 {
-			if _, err := hex.DecodeString(r); err == nil {
-				d.Constraint = gps.Revision(r)
-			}
-		} else {
-			d.Constraint, err = gps.NewSemverConstraint(r)
-			if err != nil {
-				d.Constraint = gps.NewVersion(r)
-			}
-		}
-
-		if err != nil {
-			return fmt.Errorf("Error on creating constraint for %q from %q: %s", d.Name, r, err)
-		}
-	} else if newDep.Branch != "" {
-		d.Constraint = gps.NewBranch(newDep.Branch)
-
-		if err != nil {
-			return fmt.Errorf("Error on creating constraint for %q from %q: %s", d.Name, newDep.Branch, err)
-		}
-	} else {
-		d.Constraint = gps.Any()
-	}
+	d.Version = newDep.Version
+	d.Branch = newDep.Branch
 
 	return nil
 }
@@ -526,28 +549,10 @@
 	newDep := &dep{
 		Name:       d.Name,
 		Repository: d.Repository,
+		Version:    d.Version,
+		Branch:     d.Branch,
 	}
 
-	// Pull out the correct type of constraint
-	if v, ok := d.Constraint.(gps.Version); ok {
-		switch v.Type() {
-		case "any":
-			// Do nothing; nothing here is taken as 'any'
-		case "branch":
-			newDep.Branch = v.String()
-		case "revision", "semver", "version":
-			newDep.Reference = v.String()
-		}
-	} else if gps.IsAny(d.Constraint) {
-		// We do nothing here, as the way any gets represented is with no
-		// constraint information at all
-	} else if d.Constraint != nil {
-		// The only other thing this could really be is a semver range. This
-		// will dump that appropriately.
-		newDep.Reference = d.Constraint.String()
-	}
-	// Just ignore any other case
-
 	return newDep, nil
 }
 
diff --git a/cfg/config_test.go b/cfg/config_test.go
index 09be399..72400d2 100644
--- a/cfg/config_test.go
+++ b/cfg/config_test.go
@@ -1,7 +1,6 @@
 package cfg
 
 import (
-	"reflect"
 	"testing"
 
 	"github.com/sdboyer/gps"
@@ -85,15 +84,15 @@
 
 		switch i.Name {
 		case "github.com/kylelemons/go-gypsy":
-			ref := gps.NewVersion("v1.0.0")
-			if i.Constraint != ref {
-				t.Errorf("(%s) Expected %q for constraint, got %q", i.Name, ref, i.Constraint)
+			ref := "v1.0.0"
+			if i.Version != ref {
+				t.Errorf("(%s) Expected %q for constraint, got %q", i.Name, ref, i.Version)
 			}
 
 		case "github.com/Masterminds/convert":
-			ref := gps.Revision("a9949121a2e2192ca92fa6dddfeaaa4a4412d955")
-			if i.Constraint != ref {
-				t.Errorf("(%s) Expected %q for constraint, got %q", i.Name, ref, i.Constraint)
+			ref := "a9949121a2e2192ca92fa6dddfeaaa4a4412d955"
+			if i.Version != ref {
+				t.Errorf("(%s) Expected %q for constraint, got %q", i.Name, ref, i.Version)
 			}
 
 			repo := "git@github.com:Masterminds/convert.git"
@@ -102,9 +101,9 @@
 			}
 
 		case "github.com/Masterminds/structable":
-			ref := gps.NewBranch("master")
-			if i.Constraint != ref {
-				t.Errorf("(%s) Expected %q for constraint, got %q", i.Name, ref, i.Constraint)
+			ref := "master"
+			if i.Branch != ref {
+				t.Errorf("(%s) Expected %q for constraint, got %q", i.Name, ref, i.Branch)
 			}
 
 		case "github.com/Masterminds/cookoo":
@@ -114,9 +113,9 @@
 			}
 
 		case "github.com/sdboyer/gps":
-			sv, _ := gps.NewSemverConstraint("^v1.0.0")
-			if !reflect.DeepEqual(sv, i.Constraint) {
-				t.Errorf("(%s) Expected %q for constraint, got %q", i.Name, sv, i.Constraint)
+			sv := "^v1.0.0"
+			if i.Version != sv {
+				t.Errorf("(%s) Expected %q for constraint, got %q", i.Name, sv, i.Version)
 			}
 		}
 	}
@@ -145,9 +144,9 @@
 			t.Errorf("Expected test dependency to be %s, got %s", n, ti.Name)
 		}
 
-		sv, _ := gps.NewSemverConstraint("~v1.0.0")
-		if !reflect.DeepEqual(sv, ti.Constraint) {
-			t.Errorf("(test dep: %s) Expected %q for constraint, got %q", ti.Name, sv, ti.Constraint)
+		sv := "~v1.0.0"
+		if ti.Version != sv {
+			t.Errorf("(%s) Expected %q for constraint, got %q", ti.Name, sv, ti.Version)
 		}
 	}
 
@@ -263,9 +262,9 @@
 	for _, i := range c.Imports {
 		if i.Name == "github.com/Masterminds/convert" {
 			found = true
-			ref := gps.Revision("a9949121a2e2192ca92fa6dddfeaaa4a4412d955")
-			if i.Constraint != ref {
-				t.Errorf("(%s) Expected %q for constraint, got %q", i.Name, ref, i.Constraint)
+			ref := "a9949121a2e2192ca92fa6dddfeaaa4a4412d955"
+			if i.Version != ref {
+				t.Errorf("(%s) Expected %q for constraint, got %q", i.Name, ref, i.Version)
 			}
 
 			repo := "git@github.com:Masterminds/convert.git"
@@ -276,9 +275,9 @@
 
 		if i.Name == "github.com/Masterminds/structable" {
 			found2 = true
-			ref := gps.NewVersion("v1.0.0")
-			if i.Constraint != ref {
-				t.Errorf("(%s) Expected %q for constraint, got %q", i.Name, ref, i.Constraint)
+			ref := "v1.0.0"
+			if i.Version != ref {
+				t.Errorf("(%s) Expected %q for constraint, got %q", i.Name, ref, i.Version)
 			}
 		}
 	}
diff --git a/cfg/legacy.go b/cfg/legacy.go
index 8f99d67..b171587 100644
--- a/cfg/legacy.go
+++ b/cfg/legacy.go
@@ -185,12 +185,12 @@
 		d2 := &Dependency{
 			Name:       d.Name,
 			Repository: d.Repository,
+			Version:    d.Reference,
 		}
 
-		d2.Constraint = DeduceConstraint(d.Reference)
-		// TODO(sdboyer) if the above result is a plain version, the old
-		// semantics are ambiguous wrt if the user wants a branch or tag. Check
-		// the version list (via source manager) to convert most sanely?
+		// TODO(sdboyer) d.Reference doesn't disambiguate between branches and
+		// tags. Check the version list (via source manager) to convert most
+		// sanely?
 		ds2 = append(ds2, d2)
 	}
 
diff --git a/cfg/lock.go b/cfg/lock.go
index 284cd51..fa5b018 100644
--- a/cfg/lock.go
+++ b/cfg/lock.go
@@ -314,8 +314,8 @@
 		for ii := 0; ii < len(ds); ii++ {
 			if ds[ii].Name == tds[i].Name {
 				found = true
-				if ds[ii].Constraint.String() != tds[i].Constraint.String() {
-					return &Lockfile{}, fmt.Errorf("Generating lock produced conflicting versions of %s. import (%s), testImport (%s)", tds[i].Name, ds[ii].Constraint, tds[i].Constraint)
+				if ds[ii].ConstraintsEq(*tds[i]) {
+					return &Lockfile{}, fmt.Errorf("Generating lock produced conflicting versions of %s. import (%s), testImport (%s)", tds[i].Name, ds[ii].GetConstraint(), tds[i].GetConstraint())
 				}
 				break
 			}
diff --git a/gb/gb.go b/gb/gb.go
index 2f5612b..aa30a81 100644
--- a/gb/gb.go
+++ b/gb/gb.go
@@ -9,7 +9,6 @@
 	"github.com/Masterminds/glide/msg"
 	gpath "github.com/Masterminds/glide/path"
 	"github.com/Masterminds/glide/util"
-	"github.com/sdboyer/gps"
 )
 
 // Has returns true if this dir has a GB-flavored manifest file.
@@ -52,7 +51,7 @@
 			seen[pkg] = true
 			dep := &cfg.Dependency{
 				Name:       pkg,
-				Constraint: cfg.DeduceConstraint(d.Revision),
+				Version:    d.Revision,
 				Repository: d.Repository,
 			}
 			buf = append(buf, dep)
@@ -89,7 +88,6 @@
 			seen[pkg] = true
 			dep := &cfg.Dependency{
 				Name:       pkg,
-				Constraint: gps.Any(),
 				Repository: d.Repository,
 			}
 			m = append(m, dep)
diff --git a/godep/godep.go b/godep/godep.go
index 82bc5e1..1a358aa 100644
--- a/godep/godep.go
+++ b/godep/godep.go
@@ -12,7 +12,6 @@
 	"github.com/Masterminds/glide/msg"
 	gpath "github.com/Masterminds/glide/path"
 	"github.com/Masterminds/glide/util"
-	"github.com/sdboyer/gps"
 )
 
 // This file contains commands for working with Godep.
@@ -78,7 +77,7 @@
 		pkg, _ := util.NormalizeName(d.ImportPath)
 		if !seen[pkg] {
 			seen[pkg] = true
-			dep := &cfg.Dependency{Name: pkg, Constraint: gps.Revision(d.Rev)}
+			dep := &cfg.Dependency{Name: pkg, Version: d.Rev}
 			buf = append(buf, dep)
 		}
 	}
@@ -121,7 +120,7 @@
 			// This approach does make for an uncomfortably wide possibility
 			// space where deps aren't getting what they expect, but that's
 			// better than just having the solver give up completely.
-			m = append(m, &cfg.Dependency{Name: pkg, Constraint: gps.Any()})
+			m = append(m, &cfg.Dependency{Name: pkg})
 			l.Imports = append(l.Imports, &cfg.Lock{Name: pkg, Revision: d.Rev})
 
 			// TODO this fails to differentiate between dev and non-dev imports;
diff --git a/gom/gom.go b/gom/gom.go
index 46f27d2..08f0c2c 100644
--- a/gom/gom.go
+++ b/gom/gom.go
@@ -64,13 +64,13 @@
 
 		// Check for a specific revision
 		if val, ok := gom.options["commit"]; ok {
-			dep.Constraint = gps.Revision(val.(string))
+			dep.Version = val.(string)
 		}
 		if val, ok := gom.options["tag"]; ok {
-			dep.Constraint = gps.NewVersion(val.(string))
+			dep.Version = val.(string)
 		}
 		if val, ok := gom.options["branch"]; ok {
-			dep.Constraint = gps.NewBranch(val.(string))
+			dep.Branch = val.(string)
 		}
 
 		buf = append(buf, dep)
diff --git a/gpm/gpm.go b/gpm/gpm.go
index 0f6c8ef..55bf6d9 100644
--- a/gpm/gpm.go
+++ b/gpm/gpm.go
@@ -13,7 +13,6 @@
 	"github.com/Masterminds/glide/cfg"
 	"github.com/Masterminds/glide/msg"
 	gpath "github.com/Masterminds/glide/path"
-	"github.com/sdboyer/gps"
 )
 
 // Has indicates whether a Godeps file exists.
@@ -47,7 +46,7 @@
 		if ok {
 			dep := &cfg.Dependency{Name: parts[0]}
 			if len(parts) > 1 {
-				dep.Constraint = cfg.DeduceConstraint(parts[1])
+				dep.Version = parts[1]
 			}
 			buf = append(buf, dep)
 		}
@@ -85,7 +84,7 @@
 			if len(parts) > 1 {
 				l.Imports = append(l.Imports, &cfg.Lock{Name: parts[0], Version: parts[1]})
 			}
-			m = append(m, &cfg.Dependency{Name: parts[0], Constraint: gps.Any()})
+			m = append(m, &cfg.Dependency{Name: parts[0]})
 		}
 	}
 	if err := scanner.Err(); err != nil {
diff --git a/repo/installer.go b/repo/installer.go
index de751bf..ae0d561 100644
--- a/repo/installer.go
+++ b/repo/installer.go
@@ -318,12 +318,15 @@
 			continue
 		}
 
-		if ver == dep.Constraint.String() {
-			msg.Info("--> Found desired version %s %s!", dep.Name, dep.Constraint)
+		// TODO(sdboyer) it wants a rev only here, but the lock conversion to a
+		// dep will prefer to put in the version, not the rev. ...fortunately,
+		// this whole func should be removed soon(?)
+		if ver == dep.Version {
+			msg.Info("--> Found desired version %s %s!", dep.Name, dep.GetConstraint())
 			continue
 		}
 
-		msg.Debug("--> Queue %s for update (%s != %s).", dep.Name, ver, dep.Constraint)
+		msg.Debug("--> Queue %s for update (%s != %s).", dep.Name, ver, dep.GetConstraint())
 		newDeps = append(newDeps, dep)
 	}
 	if len(newDeps) > 0 {
@@ -609,7 +612,7 @@
 
 				// The fist one wins. Would something smater than this be better?
 				exists, _ := d.Use.Get(dep.Name)
-				if exists == nil && (dep.Constraint != nil || dep.Repository != "") {
+				if exists == nil && (dep.IsUnconstrained() || dep.Repository != "") {
 					d.Use.Add(dep.Name, dep, root)
 				}
 			}
@@ -644,8 +647,8 @@
 			// There are import chains (because the import tree is resolved
 			// before the test tree) that can cause this.
 			tempD := d.Config.DevImports.Get(root)
-			if tempD.Constraint.String() != v.Constraint.String() {
-				msg.Warn("Using import %s (version %s) for test instead of testImport (version %s).", v.Name, v.Constraint, tempD.Constraint)
+			if !tempD.ConstraintsEq(*v) {
+				msg.Warn("Using import %s (version %s) for test instead of testImport (version %s).", v.Name, v.GetConstraint(), tempD.GetConstraint())
 			}
 			// TODO(mattfarina): Note repo difference in a warning.
 		}
@@ -653,12 +656,13 @@
 
 	dep, req := d.Use.Get(root)
 	if dep != nil && v != nil {
-		if v.Constraint == nil && dep.Constraint != nil {
-			v.Constraint = dep.Constraint
+		if v.IsUnconstrained() && !dep.IsUnconstrained() {
+			v.Version = dep.Version
+			v.Branch = dep.Branch
 			// Clear the pin, if set, so the new version can be used.
 			//v.Pin = ""
 			dep = v
-		} else if v.Constraint != nil && dep.Constraint != nil && v.Constraint.String() != dep.Constraint.String() {
+		} else if !dep.ConstraintsEq(*v) && !dep.IsUnconstrained() && !v.IsUnconstrained() { // constraints are not eq and non-empty
 			dest := filepath.Join(d.Destination, filepath.FromSlash(v.Name))
 			dep = determineDependency(v, dep, dest, req)
 		} else {
@@ -690,7 +694,7 @@
 
 	err := VcsVersion(dep, d.Destination)
 	if err != nil {
-		msg.Warn("Unable to set version on %s to %s. Err: %s", root, dep.Constraint, err)
+		msg.Warn("Unable to set version on %s to %s. Err: %s", root, dep.GetConstraint(), err)
 		e = err
 	}
 
@@ -701,110 +705,114 @@
 	repo, err := v.GetRepo(dest)
 	if err != nil {
 		singleWarn("Unable to access repo for %s\n", v.Name)
-		singleInfo("Keeping %s %s", v.Name, v.Constraint)
+		singleInfo("Keeping %s %s", v.Name, v.GetConstraint())
 		return v
 	}
 
-	vIsRef := repo.IsReference(v.Constraint.String())
-	depIsRef := repo.IsReference(dep.Constraint.String())
+	dc, vc := v.GetConstraint(), dep.GetConstraint()
+	vIsRef := repo.IsReference(vc.String())
+	depIsRef := repo.IsReference(dc.String())
 
 	// Both are references and they are different ones.
 	if vIsRef && depIsRef {
-		singleWarn("Conflict: %s rev is currently %s, but %s wants %s\n", v.Name, v.Constraint, req, dep.Constraint)
+		singleWarn("Conflict: %s rev is currently %s, but %s wants %s\n", v.Name, vc, req, dep.GetConstraint())
 
 		displayCommitInfo(repo, v)
 		displayCommitInfo(repo, dep)
 
-		singleInfo("Keeping %s %s", v.Name, v.Constraint)
+		singleInfo("Keeping %s %s", v.Name, vc)
 		return v
 	} else if vIsRef {
 		// The current one is a reference and the suggestion is a SemVer constraint.
-		con, err := semver.NewConstraint(dep.Constraint.String())
+		con, err := semver.NewConstraint(dep.Version)
 		if err != nil {
-			singleWarn("Version issue for %s: '%s' is neither a reference or semantic version constraint\n", dep.Name, dep.Constraint)
-			singleInfo("Keeping %s %s", v.Name, v.Constraint)
+			singleWarn("Version issue for %s: '%s' is neither a reference or semantic version constraint\n", dep.Name, dep.GetConstraint())
+			singleInfo("Keeping %s %s", v.Name, vc)
 			return v
 		}
 
-		ver, err := semver.NewVersion(v.Constraint.String())
+		ver, err := semver.NewVersion(v.Version)
 		if err != nil {
 			// The existing version is not a semantic version.
-			singleWarn("Conflict: %s version is %s, but also asked for %s\n", v.Name, v.Constraint, dep.Constraint)
+			singleWarn("Conflict: %s version is %s, but also asked for %s\n", v.Name, vc, dc)
 			displayCommitInfo(repo, v)
-			singleInfo("Keeping %s %s", v.Name, v.Constraint)
+			singleInfo("Keeping %s %s", v.Name, vc)
 			return v
 		}
 
 		if con.Matches(ver) == nil {
-			singleInfo("Keeping %s %s because it fits constraint '%s'", v.Name, v.Constraint, dep.Constraint)
+			singleInfo("Keeping %s %s because it fits constraint '%s'", v.Name, vc, dc)
 			return v
 		}
-		singleWarn("Conflict: %s version is %s but does not meet constraint '%s'\n", v.Name, v.Constraint, dep.Constraint)
-		singleInfo("Keeping %s %s", v.Name, v.Constraint)
+		singleWarn("Conflict: %s version is %s but does not meet constraint '%s'\n", v.Name, vc, dc)
+		singleInfo("Keeping %s %s", v.Name, vc)
 		return v
 	} else if depIsRef {
 
-		con, err := semver.NewConstraint(v.Constraint.String())
+		con, err := semver.NewConstraint(v.Version)
 		if err != nil {
-			singleWarn("Version issue for %s: '%s' is neither a reference or semantic version constraint\n", v.Name, v.Constraint)
-			singleInfo("Keeping %s %s", v.Name, v.Constraint)
+			singleWarn("Version issue for %s: '%s' is neither a reference or semantic version constraint\n", v.Name, vc)
+			singleInfo("Keeping %s %s", v.Name, vc)
 			return v
 		}
 
-		ver, err := semver.NewVersion(dep.Constraint.String())
+		ver, err := semver.NewVersion(dep.Version)
 		if err != nil {
-			singleWarn("Conflict: %s version is %s, but also asked for %s\n", v.Name, v.Constraint, dep.Constraint)
+			singleWarn("Conflict: %s version is %s, but also asked for %s\n", v.Name, vc, dc)
 			displayCommitInfo(repo, dep)
-			singleInfo("Keeping %s %s", v.Name, v.Constraint)
+			singleInfo("Keeping %s %s", v.Name, vc)
 			return v
 		}
 
 		if con.Matches(ver) == nil {
-			v.Constraint = dep.Constraint
-			singleInfo("Using %s %s because it fits constraint '%s'", v.Name, v.Constraint, v.Constraint)
+			singleInfo("Using %s %s because it fits constraint '%s'", v.Name, dc, vc)
+			v.Version = dep.Version
+			v.Branch = dep.Branch
+			vc = dc
 			return v
 		}
-		singleWarn("Conflict: %s semantic version constraint is %s but '%s' does not meet the constraint\n", v.Name, v.Constraint, v.Constraint)
-		singleInfo("Keeping %s %s", v.Name, v.Constraint)
+		singleWarn("Conflict: %s semantic version constraint is %s but '%s' does not meet the constraint\n", v.Name, dc, vc)
+		singleInfo("Keeping %s %s", v.Name, dc)
 		return v
 	}
 	// Neither is a vcs reference and both could be semantic version
 	// constraints that are different.
 
-	_, err = semver.NewConstraint(dep.Constraint.String())
+	_, err = semver.NewConstraint(dc.String())
 	if err != nil {
 		// dd.Constraint is not a reference or a valid constraint.
-		singleWarn("Version %s %s is not a reference or valid semantic version constraint\n", dep.Name, dep.Constraint)
-		singleInfo("Keeping %s %s", v.Name, v.Constraint)
+		singleWarn("Version %s %s is not a reference or valid semantic version constraint\n", dep.Name, dc)
+		singleInfo("Keeping %s %s", v.Name, vc)
 		return v
 	}
 
-	_, err = semver.NewConstraint(v.Constraint.String())
+	_, err = semver.NewConstraint(vc.String())
 	if err != nil {
 		// existing.Constraint is not a reference or a valid constraint.
 		// We really should never end up here.
-		singleWarn("Version %s %s is not a reference or valid semantic version constraint\n", v.Name, v.Constraint)
+		singleWarn("Version %s %s is not a reference or valid semantic version constraint\n", v.Name, vc)
 
-		v.Constraint = dep.Constraint
+		v.Version = dep.Version
+		v.Branch = dep.Branch
 		//v.Pin = ""
-		singleInfo("Using %s %s because it is a valid version", v.Name, v.Constraint)
+		singleInfo("Using %s %s because it is a valid version", v.Name, dc)
 		return v
 	}
 
 	// Both versions are constraints. Try to merge them.
 	// If either comparison has an || skip merging. That's complicated.
-	ddor := strings.Index(dep.Constraint.String(), "||")
-	eor := strings.Index(v.Constraint.String(), "||")
+	ddor := strings.Index(dep.Version, "||")
+	eor := strings.Index(v.Version, "||")
 	if ddor == -1 && eor == -1 {
 		// Add the comparisons together.
 		// TODO(sdboyer) this all just reeeeeally needs to go
-		v.Constraint = v.Constraint.Intersect(dep.Constraint)
+		v.Version = v.GetConstraint().Intersect(dep.GetConstraint()).String()
 		//v.Pin = ""
-		singleInfo("Combining %s semantic version constraints %s and %s", v.Name, v.Constraint, dep.Constraint)
+		singleInfo("Combining %s semantic version constraints %s and %s", v.Name, vc, dc)
 		return v
 	}
-	singleWarn("Conflict: %s version is %s, but also asked for %s\n", v.Name, v.Constraint, dep.Constraint)
-	singleInfo("Keeping %s %s", v.Name, v.Constraint)
+	singleWarn("Conflict: %s version is %s, but also asked for %s\n", v.Name, vc, dc)
+	singleInfo("Keeping %s %s", v.Name, vc)
 	return v
 }
 
@@ -862,7 +870,7 @@
 	displayCommitInfoPrefix + "- subject (first line): %s\n"
 
 func displayCommitInfo(repo vcs.Repo, dep *cfg.Dependency) {
-	ref := dep.Constraint.String()
+	ref := dep.GetConstraint().String()
 	c, err := repo.CommitInfo(ref)
 
 	if err == nil {
diff --git a/repo/set_reference.go b/repo/set_reference.go
index e4367e3..ff3d4c3 100644
--- a/repo/set_reference.go
+++ b/repo/set_reference.go
@@ -47,7 +47,7 @@
 					}
 					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.Constraint, err)
+						msg.Err("Failed to set version on %s to %s: %s\n", dep.Name, dep.GetConstraint(), err)
 
 						// Capture the error while making sure the concurrent
 						// operations don't step on each other.
diff --git a/repo/vcs.go b/repo/vcs.go
index 2f3b0cf..bbcebb7 100644
--- a/repo/vcs.go
+++ b/repo/vcs.go
@@ -18,7 +18,6 @@
 	gpath "github.com/Masterminds/glide/path"
 	"github.com/Masterminds/semver"
 	v "github.com/Masterminds/vcs"
-	"github.com/sdboyer/gps"
 )
 
 // VcsUpdate updates to a particular checkout based on the VCS setting.
@@ -133,22 +132,17 @@
 			// and that version is already checked out we can skip updating
 			// which is faster than going out to the Internet to perform
 			// an update.
-			if dep.Constraint != nil {
+			if !dep.IsUnconstrained() {
 				version, err := repo.Version()
 				if err != nil {
 					return err
 				}
 
-				var ib bool
-				if cv, ok := dep.Constraint.(gps.Version); ok {
-					ib = cv.Type() == "branch"
-				}
-
 				// If the current version equals the ref and it's not a
 				// branch it's a tag or commit id so we can skip
 				// performing an update.
-				if version == dep.Constraint.String() && !ib {
-					msg.Debug("%s is already set to version %s. Skipping update.", dep.Name, dep.Constraint)
+				if version == dep.Version {
+					msg.Debug("%s is already set to version %s. Skipping update.", dep.Name, dep.GetConstraint())
 					return nil
 				}
 			}
@@ -176,7 +170,7 @@
 	cwd := filepath.Join(vend, dep.Name)
 
 	// If there is no reference configured there is nothing to set.
-	if dep.Constraint == nil {
+	if dep.IsUnconstrained() {
 		// Before exiting update the pinned version
 		//_, err := dep.GetRepo(cwd)
 		//if err != nil {
@@ -204,14 +198,13 @@
 			return err
 		}
 
-		ver := dep.Constraint.String()
+		ver := dep.GetConstraint().String()
 		// References in Git can begin with a ^ which is similar to semver.
 		// If there is a ^ prefix we assume it's a semver constraint rather than
 		// part of the git/VCS commit id.
 		if repo.IsReference(ver) && !strings.HasPrefix(ver, "^") {
 			msg.Info("--> Setting version for %s to %s.\n", dep.Name, ver)
 		} else {
-
 			// Create the constraint first to make sure it's valid before
 			// working on the repo.
 			constraint, err := semver.NewConstraint(ver)
@@ -308,7 +301,7 @@
 
 				// If there is no reference set on the dep we try to checkout
 				// the default branch.
-				if dep.Constraint == nil {
+				if dep.IsUnconstrained() {
 					db := defaultBranch(repo, home)
 					if db != "" {
 						err = repo.UpdateVersion(db)