diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md
index 68b14902227d933af957f3a295528e19473c8e13..e461f769d2eb9c4b0c7b262aa8549859df51400c 100644
--- a/docs/usage/configuration-options.md
+++ b/docs/usage/configuration-options.md
@@ -2580,6 +2580,14 @@ If you wish to disable all updates outside of scheduled hours then configure thi
 By default, Renovate will attempt to update all detected dependencies, regardless of whether they are defined using pinned single versions (e.g. `1.2.3`) or constraints/ranges (e.g. (`^1.2.3`).
 You can set this option to `false` if you wish to disable updating for pinned (single version) dependencies specifically.
 
+## useBaseBranchConfig
+
+By default, Renovate will read config file from the default branch only and will ignore any config files in base branches.
+You can configure `useBaseBranchConfig=merge` to instruct Renovate to merge the config from each base branch over the top of the config in the default branch.
+
+The config file name in the base branch must be the same as in the default branch and cannot be `package.json`.
+This scenario may be useful for testing the config changes in base branches instantly.
+
 ## userStrings
 
 When a PR is closed, Renovate posts a comment to let users know that future updates will be ignored.
diff --git a/lib/config/options/index.ts b/lib/config/options/index.ts
index d74203e3901baf1d883327933e2883e238ec4d65..fb738ad170089c9075a37d1345792da27b7be951 100644
--- a/lib/config/options/index.ts
+++ b/lib/config/options/index.ts
@@ -694,6 +694,14 @@ const options: RenovateOptions[] = [
     stage: 'package',
     cli: false,
   },
+  {
+    name: 'useBaseBranchConfig',
+    description:
+      'Whether to read configuration from baseBranches instead of only the default branch',
+    type: 'string',
+    allowedValues: ['merge', 'none'],
+    default: 'none',
+  },
   {
     name: 'gitAuthor',
     description: 'Author to use for Git commits. Must conform to RFC5322.',
diff --git a/lib/config/types.ts b/lib/config/types.ts
index cd4e2e32aef47d5c1a2a3bebdde75939811f81d7..258b29e3d5a883b5b723e33ede9e6327f37d3518 100644
--- a/lib/config/types.ts
+++ b/lib/config/types.ts
@@ -158,6 +158,8 @@ export interface CustomManager {
   autoReplaceStringTemplate?: string;
 }
 
+export type UseBaseBranchConfigType = 'merge' | 'none';
+
 // TODO: Proper typings
 export interface RenovateConfig
   extends LegacyAdminConfig,
@@ -167,6 +169,7 @@ export interface RenovateConfig
     Record<string, unknown> {
   depName?: string;
   baseBranches?: string[];
+  useBaseBranchConfig?: UseBaseBranchConfigType;
   baseBranch?: string;
   defaultBranch?: string;
   branchList?: string[];
diff --git a/lib/workers/repository/process/index.spec.ts b/lib/workers/repository/process/index.spec.ts
index 5f25f20aab1e875cf6c3be8a93e531d762136680..e23b46b05d2db1f421d1e97c1f82e55b254f0509 100644
--- a/lib/workers/repository/process/index.spec.ts
+++ b/lib/workers/repository/process/index.spec.ts
@@ -1,4 +1,12 @@
-import { RenovateConfig, getConfig, git, mocked } from '../../../../test/util';
+import {
+  RenovateConfig,
+  getConfig,
+  git,
+  mocked,
+  platform,
+} from '../../../../test/util';
+import { CONFIG_VALIDATION } from '../../../constants/error-messages';
+import { getCache } from '../../../util/cache/repository';
 import * as _extractUpdate from './extract-update';
 import { extractDependencies, updateRepo } from '.';
 
@@ -34,5 +42,61 @@ describe('workers/repository/process/index', () => {
         packageFiles: undefined,
       });
     });
+
+    it('reads config from default branch if useBaseBranchConfig not specified', async () => {
+      git.branchExists.mockReturnValue(true);
+      platform.getJsonFile.mockResolvedValueOnce({});
+      config.baseBranches = ['master', 'dev'];
+      config.useBaseBranchConfig = 'none';
+      getCache().configFileName = 'renovate.json';
+      const res = await extractDependencies(config);
+      expect(res).toEqual({
+        branchList: [undefined, undefined],
+        branches: [undefined, undefined],
+        packageFiles: undefined,
+      });
+      expect(platform.getJsonFile).not.toHaveBeenCalledWith(
+        'renovate.json',
+        undefined,
+        'dev'
+      );
+    });
+
+    it('reads config from branches in baseBranches if useBaseBranchConfig specified', async () => {
+      git.branchExists.mockReturnValue(true);
+      platform.getJsonFile = jest.fn().mockResolvedValue({});
+      config.baseBranches = ['master', 'dev'];
+      config.useBaseBranchConfig = 'merge';
+      getCache().configFileName = 'renovate.json';
+      const res = await extractDependencies(config);
+      expect(res).toEqual({
+        branchList: [undefined, undefined],
+        branches: [undefined, undefined],
+        packageFiles: undefined,
+      });
+      expect(platform.getJsonFile).toHaveBeenCalledWith(
+        'renovate.json',
+        undefined,
+        'dev'
+      );
+    });
+
+    it('handles config name mismatch between baseBranches if useBaseBranchConfig specified', async () => {
+      git.branchExists.mockReturnValue(true);
+      platform.getJsonFile = jest
+        .fn()
+        .mockImplementation((fileName, repoName, branchName) => {
+          if (branchName === 'dev') {
+            throw new Error();
+          }
+          return {};
+        });
+      getCache().configFileName = 'renovate.json';
+      config.baseBranches = ['master', 'dev'];
+      config.useBaseBranchConfig = 'merge';
+      await expect(extractDependencies(config)).rejects.toThrow(
+        CONFIG_VALIDATION
+      );
+    });
   });
 });
diff --git a/lib/workers/repository/process/index.ts b/lib/workers/repository/process/index.ts
index 58a9e98541d2579eb17e21c6a01e7c97983e724c..b57c53b7858e55401156f720cb285d4bafcd9c57 100644
--- a/lib/workers/repository/process/index.ts
+++ b/lib/workers/repository/process/index.ts
@@ -1,7 +1,10 @@
 import { mergeChildConfig } from '../../../config';
 import type { RenovateConfig } from '../../../config/types';
+import { CONFIG_VALIDATION } from '../../../constants/error-messages';
 import { logger } from '../../../logger';
 import type { PackageFile } from '../../../manager/types';
+import { platform } from '../../../platform';
+import { getCache } from '../../../util/cache/repository';
 import { branchExists } from '../../../util/git';
 import { addSplit } from '../../../util/split';
 import type { BranchConfig } from '../../types';
@@ -9,16 +12,53 @@ import { readDashboardBody } from '../dependency-dashboard';
 import { ExtractResult, extract, lookup, update } from './extract-update';
 import type { WriteUpdateResult } from './write';
 
-function getBaseBranchConfig(
+async function getBaseBranchConfig(
   baseBranch: string,
   config: RenovateConfig
-): RenovateConfig {
+): Promise<RenovateConfig> {
   logger.debug(`baseBranch: ${baseBranch}`);
-  const baseBranchConfig = mergeChildConfig(config, { baseBranch });
-  if (config.baseBranches.length > 1) {
-    baseBranchConfig.branchPrefix += `${baseBranch}-`;
-    baseBranchConfig.hasBaseBranches = true;
+
+  let baseBranchConfig: RenovateConfig = config;
+
+  if (
+    config.useBaseBranchConfig === 'merge' &&
+    baseBranch !== config.defaultBranch
+  ) {
+    logger.debug(
+      { baseBranch },
+      `Merging config from base branch because useBaseBranchConfig=merge`
+    );
+
+    // Retrieve config file name autodetected for this repo
+    const cache = getCache();
+    const configFileName = cache.configFileName;
+
+    try {
+      baseBranchConfig = await platform.getJsonFile(
+        configFileName,
+        config.repository,
+        baseBranch
+      );
+    } catch (err) {
+      logger.error(
+        { configFileName, baseBranch },
+        `Error fetching config file from base branch - possible config name mismatch between branches?`
+      );
+
+      const error = new Error(CONFIG_VALIDATION);
+      error.validationSource = 'config';
+      error.validationError = 'Error fetching config file';
+      error.validationMessage = `Error fetching config file ${configFileName} from branch ${baseBranch}`;
+      throw error;
+    }
+
+    baseBranchConfig = mergeChildConfig(config, baseBranchConfig);
+    // baseBranches value should be based off the default branch
+    baseBranchConfig.baseBranches = config.baseBranches;
   }
+
+  baseBranchConfig = mergeChildConfig(baseBranchConfig, { baseBranch });
+
   return baseBranchConfig;
 }
 
@@ -36,7 +76,7 @@ export async function extractDependencies(
     const extracted: Record<string, Record<string, PackageFile[]>> = {};
     for (const baseBranch of config.baseBranches) {
       if (branchExists(baseBranch)) {
-        const baseBranchConfig = getBaseBranchConfig(baseBranch, config);
+        const baseBranchConfig = await getBaseBranchConfig(baseBranch, config);
         extracted[baseBranch] = await extract(baseBranchConfig);
       } else {
         logger.warn({ baseBranch }, 'Base branch does not exist - skipping');
@@ -45,7 +85,7 @@ export async function extractDependencies(
     addSplit('extract');
     for (const baseBranch of config.baseBranches) {
       if (branchExists(baseBranch)) {
-        const baseBranchConfig = getBaseBranchConfig(baseBranch, config);
+        const baseBranchConfig = await getBaseBranchConfig(baseBranch, config);
         const packageFiles = extracted[baseBranch];
         const baseBranchRes = await lookup(baseBranchConfig, packageFiles);
         res.branches = res.branches.concat(baseBranchRes?.branches);