Browse Source

[scm] Cache ResolveCommit call

The cache won't be used if ResolveCommit throws an exception or when
non-sha1 is used.

This optimization saves about 7% of overall gclient sync on
chromium/src.

R=gavinmak@google.com

Change-Id: I9c36d937dc7b8553818888e2e9c4eecd029a978c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/5147496
Reviewed-by: Gavin Mak <gavinmak@google.com>
Commit-Queue: Josip Sokcevic <sokcevic@chromium.org>
Josip Sokcevic 1 year ago
parent
commit
31a590e0cd
2 changed files with 22 additions and 10 deletions
  1. 18 8
      scm.py
  2. 4 2
      tests/scm_unittest.py

+ 18 - 8
scm.py

@@ -96,6 +96,7 @@ def only_int(val):
 
 class GIT(object):
     current_version = None
+    rev_parse_cache = {}
 
     @staticmethod
     def ApplyEnvVars(kwargs):
@@ -461,20 +462,27 @@ class GIT(object):
 
     @staticmethod
     def ResolveCommit(cwd, rev):
+        cache_key = None
         # We do this instead of rev-parse --verify rev^{commit}, since on
         # Windows git can be either an executable or batch script, each of which
         # requires escaping the caret (^) a different way.
         if gclient_utils.IsFullGitSha(rev):
+            # Only cache full SHAs
+            cache_key = hash(cwd + rev)
+            if val := GIT.rev_parse_cache.get(cache_key):
+                return val
+
             # git-rev parse --verify FULL_GIT_SHA always succeeds, even if we
             # don't have FULL_GIT_SHA locally. Removing the last character
             # forces git to check if FULL_GIT_SHA refers to an object in the
             # local database.
             rev = rev[:-1]
-        try:
-            return GIT.Capture(['rev-parse', '--quiet', '--verify', rev],
-                               cwd=cwd)
-        except subprocess2.CalledProcessError:
-            return None
+        res = GIT.Capture(['rev-parse', '--quiet', '--verify', rev], cwd=cwd)
+        if cache_key:
+            # We don't expect concurrent execution, so we don't lock anything.
+            GIT.rev_parse_cache[cache_key] = res
+
+        return res
 
     @staticmethod
     def IsValidRevision(cwd, rev, sha_only=False):
@@ -482,9 +490,11 @@ class GIT(object):
 
     sha_only: Fail unless rev is a sha hash.
     """
-        sha = GIT.ResolveCommit(cwd, rev)
-        if sha is None:
-            return False
+        try:
+            sha = GIT.ResolveCommit(cwd, rev)
+        except subprocess2.CalledProcessError:
+            return None
+
         if sha_only:
             return sha == rev.lower()
         return True

+ 4 - 2
tests/scm_unittest.py

@@ -143,8 +143,10 @@ class RealGitTest(fake_repos.FakeReposTestBase):
             self.skipTest('git fake repos not available')
 
     def testResolveCommit(self):
-        self.assertIsNone(scm.GIT.ResolveCommit(self.cwd, 'zebra'))
-        self.assertIsNone(scm.GIT.ResolveCommit(self.cwd, 'r123456'))
+        with self.assertRaises(Exception):
+            scm.GIT.ResolveCommit(self.cwd, 'zebra')
+        with self.assertRaises(Exception):
+            scm.GIT.ResolveCommit(self.cwd, 'r123456')
         first_rev = self.githash('repo_1', 1)
         self.assertEqual(first_rev, scm.GIT.ResolveCommit(self.cwd, first_rev))
         self.assertEqual(self.githash('repo_1', 2),