From 31a590e0cd68a693c0b58aa88e865f0544bd6778 Mon Sep 17 00:00:00 2001 From: Josip Sokcevic Date: Tue, 2 Jan 2024 19:22:53 +0000 Subject: [PATCH] [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 Commit-Queue: Josip Sokcevic --- scm.py | 26 ++++++++++++++++++-------- tests/scm_unittest.py | 6 ++++-- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/scm.py b/scm.py index 5dc9583ad..4118709b2 100644 --- a/scm.py +++ b/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 diff --git a/tests/scm_unittest.py b/tests/scm_unittest.py index e0b1a6d61..81b4d6e68 100755 --- a/tests/scm_unittest.py +++ b/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),