Handle links to multiple older workspaces.

This can happen if a previous populate operation was interrupted. Also
handle links to removed workspaces.

Change-Id: I0c12a4ba5800f7a330f5a0ff4a0733eb8ea0bc9b
diff --git a/populate/e2e_test.go b/populate/e2e_test.go
index 619e630..c57e78c 100644
--- a/populate/e2e_test.go
+++ b/populate/e2e_test.go
@@ -186,6 +186,59 @@
 
 }
 
+func TestBrokenWorkspaceLink(t *testing.T) {
+	fixture, err := newFixture()
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer fixture.Cleanup()
+
+	// We avoid talking to gitiles by inserting entries into the
+	// cache manually.
+	if err := fixture.cache.Tree.Add(gitID(ids[0]), &gitiles.Tree{
+		ID: ids[0],
+		Entries: []gitiles.TreeEntry{
+			{
+				Mode: 0100644,
+				Name: "a",
+				Type: "blob",
+				ID:   ids[1],
+				Size: newInt(42),
+			},
+		},
+	}); err != nil {
+		t.Fatal(err)
+	}
+
+	for i := 0; i <= 1; i++ {
+		if err := fixture.addWorkspace(fmt.Sprintf("m%d", i), &manifest.Manifest{
+			Project: []manifest.Project{{
+				Name:     "platform/project",
+				Path:     "p",
+				Revision: ids[0],
+			}}}); err != nil {
+			t.Fatalf("addWorkspace(%d): %v", i, err)
+		}
+	}
+
+	ws := filepath.Join(fixture.dir, "ws")
+	m0 := filepath.Join(fixture.dir, "mnt", "m0")
+	if _, _, err := Checkout(m0, ws); err != nil {
+		t.Fatalf("Checkout(m0): %v", err)
+	}
+
+	if err := os.Remove(filepath.Join(fixture.dir, "mnt", "config", "m0")); err != nil {
+		log.Fatalf("Remove: %v", err)
+	}
+
+	m1 := filepath.Join(fixture.dir, "mnt", "m1")
+	if _, changed, err := Checkout(m1, ws); err != nil {
+		t.Fatalf("Checkout(m1): %v", err)
+	} else if len(changed) > 0 {
+		t.Errorf("Got changed files %v relative to broken link.")
+	}
+}
+
 func TestFUSESymlink(t *testing.T) {
 	fixture, err := newFixture()
 	if err != nil {
diff --git a/populate/populate.go b/populate/populate.go
index a8fd03b..3df38c1 100644
--- a/populate/populate.go
+++ b/populate/populate.go
@@ -119,12 +119,13 @@
 	return nil
 }
 
-// clearLinks removes all symlinks to the RO tree. It returns the workspace name that was linked before.
-func clearLinks(mount, dir string) (string, error) {
+// clearLinks removes all symlinks to the RO tree. It returns the workspace names that were linked before.
+func clearLinks(mount, dir string) (map[string]struct{}, error) {
 	mount = filepath.Clean(mount)
 
-	var prefix string
 	var dirs []string
+
+	prevPrefixes := map[string]struct{}{}
 	if err := filepath.Walk(dir, func(n string, fi os.FileInfo, err error) error {
 		if fi == nil {
 			return fmt.Errorf("Walk %s: nil fileinfo for %s", dir, n)
@@ -135,7 +136,7 @@
 				return err
 			}
 			if strings.HasPrefix(target, mount) {
-				prefix = target
+				prevPrefixes[trimMount(target, mount)] = struct{}{}
 				if err := os.Remove(n); err != nil {
 					return err
 				}
@@ -146,22 +147,27 @@
 		}
 		return nil
 	}); err != nil {
-		return "", fmt.Errorf("Walk %s: %v", dir, err)
+		return nil, fmt.Errorf("Walk %s: %v", dir, err)
 	}
 
-	// Reverse the ordering, so we get the deepest subdirs first.
 	sort.Strings(dirs)
 	for i := range dirs {
+		// Reverse the ordering, so we get the deepest subdirs first.
 		d := dirs[len(dirs)-1-i]
 		// Ignore error: dir may still contain entries.
 		os.Remove(d)
 	}
 
-	prefix = strings.TrimPrefix(prefix, mount+"/")
-	if i := strings.Index(prefix, "/"); i != -1 {
-		prefix = prefix[:i]
+	return prevPrefixes, nil
+}
+
+func trimMount(dir, mount string) string {
+	dir = strings.TrimPrefix(dir, mount+"/")
+	if i := strings.Index(dir, "/"); i != -1 {
+		dir = dir[:i]
 	}
-	return prefix, nil
+
+	return dir
 }
 
 // Returns the filenames (as relative paths) in newDir that have
@@ -192,18 +198,26 @@
 // Returns the files that should be touched.
 func Checkout(ro, rw string) (added, changed []string, err error) {
 	ro = filepath.Clean(ro)
-	wsName, err := clearLinks(filepath.Dir(ro), rw)
+	wsNames, err := clearLinks(filepath.Dir(ro), rw)
 	if err != nil {
 		return nil, nil, err
 	}
-	oldRoot := filepath.Join(filepath.Dir(ro), wsName)
+
+	oldRoot := ""
+	for nm := range wsNames {
+		r := filepath.Join(filepath.Dir(ro), nm)
+		if _, err := os.Stat(r); err == nil && r != ro {
+			oldRoot = r
+			break
+		}
+	}
 
 	// Do the file system traversals in parallel.
 	errs := make(chan error, 3)
 	var rwTree, roTree *repoTree
 	var oldInfos map[string]*fileInfo
 
-	if wsName != "" {
+	if oldRoot != "" {
 		go func() {
 			t, err := repoTreeFromSlothFS(oldRoot)
 			if t != nil {