[cache] Skip cache updates if they contain the required revision

On my local machine, a no-op cache update now takes 0.713s, previously
it was 4.676s.

Change-Id: I457ac39c68400e64869fb6769c4a958a392dba0c
diff --git a/gitutil/git.go b/gitutil/git.go
index 45eb0f5..dbc9e62 100644
--- a/gitutil/git.go
+++ b/gitutil/git.go
@@ -215,6 +215,11 @@
 	return branches, nil
 }
 
+// CheckRevAvailable runs cat-file on a commit or tag is available locally.
+func (g *Git) CheckRevAvailable(rev string) error {
+	return g.run("cat-file", "-e", rev)
+}
+
 // CheckoutBranch checks out the given branch.
 func (g *Git) CheckoutBranch(branch string, opts ...CheckoutOpt) error {
 	args := []string{"checkout"}
diff --git a/project/project.go b/project/project.go
index 7462268..13a07be 100644
--- a/project/project.go
+++ b/project/project.go
@@ -1830,12 +1830,17 @@
 				continue
 			}
 			processingPath[cacheDirPath] = true
-			wg.Add(1)
-			fetchLimit <- struct{}{}
 			if err := project.fillDefaults(); err != nil {
 				errs <- err
 				continue
 			}
+			scm := gitutil.New(jirix, gitutil.RootDirOpt(cacheDirPath))
+			if err := scm.CheckRevAvailable(project.Revision); err == nil {
+				jirix.Logger.Infof("%s cache up-to-date; skipping\n", project.Name)
+				continue
+			}
+			wg.Add(1)
+			fetchLimit <- struct{}{}
 			go func(dir, remote string, depth int, branch string) {
 				defer func() { <-fetchLimit }()
 				defer wg.Done()
diff --git a/project/project_test.go b/project/project_test.go
index 5eae934..0e10e66 100644
--- a/project/project_test.go
+++ b/project/project_test.go
@@ -471,6 +471,18 @@
 	checkReadme(t, fake.X, localProjects[1], "master commit")
 	checkJiriRevFiles(t, fake.X, localProjects[1])
 
+	gitLocal := gitutil.New(fake.X, gitutil.RootDirOpt(localProjects[1].Path))
+	localRev, err := gitLocal.CurrentRevision()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// Update the manifest with our new HEAD position.
+	fake.AddProjectOverride(localProjects[1].Name, localProjects[1].Remote, localRev)
+	if err := fake.UpdateUniverse(false); err != nil {
+		t.Fatal(err)
+	}
+
 	// Check that cache was updated
 	cacheDirPath, err := localProjects[1].CacheDirPath(fake.X)
 	if err != nil {
@@ -481,11 +493,6 @@
 	if err != nil {
 		t.Fatal(err)
 	}
-	gitLocal := gitutil.New(fake.X, gitutil.RootDirOpt(localProjects[1].Path))
-	localRev, err := gitLocal.CurrentRevision()
-	if err != nil {
-		t.Fatal(err)
-	}
 	if cacheRev != localRev {
 		t.Fatalf("Cache revision(%v) not equal to local revision(%v)", cacheRev, localRev)
 	}