diff --git a/lib/modules/platform/comment.spec.ts b/lib/modules/platform/comment.spec.ts
index 90ad873e764c0bf5f7b78bd7d962938164ebbcf0..0bc1310d93be0e524b6d003889999d9cc5c9ee00 100644
--- a/lib/modules/platform/comment.spec.ts
+++ b/lib/modules/platform/comment.spec.ts
@@ -1,6 +1,6 @@
 import { mocked, platform } from '../../../test/util';
 import * as _cache from '../../util/cache/repository';
-import type { Cache } from '../../util/cache/repository/types';
+import type { RepoCacheData } from '../../util/cache/repository/types';
 import { ensureComment, ensureCommentRemoval } from './comment';
 
 jest.mock('.');
@@ -9,7 +9,7 @@ jest.mock('../../util/cache/repository');
 const cache = mocked(_cache);
 
 describe('modules/platform/comment', () => {
-  let repoCache: Cache = {};
+  let repoCache: RepoCacheData = {};
 
   beforeEach(() => {
     repoCache = {};
diff --git a/lib/util/cache/repository/index.spec.ts b/lib/util/cache/repository/index.spec.ts
index 8e4d01824b61aca657ef0f2b7ee45a36b96f2545..8b3e0cc3aa471d7af96c64ac5cfee533e0cf7c34 100644
--- a/lib/util/cache/repository/index.spec.ts
+++ b/lib/util/cache/repository/index.spec.ts
@@ -1,6 +1,8 @@
 import * as _fs from 'fs-extra';
 import { mocked } from '../../../../test/util';
 import { GlobalConfig } from '../../../config/global';
+import type { RenovateConfig } from '../../../config/types';
+import type { RepoCache } from './types';
 import * as repositoryCache from '.';
 
 jest.mock('fs-extra');
@@ -9,54 +11,121 @@ const fs = mocked(_fs);
 
 describe('util/cache/repository/index', () => {
   beforeEach(() => {
+    repositoryCache.reset();
     jest.resetAllMocks();
     GlobalConfig.set({ cacheDir: '/tmp/renovate/cache/' });
   });
 
-  const config = {
+  const config: RenovateConfig = {
     platform: 'github',
     repository: 'abc/def',
+    repositoryCache: 'enabled',
   };
 
-  it('catches and returns', async () => {
-    await repositoryCache.initialize({});
-    expect(fs.readFile.mock.calls).toHaveLength(0);
-  });
+  const repoCache: RepoCache = {
+    revision: 11,
+    repository: 'abc/def',
+    data: {},
+  };
 
   it('returns if cache not enabled', async () => {
     await repositoryCache.initialize({
       ...config,
       repositoryCache: 'disabled',
     });
-    expect(fs.readFile.mock.calls).toHaveLength(0);
+    expect(fs.readFile).not.toHaveBeenCalled();
   });
 
-  it('resets if invalid', async () => {
-    fs.readFile.mockResolvedValueOnce('{}' as any);
-    await repositoryCache.initialize({
-      ...config,
-      repositoryCache: 'enabled',
-    });
-    expect(repositoryCache.getCache()).toEqual({
-      repository: 'abc/def',
-      revision: repositoryCache.CACHE_REVISION,
-    });
+  it('resets if repository does not match', async () => {
+    fs.readFile.mockResolvedValueOnce(
+      JSON.stringify({
+        ...repoCache,
+        repository: 'foo/bar',
+        data: { semanticCommits: 'enabled' },
+      }) as never
+    );
+
+    await repositoryCache.initialize(config);
+
+    expect(repositoryCache.getCache()).toEqual({});
   });
 
   it('reads from cache and finalizes', async () => {
     fs.readFile.mockResolvedValueOnce(
-      `{"repository":"abc/def","revision":${repositoryCache.CACHE_REVISION}}` as any
+      JSON.stringify({
+        ...repoCache,
+        data: { semanticCommits: 'enabled' },
+      }) as never
     );
-    await repositoryCache.initialize({
-      ...config,
-      repositoryCache: 'enabled',
-    });
+
+    await repositoryCache.initialize(config);
+
+    expect(fs.readFile).toHaveBeenCalled();
+
+    const cache = repositoryCache.getCache();
+    expect(cache).toEqual({ semanticCommits: 'enabled' });
+
+    cache.semanticCommits = 'disabled';
     await repositoryCache.finalize();
-    expect(fs.readFile.mock.calls).toHaveLength(1);
-    expect(fs.outputFile.mock.calls).toHaveLength(1);
+    expect(fs.outputFile).toHaveBeenCalledWith(
+      '/tmp/renovate/cache/renovate/repository/github/abc/def.json',
+      JSON.stringify({
+        revision: 11,
+        repository: 'abc/def',
+        data: { semanticCommits: 'disabled' },
+      })
+    );
+  });
+
+  it('migrates from 10 to 11 revision', async () => {
+    fs.readFile.mockResolvedValueOnce(
+      JSON.stringify({
+        revision: 10,
+        repository: 'abc/def',
+        semanticCommits: 'enabled',
+      }) as never
+    );
+
+    await repositoryCache.initialize(config);
+
+    const cache = repositoryCache.getCache();
+    expect(cache).toEqual({ semanticCommits: 'enabled' });
+
+    cache.semanticCommits = 'disabled';
+    await repositoryCache.finalize();
+    expect(fs.outputFile).toHaveBeenCalledWith(
+      '/tmp/renovate/cache/renovate/repository/github/abc/def.json',
+      JSON.stringify({
+        revision: 11,
+        repository: 'abc/def',
+        data: { semanticCommits: 'disabled' },
+      })
+    );
   });
 
-  it('gets', () => {
+  it('does not migrate from older revisions to 11', async () => {
+    fs.readFile.mockResolvedValueOnce(
+      JSON.stringify({
+        revision: 9,
+        repository: 'abc/def',
+        semanticCommits: 'enabled',
+      }) as never
+    );
+
+    await repositoryCache.initialize(config);
+
+    const cache = repositoryCache.getCache();
+    expect(cache).toEqual({});
+  });
+
+  it('returns empty cache for non-initialized cache', () => {
     expect(repositoryCache.getCache()).toEqual({});
   });
+
+  it('returns empty cache after initialization error', async () => {
+    fs.readFile.mockRejectedValueOnce(new Error('unknown error'));
+    await repositoryCache.initialize(config);
+    const cache = repositoryCache.getCache();
+    expect(cache).toEqual({});
+  });
 });
diff --git a/lib/util/cache/repository/index.ts b/lib/util/cache/repository/index.ts
index 122bb2d327ed825c01087e77caed07397635eedf..ec298bd668a52cae1e0dcd569c2078fc873ce6d7 100644
--- a/lib/util/cache/repository/index.ts
+++ b/lib/util/cache/repository/index.ts
@@ -1,3 +1,4 @@
+import is from '@sindresorhus/is';
 import fs from 'fs-extra';
 import upath from 'upath';
 import { GlobalConfig } from '../../../config/global';
@@ -6,70 +7,102 @@ import type {
   RepositoryCacheConfig,
 } from '../../../config/types';
 import { logger } from '../../../logger';
-import type { Cache } from './types';
+import type { RepoCache, RepoCacheData } from './types';
 
 // Increment this whenever there could be incompatibilities between old and new cache structure
-export const CACHE_REVISION = 10;
+const CACHE_REVISION = 11;
 
 let repositoryCache: RepositoryCacheConfig | undefined = 'disabled';
 let cacheFileName: string | null = null;
-let cache: Cache | null = Object.create({});
+
+let repository: string | null | undefined = null;
+let data: RepoCacheData | null = null;
+
+export function reset(): void {
+  repository = null;
+  data = null;
+}
 
 export function getCacheFileName(config: RenovateConfig): string {
-  return upath.join(
-    GlobalConfig.get('cacheDir'),
-    '/renovate/repository/',
-    config.platform,
-    `${config.repository}.json`
-  );
+  const cacheDir = GlobalConfig.get('cacheDir');
+  const repoCachePath = '/renovate/repository/';
+  const platform = config.platform;
+  const fileName = `${config.repository}.json`;
+  return upath.join(cacheDir, repoCachePath, platform, fileName);
 }
 
-function validate(config: RenovateConfig, input: any): Cache | null {
-  if (
-    input &&
+function isCacheValid(
+  config: RenovateConfig,
+  input: unknown
+): input is RepoCache {
+  return (
+    is.plainObject(input) &&
+    is.string(input.repository) &&
+    is.safeInteger(input.revision) &&
     input.repository === config.repository &&
     input.revision === CACHE_REVISION
-  ) {
-    logger.debug('Repository cache is valid');
-    return input as Cache;
-  }
-  logger.info('Repository cache invalidated');
-  // reset
-  return null;
+  );
 }
 
-function createCache(repository?: string): Cache {
-  const res: Cache = Object.create({});
-  res.repository = repository;
-  res.revision = CACHE_REVISION;
-  return res;
+function canBeMigratedToV11(
+  config: RenovateConfig,
+  input: unknown
+): input is RepoCacheData & { repository?: string; revision?: number } {
+  return (
+    is.plainObject(input) &&
+    is.string(input.repository) &&
+    is.safeInteger(input.revision) &&
+    input.repository === config.repository &&
+    input.revision === 10
+  );
 }
 
 export async function initialize(config: RenovateConfig): Promise<void> {
-  cache = null;
+  reset();
+
   try {
     cacheFileName = getCacheFileName(config);
     repositoryCache = config.repositoryCache;
     if (repositoryCache === 'enabled') {
-      cache = validate(
-        config,
-        JSON.parse(await fs.readFile(cacheFileName, 'utf8'))
-      );
+      const rawCache = await fs.readFile(cacheFileName, 'utf8');
+      const oldCache = JSON.parse(rawCache);
+      if (isCacheValid(config, oldCache)) {
+        data = oldCache.data;
+        logger.debug('Repository cache is valid');
+      } else if (canBeMigratedToV11(config, oldCache)) {
+        delete oldCache.repository;
+        delete oldCache.revision;
+        data = oldCache;
+        logger.debug('Repository cache is migrated');
+      } else {
+        logger.debug('Repository cache is invalid');
+      }
     }
   } catch (err) {
     logger.debug({ cacheFileName }, 'Repository cache not found');
   }
-  cache ||= createCache(config.repository);
+
+  repository = config.repository;
+  data ??= {};
 }
 
-export function getCache(): Cache {
-  return cache ?? createCache();
+export function getCache(): RepoCacheData {
+  data ??= {};
+  return data;
 }
 
 export async function finalize(): Promise<void> {
-  if (cacheFileName && cache && repositoryCache !== 'disabled') {
-    await fs.outputFile(cacheFileName, JSON.stringify(cache));
+  if (cacheFileName && repository && data && repositoryCache !== 'disabled') {
+    await fs.outputFile(
+      cacheFileName,
+      JSON.stringify({
+        revision: CACHE_REVISION,
+        repository,
+        data,
+      })
+    );
   }
   cacheFileName = null;
-  cache = Object.create({});
+
+  reset();
 }
diff --git a/lib/util/cache/repository/types.ts b/lib/util/cache/repository/types.ts
index a9b5e0ada98bcac9c82f294145d8442436a8a84f..043fc2151f1049b71b1ed468805e70989b2ab293 100644
--- a/lib/util/cache/repository/types.ts
+++ b/lib/util/cache/repository/types.ts
@@ -32,12 +32,10 @@ export interface BranchCache {
   upgrades: BranchUpgradeCache[];
 }
 
-export interface Cache {
+export interface RepoCacheData {
   configFileName?: string;
   semanticCommits?: 'enabled' | 'disabled';
   branches?: BranchCache[];
-  repository?: string;
-  revision?: number;
   init?: RepoInitConfig;
   scan?: Record<string, BaseBranchCache>;
   lastPlatformAutomergeFailure?: string;
@@ -47,3 +45,9 @@ export interface Cache {
   gitConflicts?: GitConflictsCache;
   prComments?: Record<number, Record<string, string>>;
 }
+
+export interface RepoCache {
+  repository: string;
+  revision: number;
+  data: RepoCacheData;
+}
diff --git a/lib/util/git/conflicts-cache.spec.ts b/lib/util/git/conflicts-cache.spec.ts
index 2ae180aece1ddf324e59f14465373f8643d6b2d7..b4eea0d7d1888b723023b76ba4f93b97e4b3cd76 100644
--- a/lib/util/git/conflicts-cache.spec.ts
+++ b/lib/util/git/conflicts-cache.spec.ts
@@ -1,6 +1,6 @@
 import { mocked } from '../../../test/util';
 import * as _repositoryCache from '../cache/repository';
-import type { Cache } from '../cache/repository/types';
+import type { RepoCacheData } from '../cache/repository/types';
 import {
   getCachedConflictResult,
   setCachedConflictResult,
@@ -10,7 +10,7 @@ jest.mock('../cache/repository');
 const repositoryCache = mocked(_repositoryCache);
 
 describe('util/git/conflicts-cache', () => {
-  let repoCache: Cache = {};
+  let repoCache: RepoCacheData = {};
 
   beforeEach(() => {
     repoCache = {};
diff --git a/lib/util/http/github.spec.ts b/lib/util/http/github.spec.ts
index b537d3acaa6a52b1575931a982db352e84cec908..5c62d867ac15026f84903564cae1ba5d02117cb4 100644
--- a/lib/util/http/github.spec.ts
+++ b/lib/util/http/github.spec.ts
@@ -10,7 +10,7 @@ import {
 } from '../../constants/error-messages';
 import { GithubReleasesDatasource } from '../../modules/datasource/github-releases';
 import * as _repositoryCache from '../cache/repository';
-import type { Cache } from '../cache/repository/types';
+import type { RepoCacheData } from '../cache/repository/types';
 import * as hostRules from '../host-rules';
 import { GithubHttp, setBaseUrl } from './github';
 
@@ -47,7 +47,7 @@ query(
 
 describe('util/http/github', () => {
   let githubApi: GithubHttp;
-  let repoCache: Cache = {};
+  let repoCache: RepoCacheData = {};
 
   beforeEach(() => {
     githubApi = new GithubHttp();