You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by cc...@apache.org on 2020/01/24 00:12:15 UTC

[mynewt-newt] 02/02: Simplify repo dependencies

This is an automated email from the ASF dual-hosted git repository.

ccollins pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-newt.git

commit 7b42efd83e6a2373c526897d7d2c61c82a33274c
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Thu Jan 16 12:59:02 2020 -0800

    Simplify repo dependencies
    
    Remove two features from Mynewt's repo dependency support:
    
    1. The ability to depend on a range of repo versions, and
    2. Mandatory version.yml files.
    
    This change is described in more detail here:
    https://github.com/apache/mynewt-newt/pull/365
---
 newt/deprepo/deprepo.go       | 345 +++++++++++++++++++-----------------------
 newt/deprepo/graph.go         | 152 ++++++-------------
 newt/deprepo/matrix.go        | 172 ---------------------
 newt/downloader/downloader.go |  28 +++-
 newt/install/install.go       | 287 +++++------------------------------
 newt/newtutil/repo_version.go |  31 ++--
 newt/project/project.go       |   1 -
 newt/repo/repo.go             |   1 -
 newt/repo/version.go          | 147 ++----------------
 9 files changed, 299 insertions(+), 865 deletions(-)

diff --git a/newt/deprepo/deprepo.go b/newt/deprepo/deprepo.go
index 56c65ab..159bac0 100644
--- a/newt/deprepo/deprepo.go
+++ b/newt/deprepo/deprepo.go
@@ -25,6 +25,8 @@ import (
 	"sort"
 	"strings"
 
+	log "github.com/sirupsen/logrus"
+
 	"mynewt.apache.org/newt/newt/newtutil"
 	"mynewt.apache.org/newt/newt/repo"
 	"mynewt.apache.org/newt/util"
@@ -39,10 +41,29 @@ type VersionMap map[string]newtutil.RepoVersion
 // [repo-name] => requirements-for-key-repo
 type RequirementMap map[string]newtutil.RepoVersion
 
-// Indicates an inability to find an acceptable version of a particular repo.
+type ConflictEntry struct {
+	Dependent   RVPair
+	DependeeVer newtutil.RepoVersion
+}
+
+// Indicates dependencies on different versions of the same repo.
 type Conflict struct {
-	RepoName string
-	Filters  []Filter
+	DependeeName string
+	Entries      []ConflictEntry
+}
+
+func (c *Conflict) SortEntries() {
+	sort.Slice(c.Entries, func(i int, j int) bool {
+		ci := c.Entries[i]
+		cj := c.Entries[j]
+
+		x := CompareRVPairs(ci.Dependent, cj.Dependent)
+		if x != 0 {
+			return x < 0
+		}
+
+		return newtutil.CompareRepoVersions(ci.DependeeVer, cj.DependeeVer) < 0
+	})
 }
 
 // Returns a sorted slice of all constituent repo names.
@@ -86,27 +107,6 @@ func (vm VersionMap) String() string {
 	return s
 }
 
-// Constructs a version matrix from the specified repos.  Each row in the
-// resulting matrix corresponds to a repo in the supplied slice.  Each row node
-// represents a single version of the repo.
-func BuildMatrix(repos []*repo.Repo, vm VersionMap) (Matrix, error) {
-	m := Matrix{}
-
-	for _, r := range repos {
-		if !r.IsLocal() {
-			vers, err := r.NormalizedVersions()
-			if err != nil {
-				return m, err
-			}
-			if err := m.AddRow(r.Name(), vers); err != nil {
-				return m, err
-			}
-		}
-	}
-
-	return m, nil
-}
-
 // Builds a repo dependency graph from the repo requirements expressed in the
 // `project.yml` file.
 func BuildDepGraph(repos RepoMap, rootReqs RequirementMap) (DepGraph, error) {
@@ -120,7 +120,7 @@ func BuildDepGraph(repos RepoMap, rootReqs RequirementMap) (DepGraph, error) {
 			return nil, err
 		}
 
-		if err := dg.AddRootDep(repoName, []newtutil.RepoVersion{normalizedReq}); err != nil {
+		if err := dg.AddRootDep(repoName, normalizedReq); err != nil {
 			return nil, err
 		}
 	}
@@ -151,86 +151,6 @@ func BuildDepGraph(repos RepoMap, rootReqs RequirementMap) (DepGraph, error) {
 	return dg, nil
 }
 
-// Prunes unusable repo versions from the specified matrix.
-func PruneMatrix(m *Matrix, repos RepoMap, rootReqs RequirementMap) error {
-	pruned := map[*repo.RepoDependency]struct{}{}
-
-	// Removes versions of the depended-on package that fail to satisfy the
-	// dependent's requirements.  Each of the depended-on package's
-	// dependencies are then recursively visited.
-	var recurse func(dependentName string, dep *repo.RepoDependency) error
-	recurse = func(dependentName string, dep *repo.RepoDependency) error {
-		// Don't prune the same dependency twice; prevents infinite
-		// recursion.
-		if _, ok := pruned[dep]; ok {
-			return nil
-		}
-		pruned[dep] = struct{}{}
-
-		// Remove versions of this depended-on package that don't satisfy the
-		// dependency's version requirements.
-		r := repos[dep.Name]
-		normalizedReq, err := r.NormalizeVerReq(dep.VerReqs)
-		if err != nil {
-			return err
-		}
-		filter := Filter{
-			Name: dependentName,
-			Req:  normalizedReq,
-		}
-		m.ApplyFilter(dep.Name, filter)
-
-		// If there is only one version of the depended-on package left in the
-		// matrix, we can recursively call this function on all its
-		// dependencies.
-		//
-		// We don't do it, but it is possible to prune when there is more than
-		// one version remaining.  To accomplish this, we would collect the
-		// requirements from all versions, find their union, and remove
-		// depended-on packages that satisfy none of the requirements in the
-		// union.  The actual implementation (only prune when one version
-		// remains) is a simplified implementation of this general procedure.
-		// In exchange for simplicity, some unusable versions remain in the
-		// matrix that could have been pruned.  These unsuable versions must be
-		// evaluated unnecessarily when the matrix is being searched for an
-		// acceptable version set.
-		row := m.FindRow(r.Name())
-		if row != nil && len(row.Vers) == 1 {
-			ver := row.Vers[0]
-			commit, err := r.CommitFromVer(ver)
-			if err != nil {
-				return err
-			}
-			depRepo := repos[dep.Name]
-			for _, ddep := range depRepo.CommitDepMap()[commit] {
-				name := fmt.Sprintf("%s,%s", depRepo.Name(), ver.String())
-				if err := recurse(name, ddep); err != nil {
-					return err
-				}
-			}
-		}
-
-		return nil
-	}
-
-	// Prune versions that are guaranteed to be unusable.  Any repo version
-	// which doesn't satisfy a requirement in `project.yml` is a
-	// known-bad-version and can be removed.  These repos' dependencies can
-	// then be pruned in turn.
-	for repoName, req := range rootReqs {
-		dep := &repo.RepoDependency{
-			Name:    repoName,
-			VerReqs: req,
-		}
-
-		if err := recurse("project.yml", dep); err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
 // PruneDepGraph removes all entries from a depgraph that aren't in the given
 // repo slice.  This is necessary when the user wants to upgrade only a subset
 // of repos in the project.
@@ -264,12 +184,16 @@ func ConflictError(conflicts []Conflict) error {
 		}
 
 		s += fmt.Sprintf("    Installation of repo \"%s\" is blocked:",
-			c.RepoName)
+			c.DependeeName)
 
 		lines := []string{}
-		for _, f := range c.Filters {
-			lines = append(lines, fmt.Sprintf("\n    %30s requires %s %s",
-				f.Name, c.RepoName, f.Req.String()))
+		for _, e := range c.Entries {
+			dependee := RVPair{
+				Name: c.DependeeName,
+				Ver:  e.DependeeVer,
+			}
+			lines = append(lines, fmt.Sprintf("\n    %30s requires %s",
+				e.Dependent.String(), dependee.String()))
 		}
 		sort.Strings(lines)
 		s += strings.Join(lines, "")
@@ -278,100 +202,149 @@ func ConflictError(conflicts []Conflict) error {
 	return util.NewNewtError("Repository conflicts:\n" + s)
 }
 
-// Searches a version matrix for a set of acceptable repo versions.  If there
-// isn't an acceptable set of versions, the set with the fewest conflicts is
-// returned.
+// ResolveRepoDeps calculates the set of repo versions a project should be
+// upgraded to.
 //
-// @param m                     Matrix containing all unpruned repo versions.
-// @param dg                    The repo dependency graph.
+// dg: The project's repo dependency graph.  This includes root dependencies
+// 	   (i.e., dependencies expressed in `project.yml`).
 //
-// @return VersionMap           The first perfect set of repo versions, or the
-//                                  closest match if there is no perfect set.
-// @return []string             nil if a perfect set was found, else the names
-//                                  of the repos that lack a suitable version
-//                                  in the returned version map.
-func findClosestMatch(m Matrix, dg DepGraph) (VersionMap, []string) {
-	// Tracks the best match seen so far.
-	type Best struct {
-		vm       VersionMap
-		failures []string
+// Returns a version map on success; a set of conflicts on failure.
+func ResolveRepoDeps(dg DepGraph) (VersionMap, []Conflict) {
+	// Represents an entry in the working set.
+	type WSNode struct {
+		highPrio  bool   // If true, always "wins" without conflict.
+		dependent RVPair // Repo that expresses this dependency.
+		dependee  RVPair // Repo that is depended on.
+	}
+
+	ws := map[string]WSNode{}        // Working set (key=dependent).
+	cm := map[string]*Conflict{}     // Conflict map (key=dependee).
+	visited := map[string]struct{}{} // Tracks which nodes have been visited.
+
+	// Updates (or inserts a new) conflict object into the conflict map (cm).
+	addConflict := func(dependent RVPair, dependee RVPair) {
+		c := cm[dependee.Name]
+		if c == nil {
+			wsn := ws[dependee.Name]
+
+			c = &Conflict{
+				DependeeName: repoNameString(dependee.Name),
+				Entries: []ConflictEntry{
+					ConflictEntry{
+						Dependent: RVPair{
+							Name: repoNameString(wsn.dependent.Name),
+							Ver:  wsn.dependent.Ver,
+						},
+						DependeeVer: wsn.dependee.Ver,
+					},
+				},
+			}
+			cm[dependee.Name] = c
+		}
+
+		c.Entries = append(c.Entries, ConflictEntry{
+			Dependent:   dependent,
+			DependeeVer: dependee.Ver,
+		})
 	}
-	var best Best
-
-	for {
-		vm := m.CurVersions()
-		badRepos := dg.conflictingRepos(vm)
-		if len(badRepos) == 0 {
-			// Found a perfect match.  Return it.
-			return vm, nil
+
+	// Attempts to add a single node to the working set.  In case of a
+	// conflict, the conflict map is updated instead.
+	addWsNode := func(dependent RVPair, dependee RVPair) {
+		old, ok := ws[dependee.Name]
+		if !ok {
+			// This is the first time we've seen this repo.
+			ws[dependee.Name] = WSNode{
+				highPrio:  false,
+				dependent: dependent,
+				dependee:  dependee,
+			}
+			return
 		}
 
-		if best.failures == nil || len(badRepos) < len(best.failures) {
-			best.vm = vm
-			best.failures = badRepos
+		if newtutil.CompareRepoVersions(old.dependee.Ver, dependee.Ver) == 0 {
+			// We have already seen this exact dependency.  Ignore the
+			// duplicate.
+			return
 		}
 
-		// Evaluate the next set of versions on the following iteration.
-		if !m.Increment() {
-			// All version sets evaluated.  Return the best match.
-			return best.vm, best.failures
+		// There is already a dependency for a different version of this repo.
+		// Handle the conflict.
+		if old.highPrio {
+			// Root commit dependencies take priority over all others.
+			log.Debugf("discarding repo dependency in favor "+
+				"of root override: dep=%s->%s root=%s->%s",
+				dependent.String(), dependee.String(),
+				old.dependent.String(), old.dependee.String())
+		} else {
+			addConflict(dependent, dependee)
 		}
 	}
-}
 
-// Finds the first set of repo versions which satisfies the dependency graph.
-// If there is no acceptable set, a slice of conflicts is returned instead.
-//
-// @param m                     Matrix containing all unpruned repo versions.
-// @param dg                    The repo dependency graph.
-// @return VersionMap           The first perfect set of repo versions, or nil
-//                                  if there is no perfect set.
-// @return []Conflict           nil if a perfect set was found, else the set of
-//                                  conflicts preventing a perfect match from
-//                                  being returned.
-func FindAcceptableVersions(m Matrix, dg DepGraph) (VersionMap, []Conflict) {
-	vm, failures := findClosestMatch(m, dg)
-	if len(failures) == 0 {
-		// No failures implies a perfect match was found.  Return it.
-		return vm, nil
+	// Adds one entry to the working set (ws) for each of the given repo's
+	// dependencies.
+	visit := func(rvp RVPair) {
+		nodes := dg[rvp]
+		for _, node := range nodes {
+			addWsNode(rvp, node)
+		}
+
+		visited[rvp.Name] = struct{}{}
 	}
 
-	// A perfect version set doesn't exist.  Generate the set of relevant
-	// conflicts and return it.
-	conflicts := make([]Conflict, len(failures))
+	// Insert all the root dependencies (dependencies in `project.yml`) into
+	// the working set.  A root dependency is "high priority" if it points to a
+	// git commit rather than a version number.
+	rootDeps := dg[RVPair{}]
+	for _, dep := range rootDeps {
+		ws[dep.Name] = WSNode{
+			highPrio:  dep.Ver.Commit != "",
+			dependent: RVPair{Name: rootDependencyName},
+			dependee:  dep,
+		}
+	}
 
-	// Build a reverse dependency graph.  This will make it easy to determine
-	// which version requirements are relevant to the failure.
-	rg := dg.Reverse()
-	for i, f := range failures {
-		conflict := Conflict{
-			RepoName: f,
+	// Repeatedly iterate through the working set, visiting each unvisited
+	// node.  Stop looping when an iteration produces no new nodes.
+	for len(visited) < len(ws) {
+		// Iterate the working set in a consistent order.  The order doesn't
+		// matter for well-defined projects.  For invalid projects, different
+		// iteration orders can result in different conflicts being reported.
+		names := make([]string, 0, len(ws))
+		for name, _ := range ws {
+			names = append(names, name)
 		}
-		for _, node := range rg[f] {
-			// Determine if this filter is responsible for any conflicts.
-			// Record the name of the filter if it applies.
-			var filterName string
-			if node.Name == rootDependencyName {
-				filterName = "project.yml"
-			} else {
-				// If the version of the repo in the closest-match version map
-				// is the same one that imposes this version requirement,
-				// include it in the conflict object.
-				if newtutil.CompareRepoVersions(vm[node.Name], node.Ver) == 0 {
-					filterName = fmt.Sprintf(
-						"%s,%s", node.Name, node.Ver.String())
-				}
-			}
+		sort.Strings(names)
 
-			if filterName != "" {
-				conflict.Filters = append(conflict.Filters, Filter{
-					Name: filterName,
-					Req:  node.VerReq,
-				})
+		for _, name := range names {
+			wsn := ws[name]
+			if _, ok := visited[name]; !ok {
+				visit(wsn.dependee)
 			}
 		}
-		conflicts[i] = conflict
 	}
 
-	return vm, conflicts
+	// It is an error if any conflicts were detected.
+	if len(cm) > 0 {
+		var cs []Conflict
+		for _, c := range cm {
+			c.SortEntries()
+			cs = append(cs, *c)
+		}
+
+		sort.Slice(cs, func(i int, j int) bool {
+			return strings.Compare(cs[i].DependeeName, cs[j].DependeeName) < 0
+		})
+
+		return nil, cs
+	}
+
+	// The working set now contains the target version of each repo in the
+	// project.
+	vm := VersionMap{}
+	for name, wsn := range ws {
+		vm[name] = wsn.dependee.Ver
+	}
+
+	return vm, nil
 }
diff --git a/newt/deprepo/graph.go b/newt/deprepo/graph.go
index 676b9dd..25564f7 100644
--- a/newt/deprepo/graph.go
+++ b/newt/deprepo/graph.go
@@ -34,40 +34,32 @@ import (
 	"mynewt.apache.org/newt/util"
 )
 
-// Describes a repo that depends on other repos.
-type Dependent struct {
-	Name string
-	Ver  newtutil.RepoVersion
-}
-
 const rootDependencyName = ""
 const rootRepoName = "project.yml"
 
 // Represents a top-level repo dependency (i.e., a repo specified in
 // `project.yml`).
-var rootDependent = Dependent{Name: rootDependencyName}
+var rootDependent = RVPair{Name: rootDependencyName}
 
-// A single node in a repo dependency graph.
-type DepGraphNode struct {
-	// Name of depended-on repo.
+// Represents a repo-name,version pair.
+type RVPair struct {
 	Name string
-	// Expresses the versions of the repo that satisfy this dependency.
-	VerReq newtutil.RepoVersion
+	Ver  newtutil.RepoVersion
 }
 
 // A repo dependency graph.
 // Key: A repo with dependencies.
 // Value: The corresponding list of dependencies.
-type DepGraph map[Dependent][]DepGraphNode
+type DepGraph map[RVPair][]RVPair
 
 // A single node in a repo reverse dependency graph.
 type RevdepGraphNode struct {
 	// The name of the dependent repo.
 	Name string
 	// The version of the dependent repo.
-	Ver newtutil.RepoVersion
-	// The dependent's version requirements that apply to the graph key.
-	VerReq newtutil.RepoVersion
+	DependentVer newtutil.RepoVersion
+	// The version of the dependee repo that is required.
+	DependeeVer newtutil.RepoVersion
 }
 
 // A repo reverse dependency graph.
@@ -75,20 +67,38 @@ type RevdepGraphNode struct {
 // Value: The corresponding list of dependencies.
 type RevdepGraph map[string][]RevdepGraphNode
 
-func repoNameVerString(repoName string, ver newtutil.RepoVersion) string {
+func repoNameString(repoName string) string {
 	if repoName == rootDependencyName {
 		return rootRepoName
 	} else {
-		return fmt.Sprintf("%s-%s", repoName, ver.String())
+		return repoName
+	}
+}
+
+func repoNameVerString(repoName string, ver newtutil.RepoVersion) string {
+	if repoName == rootDependencyName || repoName == rootRepoName {
+		return rootRepoName
+	} else {
+		return fmt.Sprintf("%s/%s", repoName, ver.String())
 	}
 }
 
-func (dep *Dependent) String() string {
-	return repoNameVerString(dep.Name, dep.Ver)
+func (rvp *RVPair) String() string {
+	return repoNameVerString(rvp.Name, rvp.Ver)
 }
 
-func (dgn *DepGraphNode) String() string {
-	return fmt.Sprintf("%s,%s", dgn.Name, dgn.VerReq.String())
+func CompareRVPairs(a RVPair, b RVPair) int {
+	x := strings.Compare(a.Name, b.Name)
+	if x != 0 {
+		return x
+	}
+
+	x = newtutil.CompareRepoVersions(a.Ver, b.Ver)
+	if x != 0 {
+		return x
+	}
+
+	return 0
 }
 
 func (dg DepGraph) String() string {
@@ -108,8 +118,8 @@ func (dg DepGraph) String() string {
 }
 
 func (rgn *RevdepGraphNode) String() string {
-	return fmt.Sprintf("%s,%s", repoNameVerString(rgn.Name, rgn.Ver),
-		rgn.VerReq.String())
+	return fmt.Sprintf("%s,%s", repoNameVerString(rgn.Name, rgn.DependentVer),
+		rgn.DependeeVer.String())
 }
 
 func (rg RevdepGraph) String() string {
@@ -137,7 +147,7 @@ func (rg RevdepGraph) String() string {
 func (dg DepGraph) AddRepoVer(repoName string, repoVer newtutil.RepoVersion,
 	reqMap RequirementMap) error {
 
-	dep := Dependent{
+	dep := RVPair{
 		Name: repoName,
 		Ver:  repoVer,
 	}
@@ -149,9 +159,9 @@ func (dg DepGraph) AddRepoVer(repoName string, repoVer newtutil.RepoVersion,
 	}
 
 	for depName, depReq := range reqMap {
-		dg[dep] = append(dg[dep], DepGraphNode{
-			Name:   depName,
-			VerReq: depReq,
+		dg[dep] = append(dg[dep], RVPair{
+			Name: depName,
+			Ver:  depReq,
 		})
 	}
 
@@ -159,9 +169,7 @@ func (dg DepGraph) AddRepoVer(repoName string, repoVer newtutil.RepoVersion,
 }
 
 // Adds a root dependency (i.e., required repo specified in `project.yml`).
-func (dg DepGraph) AddRootDep(repoName string,
-	verReqs []newtutil.RepoVersion) error {
-
+func (dg DepGraph) AddRootDep(repoName string, ver newtutil.RepoVersion) error {
 	rootDeps := dg[rootDependent]
 	for _, d := range rootDeps {
 		if d.Name == repoName {
@@ -171,9 +179,9 @@ func (dg DepGraph) AddRootDep(repoName string,
 		}
 	}
 
-	dg[rootDependent] = append(dg[rootDependent], DepGraphNode{
-		Name:   repoName,
-		VerReq: verReqs[0],
+	dg[rootDependent] = append(dg[rootDependent], RVPair{
+		Name: repoName,
+		Ver:  ver,
 	})
 
 	return nil
@@ -191,12 +199,13 @@ func (dg DepGraph) Reverse() RevdepGraph {
 
 	for dependent, nodes := range dg {
 		for _, node := range nodes {
-			// Nothing depends on project.yml (""), so exclude it from the result.
+			// Nothing depends on project.yml (""), so exclude it from the
+			// result.
 			if node.Name != "" {
 				rg[node.Name] = append(rg[node.Name], RevdepGraphNode{
-					Name:   dependent.Name,
-					Ver:    dependent.Ver,
-					VerReq: node.VerReq,
+					Name:         dependent.Name,
+					DependentVer: dependent.Ver,
+					DependeeVer:  node.Ver,
 				})
 			}
 		}
@@ -204,70 +213,3 @@ func (dg DepGraph) Reverse() RevdepGraph {
 
 	return rg
 }
-
-// Identifies repos which cannot satisfy all their dependents.  For example, if
-// `project.yml` requires X1 and Y2, but Y2 requires X2, then X is a
-// conflicting repo (no overlap in requirement sets).
-//
-// Returns the names of all repos that cannot be included in the build.  For
-// example, if:
-//     * X depends on Z 1.0.0
-//     * Y depends on Z 2.0.0
-// then Z is included in the returned slice because it can't satisfy all its
-// dependents.  X and Y are *not* included (unless they also have depedents
-// that cannot be satisfied).
-func (dg DepGraph) conflictingRepos(vm VersionMap) []string {
-	badRepoMap := map[string]struct{}{}
-
-	// Create a reverse dependency graph.
-	rg := dg.Reverse()
-
-	for dependeeName, nodes := range rg {
-		dependeeVer, ok := vm[dependeeName]
-		if !ok {
-			// This version was pruned from the matrix.  It is unusable.
-			badRepoMap[dependeeName] = struct{}{}
-			continue
-		}
-
-		// Dependee is the repo being depended on.  We want to determine if it
-		// can be included in the project without violating any dependency
-		// requirements.
-		//
-		// For each repo that depends on dependee (i.e., for each dependent):
-		// 1. Determine which of the dependent's requirements apply to the
-		//    dependee (e.g., if we are evaluating v2 of the dependent, then
-		//    v1's requirements do not apply).
-		// 2. Check if the dependee satisfies all applicable requirements.
-		for _, node := range nodes {
-			var nodeApplies bool
-			if node.Name == rootDependencyName {
-				// project.yml requirements are always applicable.
-				nodeApplies = true
-			} else {
-				// Repo dependency requirements only apply if they are
-				// associated with the version of the dependent that we are
-				// evaluating.
-				dependentVer, ok := vm[node.Name]
-				if ok {
-					nodeApplies = newtutil.CompareRepoVersions(
-						dependentVer, node.Ver) == 0
-				}
-			}
-			if nodeApplies {
-				if !dependeeVer.Satisfies(node.VerReq) {
-					badRepoMap[dependeeName] = struct{}{}
-					break
-				}
-			}
-		}
-	}
-
-	badRepoSlice := make([]string, 0, len(badRepoMap))
-	for repoName, _ := range badRepoMap {
-		badRepoSlice = append(badRepoSlice, repoName)
-	}
-	sort.Strings(badRepoSlice)
-
-	return badRepoSlice
-}
diff --git a/newt/deprepo/matrix.go b/newt/deprepo/matrix.go
deleted file mode 100644
index ca7d99a..0000000
--- a/newt/deprepo/matrix.go
+++ /dev/null
@@ -1,172 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-// deprepo: Package for resolving repo dependencies.
-package deprepo
-
-import (
-	"fmt"
-	"strings"
-
-	"mynewt.apache.org/newt/newt/newtutil"
-	"mynewt.apache.org/newt/util"
-)
-
-// Eliminates non-matching version numbers when applied to a matrix row.
-type Filter struct {
-	Name string
-	Req  newtutil.RepoVersion
-}
-
-// Contains all versions of a single repo.  These version numbers are read from
-// the repo's `repository.yml` file.  Only normalized versions are included.
-type MatrixRow struct {
-	// The name of the repo that the row corresponds to.
-	RepoName string
-
-	// All normalized versions of the repo.
-	Vers []newtutil.RepoVersion
-
-	// Indicates the version of this repo currently being evaluated for
-	// conflicts.
-	VerIdx int
-
-	// All filters that have been applied to this row.  This is only used
-	// during reporting.
-	Filters []Filter
-}
-
-// Contains all versions of a set of repos.  Each row correponds to a single
-// repo.  Each element within a row represents a single version of the repo.
-//
-// The Matrix type serves two purposes:
-//
-// 1. Simple lookup: Provides a convenient means of determining whether a
-// specific version of a repo exists.
-//
-// 2. Requirements matching: The client can cycle through all combinations of
-// repo versions via the `Increment()` function.  Each combination can be
-// exported as a version map via the `CurVersions()` function.  By evaluating
-// each version map against a set of requirements, the client can find the set
-// of repo versions to upgrade to.
-type Matrix struct {
-	rows []MatrixRow
-}
-
-func (m *Matrix) String() string {
-	lines := make([]string, len(m.rows))
-
-	for i, row := range m.rows {
-		line := fmt.Sprintf("%s:", row.RepoName)
-		for _, v := range row.Vers {
-			line += fmt.Sprintf(" %s", v.String())
-		}
-
-		lines[i] = line
-	}
-
-	return strings.Join(lines, "\n")
-}
-
-// Adjusts the matrix to point to the next possible set of repo versions.
-//
-// @return bool                 true if the matrix points to a new set;
-//                              false if the matrix wrapped around to the first
-//                                  set.
-func (m *Matrix) Increment() bool {
-	for i := range m.rows {
-		row := &m.rows[i]
-
-		row.VerIdx++
-		if row.VerIdx < len(row.Vers) {
-			return true
-		}
-
-		// No more versions left for this repo; proceed to next.
-		row.VerIdx = 0
-	}
-
-	// All version combinations evaluated.
-	return false
-}
-
-func (m *Matrix) findRowIdx(repoName string) int {
-	for i, row := range m.rows {
-		if row.RepoName == repoName {
-			return i
-		}
-	}
-
-	return -1
-}
-
-func (m *Matrix) FindRow(repoName string) *MatrixRow {
-	idx := m.findRowIdx(repoName)
-	if idx == -1 {
-		return nil
-	}
-	return &m.rows[idx]
-}
-
-func (m *Matrix) AddRow(repoName string,
-	vers []newtutil.RepoVersion) error {
-
-	if m.findRowIdx(repoName) != -1 {
-		return util.FmtNewtError("Duplicate repo \"%s\" in repo matrix",
-			repoName)
-	}
-
-	m.rows = append(m.rows, MatrixRow{
-		RepoName: repoName,
-		Vers:     newtutil.SortedVersionsDesc(vers),
-	})
-
-	return nil
-}
-
-// Removes all non-matching versions of the specified repo from the matrix.
-func (m *Matrix) ApplyFilter(repoName string, filter Filter) {
-	rowIdx := m.findRowIdx(repoName)
-	if rowIdx == -1 {
-		return
-	}
-	row := &m.rows[rowIdx]
-
-	goodVers := []newtutil.RepoVersion{}
-	for _, v := range row.Vers {
-		if v.Satisfies(filter.Req) {
-			goodVers = append(goodVers, v)
-		}
-	}
-
-	row.Vers = goodVers
-	row.Filters = append(row.Filters, filter)
-}
-
-// Constructs a version map from the matrix's current state.
-func (m *Matrix) CurVersions() VersionMap {
-	vm := make(VersionMap, len(m.rows))
-	for _, row := range m.rows {
-		if len(row.Vers) > 0 {
-			vm[row.RepoName] = row.Vers[row.VerIdx]
-		}
-	}
-
-	return vm
-}
diff --git a/newt/downloader/downloader.go b/newt/downloader/downloader.go
index 53c0457..c36ee3c 100644
--- a/newt/downloader/downloader.go
+++ b/newt/downloader/downloader.go
@@ -578,20 +578,40 @@ func (gd *GenericDownloader) CommitsFor(
 
 	commit = fixupCommitString(commit)
 
-	var commits []string
+	cm := map[string]struct{}{}
 
-	// Add all commits that are equivalent to the specified string.
+	// Add all cm that are equivalent to the specified string.
 	for _, c := range gd.commits {
 		if commit == c.hash {
 			// User specified a hash; add the corresponding branch or tag name.
-			commits = append(commits, c.name)
+			cm[c.name] = struct{}{}
 		} else if commit == c.name {
 			// User specified a branch or tag; add the corresponding hash.
-			commits = append(commits, c.hash)
+			cm[c.hash] = struct{}{}
+		}
+	}
+
+	// If the user specified a hash, add the hash itself.
+	ct, err := gd.CommitType(path, commit)
+	if err != nil {
+		return nil, err
+	}
+
+	if ct == COMMIT_TYPE_HASH {
+		hash, err := gd.HashFor(path, commit)
+		if err != nil {
+			return nil, err
 		}
+		cm[hash] = struct{}{}
 	}
 
+	// Sort the list of commit strings.
+	var commits []string
+	for cstring, _ := range cm {
+		commits = append(commits, cstring)
+	}
 	sort.Strings(commits)
+
 	return commits, nil
 }
 
diff --git a/newt/install/install.go b/newt/install/install.go
index 4105f84..9085f28 100644
--- a/newt/install/install.go
+++ b/newt/install/install.go
@@ -36,16 +36,6 @@
 // "version specifiers".  Version specifiers map to "official releases", while
 // git commits typically map to "custom versions".
 //
-// Before newt can do anything with a repo requirement, it needs to extrapolate
-// two pieces of information:
-// 1. The normalized version number.
-// 2. The git commit.
-//
-// Newt needs the normalized version to determine the repo's dependencies, and
-// to ensure the version of the repo is compatible with the version of newt
-// being used.  Newt needs the git commit so that it knows how to checkout the
-// desired repo version.
-//
 // ### VERSION SPECIFIERS
 //
 // A repo's `repository.yml` file maps version specifiers to git commits in its
@@ -59,32 +49,6 @@
 // By performing a series of recursive lookups, newt converts a version
 // specifier to a normalized-version,git-commit pair.
 //
-// ### GIT COMMITS
-//
-// When newt encounters a git commit in the `project.yml` file, it already has
-// one piece of information that it needs: the git commit.  Newt uses the
-// following procedure to extrapolate its corresponding repo version:
-//
-// 1. If the repo at the commit contains a `version.yml` file, read the version
-//    from this file.
-// 2. Else, if the repo's `repository.yml` file maps the commit to a version
-//    number, use that version number.
-// 3. Else, warn the user and assume 0.0.0.
-//
-// The `version.yml` file is expected to be present in every commit in a repo.
-// It has the following form:
-//    repo.version: <normalized-version-number>
-//
-// For example, if commit 10 of repo X contains the following `version.yml`
-// file:
-//    repo.version: 1.10.0
-//
-// and commit 20 of repo X changes `version.yml` to:
-//    repo.version: 2.0.0
-//
-// then newt extrapolates 1.10.0 from commits 10 through 19 (inclusive).
-// Commit 20 and beyond correspond to 2.0.0.
-//
 // ### VERSION STRINGS
 //
 // Newt uses the following procedure when displaying a repo version to the
@@ -93,11 +57,8 @@
 // Official releases are expressed as a normalized version.
 //     e.g., 1.10.0
 //
-// Custom versions are expressed with the following form:
-//     <extrapolated-version>/<git-commit>
-//
-// E.g.,:
-//     0.0.0/0aae710654b48d9a84d54de771cc18427709df7d
+// Custom versions are expressed as a git hash.
+// 	   e.g., 0aae710654b48d9a84d54de771cc18427709df7d
 // ----------------------------------------------------------------------------
 
 package install
@@ -106,7 +67,6 @@ import (
 	"bufio"
 	"fmt"
 	"os"
-	"sort"
 	"strings"
 
 	log "github.com/sirupsen/logrus"
@@ -126,8 +86,7 @@ const (
 )
 
 // Determines the currently installed version of the specified repo.  If the
-// repo doesn't have a valid `version.yml` file, and it isn't using a commit
-// that maps to a version, 0.0.0 is returned.
+// repo isn't using a commit that maps to a version, 0.0.0 is returned.
 func detectVersion(r *repo.Repo) (newtutil.RepoVersion, error) {
 	ver, err := r.InstalledVersion()
 	if err != nil {
@@ -254,104 +213,6 @@ func (inst *Installer) ensureDepsInList(repos []*repo.Repo,
 	return deps
 }
 
-// Normalizes the installer's set of repo requirements.  Only the repos in the
-// specified slice are considered.
-//
-// A repo requirement takes one of two forms:
-//  * Version specifier (e.g., 1.3.0. or 0-dev).
-//  * Git commit (e.g., 1f48a3c or master).
-//
-// This function converts requirements from the second form to the first.  A
-// git commit is converted to a version number with this procedure:
-//
-// 1. If the specified commit contains a `version.yml` file, read the version
-//    from this file.
-// 2. Else, if the repo's `repository.yml` file maps the commit to a version
-//    number, use that version number.
-// 3. Else, assume 0.0.0.
-func (inst *Installer) inferReqVers(repos []*repo.Repo) error {
-	for _, r := range repos {
-		req, ok := inst.reqs[r.Name()]
-		if ok {
-			if req.Commit != "" {
-				ver, err := r.NonInstalledVersion(req.Commit)
-				if err != nil {
-					return err
-				}
-
-				if ver == nil {
-					util.OneTimeWarning(
-						"Could not detect version of requested repo "+
-							"%s:%s; assuming 0.0.0",
-						r.Name(), req.Commit)
-
-					ver = &req
-				}
-				req = *ver
-				req.Commit, err = r.HashFromVer(req)
-				if err != nil {
-					return err
-				}
-
-				inst.reqs[r.Name()] = req
-			}
-		}
-	}
-
-	return nil
-}
-
-// Determines if the `project.yml` file specifies a nonexistent repo version.
-// Only the repos in the specified slice are considered.
-//
-// @param repos                 The list of repos to consider during the check.
-// @param m                     A matrix containing all versions of the
-//                                  specified repos.
-//
-// @return error                Error if any repo requirement is invalid.
-func (inst *Installer) detectIllegalRepoReqs(
-	repos []*repo.Repo, m deprepo.Matrix) error {
-
-	var lines []string
-	for _, r := range repos {
-		req, ok := inst.reqs[r.Name()]
-		if ok {
-			row := m.FindRow(r.Name())
-			if row == nil {
-				return util.FmtNewtError(
-					"internal error; repo \"%s\" missing from matrix", r.Name())
-			}
-
-			r := inst.repos[r.Name()]
-			nreq, err := r.NormalizeVerReq(req)
-			if err != nil {
-				return err
-			}
-
-			anySatisfied := false
-			for _, ver := range row.Vers {
-				if ver.Satisfies(nreq) {
-					anySatisfied = true
-					break
-				}
-			}
-			if !anySatisfied {
-				line := fmt.Sprintf("    %s,%s", r.Name(), nreq.String())
-				lines = append(lines, line)
-			}
-		}
-	}
-
-	if len(lines) > 0 {
-		sort.Strings(lines)
-		return util.NewNewtError(
-			"project.yml file specifies nonexistent repo versions:\n" +
-				strings.Join(lines, "\n"))
-	}
-
-	return nil
-}
-
 // Indicates whether a repo should be upgraded to the specified version.  A
 // repo should be upgraded if it is not currently installed, or if a version
 // other than the desired one is installed.
@@ -399,7 +260,8 @@ func (inst *Installer) filterUpgradeList(
 
 	filtered := deprepo.VersionMap{}
 
-	for name, ver := range vm {
+	for _, name := range vm.SortedNames() {
+		ver := vm[name]
 		doUpgrade, err := inst.shouldUpgradeRepo(name, ver)
 		if err != nil {
 			return nil, err
@@ -526,22 +388,6 @@ func (inst *Installer) installPrompt(vm deprepo.VersionMap, op installOp,
 	}
 }
 
-// Filters out repos from a version map, keeping only those which are present
-// in the supplied slice.
-func filterVersionMap(
-	vm deprepo.VersionMap, keep []*repo.Repo) deprepo.VersionMap {
-
-	filtered := deprepo.VersionMap{}
-	for _, r := range keep {
-		name := r.Name()
-		if ver, ok := vm[name]; ok {
-			filtered[name] = ver
-		}
-	}
-
-	return filtered
-}
-
 // Creates a slice of repos, each corresponding to an element in the provided
 // version map.  The returned slice is sorted by repo name.
 func (inst *Installer) versionMapRepos(
@@ -564,34 +410,6 @@ func (inst *Installer) versionMapRepos(
 	return repos, nil
 }
 
-// assignCommits applies commit hashes to the selected version of each repo in
-// a version map.  In other words, it propagates `<...>-commit` requirements
-// from `project.yml` and `repository.yml` files.  If override is false,
-// attempting to set a commit for the same repo twice results in an error.
-func (inst *Installer) assignCommits(vm deprepo.VersionMap, repoName string,
-	req newtutil.RepoVersion, override bool) error {
-
-	curVer := vm[repoName]
-	if curVer.Satisfies(req) {
-		if req.Commit != "" {
-			prevCommit := vm[repoName].Commit
-			newCommit := req.Commit
-
-			if !override && prevCommit != "" && prevCommit != newCommit {
-				return util.FmtNewtError(
-					"repo %s: multiple commits: %s, %s",
-					repoName, vm[repoName].Commit, req.Commit)
-
-			}
-			ver := vm[repoName]
-			ver.Commit = req.Commit
-			vm[repoName] = ver
-		}
-	}
-
-	return nil
-}
-
 // Calculates a map of repos and version numbers that should be included in an
 // install or upgrade operation.
 func (inst *Installer) calcVersionMap(candidates []*repo.Repo) (
@@ -619,25 +437,23 @@ func (inst *Installer) calcVersionMap(candidates []*repo.Repo) (
 		}
 	}
 
-	m, err := deprepo.BuildMatrix(repoList, inst.vers)
-	if err != nil {
-		return nil, err
-	}
-
-	if err := inst.inferReqVers(repoList); err != nil {
-		return nil, err
-	}
-
-	// If the `project.yml` file specifies an invalid repo version, abort now.
-	if err := inst.detectIllegalRepoReqs(repoList, m); err != nil {
-		return nil, err
-	}
-
-	// Remove blocked repo versions from the table.
-	if err := deprepo.PruneMatrix(
-		&m, inst.repos, inst.reqs); err != nil {
+	// Ensure project.yml doesn't specify any invalid versions
+	for repoName, repoVer := range inst.reqs {
+		rvp := deprepo.RVPair{
+			Name: repoName,
+			Ver:  repoVer,
+		}
+		r := inst.repos[repoName]
+		if r == nil {
+			return nil, util.FmtNewtError(
+				"project.yml depends on an unknown repo: %s", rvp.String())
+		}
 
-		return nil, err
+		if !r.VersionIsValid(repoVer) {
+			return nil, util.FmtNewtError(
+				"project.yml depends on an unknown repo version: %s",
+				rvp.String())
+		}
 	}
 
 	// Construct a repo dependency graph from the `project.yml` version
@@ -647,54 +463,21 @@ func (inst *Installer) calcVersionMap(candidates []*repo.Repo) (
 		return nil, err
 	}
 
-	log.Debugf("Repo dependency graph:\n%s\n", dg.String())
-	log.Debugf("Repo reverse dependency graph:\n%s\n", dg.Reverse().String())
+	log.Debugf("repo dependency graph:\n%s\n", dg.String())
+	log.Debugf("repo reverse dependency graph:\n%s\n", dg.Reverse().String())
 
-	// Don't consider repos that the user excluded (if a repo list was
-	// specified).
-	deprepo.PruneDepGraph(dg, candidates)
+	// Don't consider repos that the user excluded (i.e., if a partial repo
+	// list was specified).
+	deprepo.PruneDepGraph(dg, repoList)
 
-	// Try to find a version set that satisfies the dependency graph.  If no
-	// such set exists, report the conflicts and abort.
-	vm, conflicts := deprepo.FindAcceptableVersions(m, dg)
+	vm, conflicts := deprepo.ResolveRepoDeps(dg)
 	if len(conflicts) > 0 {
 		return nil, deprepo.ConflictError(conflicts)
 	}
 
-	// If repos specify git commits in their repo-dependencies, ensure we get
-	// them.
-	rg := dg.Reverse()
-	for name, nodes := range rg {
-		for _, node := range nodes {
-			if newtutil.CompareRepoVersions(node.Ver, vm[node.Name]) == 0 {
-				// Don't consider project.yml dependencies here.  Those get
-				// assigned last so that they can override inter-repo
-				// dependencies.
-				if node.Name != "" {
-					if err := inst.assignCommits(vm, name, node.VerReq, false); err != nil {
-						return nil, err
-					}
-				}
-			}
-		}
-	}
-
-	// If project.yml specified any specific git commits, ensure we get them.
-	// Commits specified in project.yml override those from repo-dependencies.
-	for name, reqs := range inst.reqs {
-		if err := inst.assignCommits(vm, name, reqs, true); err != nil {
-			return nil, err
-		}
-	}
-
-	log.Debugf("repo version map after project.yml overrides:\n%s",
+	log.Debugf("repo version map:\n%s",
 		vm.String())
 
-	// Now that we know which repo versions we want, we can eliminate some
-	// false-positives from the repo list.
-	repoList = inst.ensureDepsInList(candidates, vm)
-	vm = filterVersionMap(vm, repoList)
-
 	return vm, nil
 }
 
@@ -918,18 +701,20 @@ func (inst *Installer) remoteRepoInfo(r *repo.Repo, vm *deprepo.VersionMap) {
 	ri := inst.gatherInfo(r, vm)
 	s := fmt.Sprintf("    * %s:", r.Name())
 
-	s += fmt.Sprintf(" %s,", ri.commitHash)
+	s += fmt.Sprintf(" %s", ri.commitHash)
 	if ri.installedVer == nil {
-		s += " (not installed)"
+		s += ", (not installed)"
 	} else if ri.errorText != "" {
-		s += fmt.Sprintf(" (unknown: %s)", ri.errorText)
+		s += fmt.Sprintf(", (unknown: %s)", ri.errorText)
 	} else {
-		s += fmt.Sprintf(" %s", ri.installedVer.String())
+		if ri.installedVer.Commit == "" {
+			s += fmt.Sprintf(", %s", ri.installedVer.String())
+		}
 		if ri.dirtyState != "" {
-			s += fmt.Sprintf(" (dirty: %s)", ri.dirtyState)
+			s += fmt.Sprintf(", (dirty: %s)", ri.dirtyState)
 		}
 		if ri.needsUpgrade {
-			s += " (needs upgrade)"
+			s += ", (needs upgrade)"
 		}
 	}
 	util.StatusMessage(util.VERBOSITY_DEFAULT, "%s\n", s)
diff --git a/newt/newtutil/repo_version.go b/newt/newtutil/repo_version.go
index 5e64b42..b8516db 100644
--- a/newt/newtutil/repo_version.go
+++ b/newt/newtutil/repo_version.go
@@ -71,31 +71,40 @@ func (v *RepoVersion) toComparable() RepoVersion {
 	return clone
 }
 
-func CompareRepoVersions(v1 RepoVersion, v2 RepoVersion) int64 {
+func CompareRepoVersions(v1 RepoVersion, v2 RepoVersion) int {
 	v1 = v1.toComparable()
 	v2 = v2.toComparable()
 
+	toInt := func(i64 int64) int {
+		if i64 < 0 {
+			return -1
+		} else if i64 > 0 {
+			return 1
+		} else {
+			return 0
+		}
+	}
+
 	if r := v1.Major - v2.Major; r != 0 {
-		return r
+		return toInt(r)
 	}
 
 	if r := v1.Minor - v2.Minor; r != 0 {
-		return r
+		return toInt(r)
 	}
 
 	if r := v1.Revision - v2.Revision; r != 0 {
-		return r
+		return toInt(r)
 	}
 
 	return 0
 }
 
-// XXX: Remove this
-func (v *RepoVersion) Satisfies(verReq RepoVersion) bool {
-	return CompareRepoVersions(verReq, *v) == 0
-}
-
 func (ver *RepoVersion) String() string {
+	if ver.Commit != "" {
+		return ver.Commit
+	}
+
 	s := fmt.Sprintf("%d", ver.Major)
 	if ver.Minor != VERSION_FLOATING {
 		s += fmt.Sprintf(".%d", ver.Minor)
@@ -108,10 +117,6 @@ func (ver *RepoVersion) String() string {
 		s += fmt.Sprintf("-%s", ver.Stability)
 	}
 
-	if ver.Commit != "" {
-		s += fmt.Sprintf("/%s", ver.Commit)
-	}
-
 	return s
 }
 
diff --git a/newt/project/project.go b/newt/project/project.go
index 42e4669..05beb1c 100644
--- a/newt/project/project.go
+++ b/newt/project/project.go
@@ -189,7 +189,6 @@ func (proj *Project) FindRepoPath(rname string) string {
 func (proj *Project) GetRepoVersion(
 	rname string) (*newtutil.RepoVersion, error) {
 
-	// First, try to read the repo's `version.yml` file.
 	r := proj.repos[rname]
 	if r == nil {
 		return nil, nil
diff --git a/newt/repo/repo.go b/newt/repo/repo.go
index 075e0bf..17d9e60 100644
--- a/newt/repo/repo.go
+++ b/newt/repo/repo.go
@@ -43,7 +43,6 @@ const REPO_NAME_LOCAL = "local"
 const REPO_DEFAULT_PERMS = 0755
 
 const REPO_FILE_NAME = "repository.yml"
-const REPO_VER_FILE_NAME = "version.yml"
 const REPOS_DIR = "repos"
 
 type Repo struct {
diff --git a/newt/repo/version.go b/newt/repo/version.go
index 561477e..5f0329b 100644
--- a/newt/repo/version.go
+++ b/newt/repo/version.go
@@ -26,14 +26,10 @@ import (
 
 	log "github.com/sirupsen/logrus"
 
-	"mynewt.apache.org/newt/newt/config"
 	"mynewt.apache.org/newt/newt/newtutil"
 	"mynewt.apache.org/newt/util"
 )
 
-var versionYmlMissing = util.FmtNewtError("version.yml missing")
-var versionYmlBad = util.FmtNewtError("version.yml bad")
-
 func versString(vers []newtutil.RepoVersion) string {
 	s := "["
 
@@ -123,6 +119,16 @@ func (r *Repo) CommitFromVer(ver newtutil.RepoVersion) (string, error) {
 	return commit, nil
 }
 
+func (r *Repo) VersionIsValid(ver newtutil.RepoVersion) bool {
+	if ver.Commit == "" {
+		_, err := r.CommitFromVer(ver)
+		return err == nil
+	}
+
+	cs, _ := r.downloader.CommitsFor(r.Path(), ver.Commit)
+	return len(cs) > 0
+}
+
 // Determines whether the two specified commits refer to the same point in the
 // repo's history.
 func (r *Repo) CommitsEquivalent(c1 string, c2 string) (bool, error) {
@@ -302,113 +308,15 @@ func (r *Repo) VersionsEqual(v1 newtutil.RepoVersion,
 	return h1 == h2
 }
 
-// Parses the `version.yml` file at the specified path.  On success, the parsed
-// version is returned.
-func parseVersionYml(path string) (newtutil.RepoVersion, error) {
-	yc, err := config.ReadFile(path)
-	if err != nil {
-		if util.IsNotExist(err) {
-			return newtutil.RepoVersion{}, versionYmlMissing
-		} else {
-			return newtutil.RepoVersion{}, versionYmlBad
-		}
-	}
-
-	verString, err := yc.GetValString("repo.version", nil)
-	util.OneTimeWarningError(err)
-
-	if verString == "" {
-		return newtutil.RepoVersion{}, versionYmlBad
-	}
-
-	ver, err := newtutil.ParseRepoVersion(verString)
-	if err != nil || !ver.IsNormalized() {
-		return newtutil.RepoVersion{}, versionYmlBad
-	}
-
-	return ver, nil
-}
-
-// Reads and parses the `version.yml` file belonging to an installed repo.
-func (r *Repo) installedVersionYml() (*newtutil.RepoVersion, error) {
-	ver, err := parseVersionYml(r.Path() + "/" + REPO_VER_FILE_NAME)
-	if err != nil {
-		return nil, err
-	}
-
-	return &ver, nil
-}
-
-// Downloads and parses the `version.yml` file from the repo at the specified
-// commit.
-func (r *Repo) nonInstalledVersionYml(
-	commit string) (*newtutil.RepoVersion, error) {
-
-	filePath, err := r.downloadFile(commit, REPO_VER_FILE_NAME)
-	if err != nil {
-		// The download failed.  Determine if the commit string is bad or if
-		// the file just doesn't exist in that commit.
-		if _, e2 := r.downloader.CommitType(r.localPath, commit); e2 != nil {
-			// Bad commit string.
-			return nil, err
-		}
-
-		// The commit exists, but it doesn't contain a `version.yml` file.
-		// Assume the commit corresponds to version 0.0.0.
-		return nil, versionYmlMissing
-	}
-
-	ver, err := parseVersionYml(filePath)
-	if err != nil {
-		return nil, err
-	}
-
-	return &ver, nil
-}
-
-// Tries to determine which repo version meets the specified criteria:
-//  * Maps to the specified commit string (or an equivalent commit).
-//  * Is equal to the specified version read from `version.yml` (if not-null).
-func (r *Repo) inferVersion(commit string, vyVer *newtutil.RepoVersion) (
-	*newtutil.RepoVersion, error) {
-
+// Tries to determine which repo version maps to the specified commit string
+// (or an equivalent commit).
+func (r *Repo) inferVersion(commit string) (*newtutil.RepoVersion, error) {
 	// Search `repository.yml` for versions that the specified commit maps to.
 	ryVers, err := r.VersFromEquivCommit(commit)
 	if err != nil {
 		return nil, err
 	}
 
-	// If valid versions were derived from both `version.yml` and the specified
-	// commit+`repository.yml`, look for a common version.
-	if vyVer != nil {
-		if len(ryVers) > 0 {
-			for _, cv := range ryVers {
-				if newtutil.CompareRepoVersions(*vyVer, cv) == 0 {
-					return vyVer, nil
-				}
-			}
-
-			util.OneTimeWarning(
-				"Version mismatch in %s:%s; repository.yml:%s, version.yml:%s",
-				r.Name(), commit, versString(ryVers), vyVer.String())
-		} else {
-			// If the set of commits doesn't contain a version from
-			// `repository.yml`, record the commit hash in the version
-			// specifier.  This will distinguish the returned version from its
-			// corresponding official release.
-			hash, err := r.downloader.HashFor(r.Path(), commit)
-			if err != nil {
-				return nil, err
-			}
-			vyVer.Commit = hash
-		}
-
-		// Always prefer the version in `version.yml`.
-		log.Debugf("Inferred version %s from %s:%s from version.yml",
-			vyVer.String(), r.Name(), commit)
-		return vyVer, nil
-	}
-
 	if len(ryVers) > 0 {
 		log.Debugf("Inferred version %s for %s:%s from repository.yml",
 			ryVers[0].String(), r.Name(), commit)
@@ -421,11 +329,6 @@ func (r *Repo) inferVersion(commit string, vyVer *newtutil.RepoVersion) (
 // Retrieves the installed version of the repo.  Returns nil if the version
 // cannot be detected.
 func (r *Repo) InstalledVersion() (*newtutil.RepoVersion, error) {
-	vyVer, err := r.installedVersionYml()
-	if err != nil && err != versionYmlMissing && err != versionYmlBad {
-		return nil, err
-	}
-
 	hash, err := r.CurrentHash()
 	if err != nil {
 		return nil, err
@@ -434,7 +337,7 @@ func (r *Repo) InstalledVersion() (*newtutil.RepoVersion, error) {
 		return nil, nil
 	}
 
-	ver, err := r.inferVersion(hash, vyVer)
+	ver, err := r.inferVersion(hash)
 	if err != nil {
 		return nil, err
 	}
@@ -447,30 +350,10 @@ func (r *Repo) InstalledVersion() (*newtutil.RepoVersion, error) {
 func (r *Repo) NonInstalledVersion(
 	commit string) (*newtutil.RepoVersion, error) {
 
-	ver, versionYmlErr := r.nonInstalledVersionYml(commit)
-	if versionYmlErr != nil &&
-		versionYmlErr != versionYmlMissing &&
-		versionYmlErr != versionYmlBad {
-
-		return nil, versionYmlErr
-	}
-
-	ver, err := r.inferVersion(commit, ver)
+	ver, err := r.inferVersion(commit)
 	if err != nil {
 		return nil, err
 	}
 
-	if ver == nil {
-		if versionYmlErr == versionYmlMissing {
-			util.OneTimeWarning(
-				"%s:%s does not contain a `version.yml` file.",
-				r.Name(), commit)
-		} else if versionYmlErr == versionYmlBad {
-			util.OneTimeWarning(
-				"%s:%s contains a malformed `version.yml` file.",
-				r.Name(), commit)
-		}
-	}
-
 	return ver, nil
 }