Merge "jiri: Use the latest update snapshot manifest when computing LocalProjects."
diff --git a/jiri/.api b/jiri/.api
index 384b799..bbf12c1 100644
--- a/jiri/.api
+++ b/jiri/.api
@@ -14,6 +14,7 @@
 pkg jiri, method (*X) ResolveManifestPath(string) (string, error)
 pkg jiri, method (*X) RootMetaDir() string
 pkg jiri, method (*X) UpdateHistoryDir() string
+pkg jiri, method (*X) UpdateHistoryLatestLink() string
 pkg jiri, method (*X) UsageErrorf(string, ...interface{}) error
 pkg jiri, method (*X) UsingOldManifests() bool
 pkg jiri, method (RelPath) Abs(*X) string
diff --git a/jiri/x.go b/jiri/x.go
index 20133dd..6a574b3 100644
--- a/jiri/x.go
+++ b/jiri/x.go
@@ -136,6 +136,12 @@
 	return filepath.Join(x.RootMetaDir(), "update_history")
 }
 
+// UpdateHistoryLatestLink returns the path to a symlink that points to the
+// latest update in the update history directory.
+func (x *X) UpdateHistoryLatestLink() string {
+	return filepath.Join(x.UpdateHistoryDir(), "latest")
+}
+
 // ResolveManifestPath resolves the given manifest name to an absolute path in
 // the local filesystem.
 func (x *X) ResolveManifestPath(name string) (string, error) {
diff --git a/profiles/profilesreader/reader.go b/profiles/profilesreader/reader.go
index 7949cc7..eff4533 100644
--- a/profiles/profilesreader/reader.go
+++ b/profiles/profilesreader/reader.go
@@ -117,7 +117,7 @@
 	if err != nil {
 		return nil, err
 	}
-	projects, tools, err := project.ReadManifest(jirix)
+	projects, tools, err := project.ReadJiriManifest(jirix)
 	if err != nil {
 		return nil, err
 	}
diff --git a/project/.api b/project/.api
index f689087..f475c27 100644
--- a/project/.api
+++ b/project/.api
@@ -17,7 +17,7 @@
 pkg project, func PollProjects(*jiri.X, map[string]struct{}) (Update, error)
 pkg project, func ProjectAtPath(*jiri.X, string) (Project, error)
 pkg project, func ProjectFromFile(*jiri.X, string) (*Project, error)
-pkg project, func ReadManifest(*jiri.X) (Projects, Tools, error)
+pkg project, func ReadJiriManifest(*jiri.X) (Projects, Tools, error)
 pkg project, func TransitionBinDir(*jiri.X) error
 pkg project, func UpdateUniverse(*jiri.X, bool) error
 pkg project, method (*Manifest) ToBytes() ([]byte, error)
diff --git a/project/paths.go b/project/paths.go
index 7ff4bb8..d34c41f 100644
--- a/project/paths.go
+++ b/project/paths.go
@@ -18,7 +18,7 @@
 // breaks.  We should revisit the whole data directory thing, and in particular
 // see if we can get rid of tools having to know their own names.
 func DataDirPath(jirix *jiri.X, toolName string) (string, error) {
-	projects, tools, err := ReadManifest(jirix)
+	projects, tools, err := ReadJiriManifest(jirix)
 	if err != nil {
 		return "", err
 	}
diff --git a/project/project.go b/project/project.go
index 30cc6ad..07fdae7 100644
--- a/project/project.go
+++ b/project/project.go
@@ -564,7 +564,7 @@
 	}
 
 	// Add all tools from the current manifest to the snapshot manifest.
-	_, tools, err := ReadManifest(jirix)
+	_, tools, err := ReadJiriManifest(jirix)
 	if err != nil {
 		return err
 	}
@@ -643,11 +643,16 @@
 	jirix.TimerPush("local projects")
 	defer jirix.TimerPop()
 
-	if scanMode == FastScan {
-		// Fast path:  Full scan was not requested, and all projects in
-		// manifest exist on local filesystem.  We just use the projects
-		// directly from the manifest.
-		manifestProjects, _, err := ReadManifest(jirix)
+	latestUpdateSnapshot := jirix.UpdateHistoryLatestLink()
+	latestUpdateSnapshotExists, err := jirix.NewSeq().IsFile(latestUpdateSnapshot)
+	if err != nil {
+		return nil, err
+	}
+	if scanMode == FastScan && latestUpdateSnapshotExists {
+		// Fast path:  Full scan was not requested, and we have a snapshot
+		// containing the latest update.  Check that the projects listed in the
+		// snapshot exist locally.  If not, then fall back on the slow path.
+		manifestProjects, _, err := readManifestFile(jirix, latestUpdateSnapshot)
 		if err != nil {
 			return nil, err
 		}
@@ -665,7 +670,7 @@
 	// JIRI_ROOT.
 	projects := Projects{}
 	jirix.TimerPush("scan fs")
-	err := findLocalProjects(jirix, jirix.Root, projects)
+	err = findLocalProjects(jirix, jirix.Root, projects)
 	jirix.TimerPop()
 	if err != nil {
 		return nil, err
@@ -711,7 +716,7 @@
 	if err != nil {
 		return nil, err
 	}
-	remoteProjects, _, err := ReadManifest(jirix)
+	remoteProjects, _, err := ReadJiriManifest(jirix)
 	if err != nil {
 		return nil, err
 	}
@@ -772,15 +777,19 @@
 	return update, nil
 }
 
-// ReadManifest retrieves and parses the manifest that determines what
-// projects and tools are part of the jiri universe.
-func ReadManifest(jirix *jiri.X) (Projects, Tools, error) {
-	jirix.TimerPush("read manifest")
-	defer jirix.TimerPop()
+// ReadJiriManifest reads and parses the .jiri_manifest file.
+func ReadJiriManifest(jirix *jiri.X) (Projects, Tools, error) {
 	file, err := jirix.ResolveManifestPath(jirix.Manifest())
 	if err != nil {
 		return nil, nil, err
 	}
+	return readManifestFile(jirix, file)
+}
+
+// readManifestFile reads and parses the manifest with the given filename.
+func readManifestFile(jirix *jiri.X, file string) (Projects, Tools, error) {
+	jirix.TimerPush("read manifest")
+	defer jirix.TimerPop()
 	var imp importer
 	projects, tools := Projects{}, Tools{}
 	if err := imp.Load(jirix, jirix.Root, file, "", projects, tools); err != nil {
@@ -853,7 +862,7 @@
 	if err := updateManifestProjects(jirix); err != nil {
 		return err
 	}
-	remoteProjects, remoteTools, err := ReadManifest(jirix)
+	remoteProjects, remoteTools, err := ReadJiriManifest(jirix)
 	if err != nil {
 		return err
 	}
@@ -2151,7 +2160,7 @@
 // ParseNames identifies the set of projects that a jiri command should
 // be applied to.
 func ParseNames(jirix *jiri.X, args []string, defaultProjects map[string]struct{}) (Projects, error) {
-	manifestProjects, _, err := ReadManifest(jirix)
+	manifestProjects, _, err := ReadJiriManifest(jirix)
 	if err != nil {
 		return nil, err
 	}
diff --git a/project/project_test.go b/project/project_test.go
index 706e270..95ae6d0 100644
--- a/project/project_test.go
+++ b/project/project_test.go
@@ -319,8 +319,6 @@
 	jirix, cleanup := jiritest.NewX(t)
 	defer cleanup()
 
-	manifestDir := setupNewProject(t, jirix, jirix.Root, ".manifest", false)
-
 	// Create some projects.
 	numProjects, projectPaths := 3, []string{}
 	for i := 0; i < numProjects; i++ {
@@ -337,8 +335,23 @@
 		projectPaths = append(projectPaths, path)
 	}
 
-	// Create manifest but only tell it about the first project.
-	createRemoteManifest(t, jirix, manifestDir, projectPaths[:1])
+	// Create a latest update snapshot but only tell it about the first project.
+	manifest := project.Manifest{
+		Projects: []project.Project{
+			{
+				Name:     projectPaths[0],
+				Path:     localProjectName(0),
+				Protocol: "git",
+				Remote:   projectPaths[0],
+			},
+		},
+	}
+	if err := jirix.NewSeq().MkdirAll(jirix.UpdateHistoryDir(), 0755).Done(); err != nil {
+		t.Fatalf("MkdirAll(%v) failed: %v", jirix.UpdateHistoryDir(), err)
+	}
+	if err := manifest.ToFile(jirix, jirix.UpdateHistoryLatestLink()); err != nil {
+		t.Fatalf("manifest.ToFile(%v) failed: %v", jirix.UpdateHistoryLatestLink(), err)
+	}
 
 	// LocalProjects with scanMode = FastScan should only find the first
 	// project.
diff --git a/rebuild.go b/rebuild.go
index 4b65150..3f68b9a 100644
--- a/rebuild.go
+++ b/rebuild.go
@@ -29,7 +29,7 @@
 }
 
 func runRebuild(jirix *jiri.X, args []string) (e error) {
-	_, tools, err := project.ReadManifest(jirix)
+	_, tools, err := project.ReadJiriManifest(jirix)
 	if err != nil {
 		return err
 	}
diff --git a/runutil/sequence.go b/runutil/sequence.go
index 9bf28d9..40754d4 100644
--- a/runutil/sequence.go
+++ b/runutil/sequence.go
@@ -947,6 +947,9 @@
 		fileInfo, err = os.Stat(dirname)
 		return err
 	}, fmt.Sprintf("isdir %q", dirname))
+	if IsNotExist(err) {
+		return false, nil
+	}
 	if err != nil {
 		return false, err
 	}
@@ -967,6 +970,9 @@
 		fileInfo, err = os.Stat(file)
 		return err
 	}, fmt.Sprintf("isfile %q", file))
+	if IsNotExist(err) {
+		return false, nil
+	}
 	if err != nil {
 		return false, err
 	}
diff --git a/update.go b/update.go
index 05f46dd..f9ef8a7 100644
--- a/update.go
+++ b/update.go
@@ -65,6 +65,11 @@
 	if err := project.CreateSnapshot(jirix, snapshotFile); err != nil {
 		return err
 	}
+	// Point the "latest" update history symlink to the new snapshot file.
+	link := jirix.UpdateHistoryLatestLink()
+	if err := jirix.NewSeq().RemoveAll(link).Symlink(snapshotFile, link).Done(); err != nil {
+		return err
+	}
 	// Only attempt the bin dir transition after the update has succeeded, to
 	// avoid messy partial states.
 	return project.TransitionBinDir(jirix)