diff --git a/lib/util/git/index.spec.ts b/lib/util/git/index.spec.ts index 11e5182a856e81726d75e44b90b6a7902d15b560..ca69d170d0fc27dacc2f2ccb8d2ac4f77130e57f 100644 --- a/lib/util/git/index.spec.ts +++ b/lib/util/git/index.spec.ts @@ -12,18 +12,20 @@ import type { BranchCache } from '../cache/repository/types'; import { newlineRegex, regEx } from '../regex'; import * as _conflictsCache from './conflicts-cache'; import * as _modifiedCache from './modified-cache'; +import * as _parentShaCache from './parent-sha-cache'; import type { FileChange } from './types'; import * as git from '.'; import { setNoVerify } from '.'; jest.mock('./conflicts-cache'); jest.mock('./modified-cache'); +jest.mock('./parent-sha-cache'); jest.mock('delay'); jest.mock('../cache/repository'); const repoCache = mocked(_repoCache); const conflictsCache = mocked(_conflictsCache); const modifiedCache = mocked(_modifiedCache); - +const parentShaCache = mocked(_parentShaCache); // Class is no longer exported const SimpleGit = Git().constructor as { prototype: ReturnType<typeof Git> }; @@ -114,6 +116,7 @@ describe('util/git/index', () => { // override some local git settings for better testing const local = Git(tmpDir.path); await local.addConfig('commit.gpgsign', 'false'); + parentShaCache.getCachedBranchParentShaResult.mockReturnValue(null); }); afterEach(async () => { @@ -319,9 +322,14 @@ describe('util/git/index', () => { expect(parentSha).toEqual(git.getBranchCommit(defaultBranch)); }); - it('should return false if not found', async () => { + it('should return null if not found', async () => { expect(await git.getBranchParentSha('not_found')).toBeNull(); }); + + it('should return cached value', async () => { + parentShaCache.getCachedBranchParentShaResult.mockReturnValueOnce('111'); + expect(await git.getBranchParentSha('not_found')).toBe('111'); + }); }); describe('getBranchFiles(branchName)', () => { diff --git a/lib/util/git/index.ts b/lib/util/git/index.ts index 4e99a4527d35ce39b3e9c48beafa11f240231fb3..c7b52a37baf3acd153f4f47f588af1f415bcb79b 100644 --- a/lib/util/git/index.ts +++ b/lib/util/git/index.ts @@ -39,6 +39,7 @@ import { getCachedModifiedResult, setCachedModifiedResult, } from './modified-cache'; +import { getCachedBranchParentShaResult } from './parent-sha-cache'; import { configSigningKey, writePrivateKey } from './private-key'; import type { CommitFilesConfig, @@ -467,9 +468,14 @@ export function getBranchCommit(branchName: string): CommitSha | null { export async function getBranchParentSha( branchName: string ): Promise<CommitSha | null> { + const branchSha = getBranchCommit(branchName); + let parentSha = getCachedBranchParentShaResult(branchName, branchSha); + if (parentSha !== null) { + return parentSha; + } + try { - const branchSha = getBranchCommit(branchName); - const parentSha = await git.revparse([`${branchSha}^`]); + parentSha = await git.revparse([`${branchSha}^`]); return parentSha; } catch (err) { logger.debug({ err }, 'Error getting branch parent sha'); diff --git a/lib/util/git/parent-sha-cache.spec.ts b/lib/util/git/parent-sha-cache.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..9e88085d832086a3a90f17d4b0e0ea4351ba392c --- /dev/null +++ b/lib/util/git/parent-sha-cache.spec.ts @@ -0,0 +1,71 @@ +import { mocked } from '../../../test/util'; +import * as _repositoryCache from '../cache/repository'; +import type { BranchCache, RepoCacheData } from '../cache/repository/types'; +import { getCachedBranchParentShaResult } from './parent-sha-cache'; + +jest.mock('../cache/repository'); +const repositoryCache = mocked(_repositoryCache); + +describe('util/git/parent-sha-cache', () => { + let repoCache: RepoCacheData = {}; + + beforeEach(() => { + repoCache = {}; + repositoryCache.getCache.mockReturnValue(repoCache); + }); + + describe('getCachedBranchParentShaResult', () => { + it('returns null if cache is not populated', () => { + expect(getCachedBranchParentShaResult('foo', '111')).toBeNull(); + }); + + it('returns null if target key not found', () => { + repositoryCache.getCache.mockReturnValue({ + branches: [ + { + branchName: 'not_foo', + sha: '111', + } as BranchCache, + ], + }); + expect(getCachedBranchParentShaResult('foo', '111')).toBeNull(); + }); + + it('returns null if target key is null', () => { + repositoryCache.getCache.mockReturnValue({ + branches: [ + { + branchName: 'not_foo', + sha: null, + } as BranchCache, + ], + }); + expect(getCachedBranchParentShaResult('foo', '111')).toBeNull(); + }); + + it('returns null if target SHA has changed', () => { + repositoryCache.getCache.mockReturnValue({ + branches: [ + { + branchName: 'foo', + sha: '222', + } as BranchCache, + ], + }); + expect(getCachedBranchParentShaResult('foo', '111')).toBeNull(); + }); + + it('returns cached value', () => { + repositoryCache.getCache.mockReturnValue({ + branches: [ + { + branchName: 'foo', + sha: '111', + parentSha: '000', + } as BranchCache, + ], + }); + expect(getCachedBranchParentShaResult('foo', '111')).toBe('000'); + }); + }); +}); diff --git a/lib/util/git/parent-sha-cache.ts b/lib/util/git/parent-sha-cache.ts new file mode 100644 index 0000000000000000000000000000000000000000..fef31e074bf3530cac82f209062c900b29371cc6 --- /dev/null +++ b/lib/util/git/parent-sha-cache.ts @@ -0,0 +1,15 @@ +import { getCache } from '../cache/repository'; + +export function getCachedBranchParentShaResult( + branchName: string, + branchSha: string | null +): string | null { + const { branches } = getCache(); + const branch = branches?.find((branch) => branch.branchName === branchName); + + if (branchSha === branch?.sha) { + return branch.parentSha; + } + + return null; +}