diff --git a/lib/config/definitions.js b/lib/config/definitions.js
index 4d37ae2e8e5f5b0b58baf0861af79784a9cf7a05..982dfa05d5387923b9845092bafca17bf646cad4 100644
--- a/lib/config/definitions.js
+++ b/lib/config/definitions.js
@@ -235,14 +235,6 @@ const options = [
     type: 'boolean',
   },
   // Bot administration
-  {
-    name: 'gitFs',
-    description: 'Use git for FS operations instead of API. GitHub only.',
-    type: 'string',
-    allowedValues: ['https', 'http', 'ssh'],
-    admin: true,
-    default: null,
-  },
   {
     name: 'persistRepoData',
     description:
diff --git a/lib/config/migration.js b/lib/config/migration.js
index ff0006028be0725cccca312a4815deb0db35ed2f..223168fefa3b68e35f4ebb6104c610fb6820c10e 100644
--- a/lib/config/migration.js
+++ b/lib/config/migration.js
@@ -65,15 +65,6 @@ function migrateConfig(config, parentKey) {
           migratedConfig.postUpdateOptions.push('gomodTidy');
         }
         delete migratedConfig.gomodTidy;
-      } else if (key === 'gitFs') {
-        if (val === false) {
-          isMigrated = true;
-          migratedConfig.gitFs = null;
-        }
-        if (val === true) {
-          isMigrated = true;
-          migratedConfig.gitFs = 'https';
-        }
       } else if (parentKey === 'hostRules' && key === 'platform') {
         isMigrated = true;
         migratedConfig.hostType = val;
diff --git a/lib/manager/bundler/artifacts.js b/lib/manager/bundler/artifacts.js
index c9ced092fce031939893d248fcdebaaea2f40f42..ddc82d275f40e86c92971a751c1c4df0398ecbdd 100644
--- a/lib/manager/bundler/artifacts.js
+++ b/lib/manager/bundler/artifacts.js
@@ -40,15 +40,6 @@ async function getArtifacts(
     const localPackageFileName = upath.join(config.localDir, packageFileName);
     await fs.outputFile(localPackageFileName, newPackageFileContent);
     const localLockFileName = upath.join(config.localDir, lockFileName);
-    if (!config.gitFs) {
-      await fs.outputFile(localLockFileName, existingLockFileContent);
-      const fileList = await platform.getFileList();
-      const gemspecs = fileList.filter(file => file.endsWith('.gemspec'));
-      for (const gemspec of gemspecs) {
-        const content = await platform.getFile(gemspec);
-        await fs.outputFile(upath.join(config.localDir, gemspec), content);
-      }
-    }
     const env =
       global.trustLevel === 'high'
         ? process.env
@@ -137,19 +128,9 @@ async function getArtifacts(
       { seconds, type: 'Gemfile.lock', stdout, stderr },
       'Generated lockfile'
     );
-    // istanbul ignore if
-    if (config.gitFs) {
-      const status = await platform.getRepoStatus();
-      if (!status.modified.includes(lockFileName)) {
-        return null;
-      }
-    } else {
-      const newLockFileContent = await fs.readFile(localLockFileName, 'utf8');
-
-      if (newLockFileContent === existingLockFileContent) {
-        logger.debug('Gemfile.lock is unchanged');
-        return null;
-      }
+    const status = await platform.getRepoStatus();
+    if (!status.modified.includes(lockFileName)) {
+      return null;
     }
     logger.debug('Returning updated Gemfile.lock');
     return [
@@ -161,18 +142,6 @@ async function getArtifacts(
       },
     ];
   } catch (err) {
-    if (
-      err.stdout &&
-      err.stdout.includes('No such file or directory') &&
-      !config.gitFs
-    ) {
-      logger.warn(
-        { err },
-        'It is necessary to run Renovate in gitFs mode - contact your bot administrator'
-      );
-      global.repoCache.bundlerArtifactsError = 'bundler-fs';
-      throw new Error('bundler-fs');
-    }
     if (
       (err.stdout &&
         err.stdout.includes('Please supply credentials for this source')) ||
diff --git a/lib/manager/cargo/artifacts.js b/lib/manager/cargo/artifacts.js
index 828fdc53414a3b2875d517df3fcd1bdbb5c400a4..cfa4a9e66ee60a1ed87eaa75b91d0c481959b58d 100644
--- a/lib/manager/cargo/artifacts.js
+++ b/lib/manager/cargo/artifacts.js
@@ -18,12 +18,6 @@ async function getArtifacts(
     logger.debug('No updated cargo deps - returning null');
     return null;
   }
-  if (!config.gitFs) {
-    logger.warn(
-      'Renovate administrator should enable gitFs in order to support Cargo.lock updating'
-    );
-    return null;
-  }
   const lockFileName = 'Cargo.lock';
   const existingLockFileContent = await platform.getFile(lockFileName);
   if (!existingLockFileContent) {
diff --git a/lib/manager/composer/artifacts.js b/lib/manager/composer/artifacts.js
index 9546d81479d26d4e2828bbe7253199ba0ccb22fc..34ccb6f0677752ff6b3912edcea14a477a4e13c9 100644
--- a/lib/manager/composer/artifacts.js
+++ b/lib/manager/composer/artifacts.js
@@ -35,9 +35,6 @@ async function getArtifacts(
     const localPackageFileName = upath.join(config.localDir, packageFileName);
     await fs.outputFile(localPackageFileName, newPackageFileContent);
     const localLockFileName = upath.join(config.localDir, lockFileName);
-    if (!config.gitFs) {
-      await fs.outputFile(localLockFileName, existingLockFileContent);
-    }
     const authJson = {};
     let credentials = hostRules.find({
       hostType: 'github',
@@ -133,19 +130,9 @@ async function getArtifacts(
       { seconds, type: 'composer.lock', stdout, stderr },
       'Generated lockfile'
     );
-    // istanbul ignore if
-    if (config.gitFs) {
-      const status = await platform.getRepoStatus();
-      if (!status.modified.includes(lockFileName)) {
-        return null;
-      }
-    } else {
-      const newLockFileContent = await fs.readFile(localLockFileName, 'utf8');
-
-      if (newLockFileContent === existingLockFileContent) {
-        logger.debug('composer.lock is unchanged');
-        return null;
-      }
+    const status = await platform.getRepoStatus();
+    if (!status.modified.includes(lockFileName)) {
+      return null;
     }
     logger.debug('Returning updated composer.lock');
     return [
diff --git a/lib/manager/gomod/artifacts.js b/lib/manager/gomod/artifacts.js
index 3ff129b97917ab7708824bb79d626873b8002031..93c1655ae73cf4f22e71d91a7c8e47b9746661cb 100644
--- a/lib/manager/gomod/artifacts.js
+++ b/lib/manager/gomod/artifacts.js
@@ -38,9 +38,6 @@ async function getArtifacts(
     }
     await fs.outputFile(localGoModFileName, massagedGoMod);
     const localGoSumFileName = upath.join(config.localDir, sumFileName);
-    if (!config.gitFs) {
-      await fs.outputFile(localGoSumFileName, existingGoSumContent);
-    }
     const env =
       global.trustLevel === 'high'
         ? process.env
@@ -99,43 +96,27 @@ async function getArtifacts(
       config.postUpdateOptions &&
       config.postUpdateOptions.includes('gomodTidy')
     ) {
-      // istanbul ignore else
-      if (config.gitFs) {
-        args = 'mod tidy';
-        if (cmd.includes('.insteadOf')) {
-          args += '"';
-        }
-        logger.debug({ cmd, args }, 'go mod tidy command');
-        ({ stdout, stderr } = await exec(`${cmd} ${args}`, {
-          cwd,
-          shell: true,
-          env,
-        }));
-        duration = process.hrtime(startTime);
-        seconds = Math.round(duration[0] + duration[1] / 1e9);
-        logger.info(
-          { seconds, stdout, stderr },
-          'Tidied Go Modules after update'
-        );
-      } else {
-        logger.warn(
-          'Renovate administrator should enable gitFs in order to support tidying go modules'
-        );
+      args = 'mod tidy';
+      if (cmd.includes('.insteadOf')) {
+        args += '"';
       }
+      logger.debug({ cmd, args }, 'go mod tidy command');
+      ({ stdout, stderr } = await exec(`${cmd} ${args}`, {
+        cwd,
+        shell: true,
+        env,
+      }));
+      duration = process.hrtime(startTime);
+      seconds = Math.round(duration[0] + duration[1] / 1e9);
+      logger.info(
+        { seconds, stdout, stderr },
+        'Tidied Go Modules after update'
+      );
     }
     const res = [];
-    if (config.gitFs) {
-      const status = await platform.getRepoStatus();
-      if (!status.modified.includes(sumFileName)) {
-        return null;
-      }
-    } else {
-      const newGoSumContent = await fs.readFile(localGoSumFileName, 'utf8');
-
-      if (newGoSumContent === existingGoSumContent) {
-        logger.debug('go.sum is unchanged');
-        return null;
-      }
+    let status = await platform.getRepoStatus();
+    if (!status.modified.includes(sumFileName)) {
+      return null;
     }
     logger.debug('Returning updated go.sum');
     res.push({
@@ -148,12 +129,28 @@ async function getArtifacts(
     const vendorModulesFileName = upath.join(vendorDir, 'modules.txt');
     // istanbul ignore if
     if (await platform.getFile(vendorModulesFileName)) {
-      if (config.gitFs) {
-        args = 'mod vendor';
+      args = 'mod vendor';
+      if (cmd.includes('.insteadOf')) {
+        args += '"';
+      }
+      logger.debug({ cmd, args }, 'go mod vendor command');
+      ({ stdout, stderr } = await exec(`${cmd} ${args}`, {
+        cwd,
+        shell: true,
+        env,
+      }));
+      duration = process.hrtime(startTime);
+      seconds = Math.round(duration[0] + duration[1] / 1e9);
+      logger.info({ seconds, stdout, stderr }, 'Vendored modules');
+      if (
+        config.postUpdateOptions &&
+        config.postUpdateOptions.includes('gomodTidy')
+      ) {
+        args = 'mod tidy';
         if (cmd.includes('.insteadOf')) {
           args += '"';
         }
-        logger.debug({ cmd, args }, 'go mod vendor command');
+        logger.debug({ cmd, args }, 'go mod tidy command');
         ({ stdout, stderr } = await exec(`${cmd} ${args}`, {
           cwd,
           shell: true,
@@ -161,52 +158,30 @@ async function getArtifacts(
         }));
         duration = process.hrtime(startTime);
         seconds = Math.round(duration[0] + duration[1] / 1e9);
-        logger.info({ seconds, stdout, stderr }, 'Vendored modules');
-        if (
-          config.postUpdateOptions &&
-          config.postUpdateOptions.includes('gomodTidy')
-        ) {
-          args = 'mod tidy';
-          if (cmd.includes('.insteadOf')) {
-            args += '"';
-          }
-          logger.debug({ cmd, args }, 'go mod tidy command');
-          ({ stdout, stderr } = await exec(`${cmd} ${args}`, {
-            cwd,
-            shell: true,
-            env,
-          }));
-          duration = process.hrtime(startTime);
-          seconds = Math.round(duration[0] + duration[1] / 1e9);
-          logger.info(
-            { seconds, stdout, stderr },
-            'Tidied Go Modules after vendoring'
-          );
-        }
-        const status = await platform.getRepoStatus();
-        for (const f of status.modified.concat(status.not_added)) {
-          if (f.startsWith(vendorDir)) {
-            const localModified = upath.join(config.localDir, f);
-            res.push({
-              file: {
-                name: f,
-                contents: await fs.readFile(localModified, 'utf8'),
-              },
-            });
-          }
-        }
-        for (const f of status.deleted || []) {
+        logger.info(
+          { seconds, stdout, stderr },
+          'Tidied Go Modules after vendoring'
+        );
+      }
+      status = await platform.getRepoStatus();
+      for (const f of status.modified.concat(status.not_added)) {
+        if (f.startsWith(vendorDir)) {
+          const localModified = upath.join(config.localDir, f);
           res.push({
             file: {
-              name: '|delete|',
-              contents: f,
+              name: f,
+              contents: await fs.readFile(localModified, 'utf8'),
             },
           });
         }
-      } else {
-        logger.warn(
-          'Vendor modules found - Renovate administrator should enable gitFs in order to update them'
-        );
+      }
+      for (const f of status.deleted || []) {
+        res.push({
+          file: {
+            name: '|delete|',
+            contents: f,
+          },
+        });
       }
     }
     const finalGoModContent = (await fs.readFile(
diff --git a/lib/manager/gradle/index.js b/lib/manager/gradle/index.js
index a0de061e969fdd830591df1f36d924e69a0e1ae8..08d3ba5b943302853506980741f7e89ce37c897e 100644
--- a/lib/manager/gradle/index.js
+++ b/lib/manager/gradle/index.js
@@ -1,6 +1,5 @@
 const { exec } = require('child-process-promise');
 const fs = require('fs-extra');
-const path = require('path');
 
 const gradle = require('./build-gradle');
 const updatesReport = require('./gradle-updates-report');
@@ -15,16 +14,6 @@ async function extractAllPackageFiles(config, packageFiles) {
     return null;
   }
   logger.info('Extracting dependencies from all gradle files');
-  // Gradle needs all files to be written to disk before we parse any as some files may reference others
-  // But if we're using gitFs then it's not necessary because they're already there
-  if (!config.gitFs) {
-    const files = [...packageFiles, 'gradlew'];
-    for (const file of files) {
-      const localFileName = path.join(config.localDir, file);
-      const content = await platform.getFile(file);
-      await fs.outputFile(localFileName, content);
-    }
-  }
 
   await updatesReport.createRenovateGradlePlugin(config.localDir);
   await executeGradle(config);
@@ -112,16 +101,11 @@ async function executeGradle(config) {
 async function getGradleCommandLine(config) {
   let cmd;
   const gradlewExists = await fs.exists(config.localDir + '/gradlew');
-  if (gradlewExists && !config.gitFs) {
-    logger.warn(
-      'Found gradle wrapper (gradlew) in the project, but --git-fs is not enabled, not using it.'
-    );
-  }
   if (config.binarySource === 'docker') {
     cmd = `docker run --rm -v ${config.localDir}:${config.localDir} -w ${
       config.localDir
     } renovate/gradle gradle`;
-  } else if (config.gitFs && gradlewExists) {
+  } else if (gradlewExists) {
     cmd = 'sh gradlew';
   } else {
     cmd = 'gradle';
diff --git a/lib/manager/pip_setup/extract.js b/lib/manager/pip_setup/extract.js
index 1a9d8cee8fe77fd08454a06aa275f60f365eb060..74b048dd4a719591c5cdb545487e384759e82693 100644
--- a/lib/manager/pip_setup/extract.js
+++ b/lib/manager/pip_setup/extract.js
@@ -1,5 +1,4 @@
 const { exec } = require('child-process-promise');
-const fs = require('fs-extra');
 const { join } = require('upath');
 const { isSkipComment } = require('../../util/ignore');
 const { dependencyPattern } = require('../pip_requirements/extract');
@@ -40,11 +39,6 @@ async function getPythonAlias() {
 
 async function extractSetupFile(content, packageFile, config) {
   const cwd = config.localDir;
-  // extract.py needs setup.py to be written to disk
-  if (!config.gitFs) {
-    const localFileName = join(config.localDir, packageFile);
-    await fs.outputFile(localFileName, content);
-  }
   let cmd;
   const args = [join(__dirname, 'extract.py'), packageFile];
   // istanbul ignore if
@@ -72,34 +66,22 @@ async function extractSetupFile(content, packageFile, config) {
     cmd = await getPythonAlias();
   }
   logger.debug({ cmd, args }, 'python command');
-  let stdout;
-  let stderr;
-  try {
-    ({ stdout, stderr } = await exec(`${cmd} ${args.join(' ')}`, {
-      cwd,
-      shell: true,
-      timeout: 5000,
-    }));
-  } catch (err) {
-    if (
-      err.message &&
-      err.message.includes('No such file or directory') &&
-      !config.gitFs
-    ) {
-      logger.warn(
-        'File not found error when extracting setup.py. Ask your Renovate administrator to enable gitFs and try again'
-      );
-    }
-    throw err;
-  }
-  if (stderr) {
-    stderr = stderr.replace(/.*\n\s*import imp/, '').trim();
+  const res = await exec(`${cmd} ${args.join(' ')}`, {
+    cwd,
+    shell: true,
+    timeout: 5000,
+  });
+  if (res.stderr) {
+    const stderr = res.stderr.replace(/.*\n\s*import imp/, '').trim();
     // istanbul ignore if
     if (stderr.length) {
-      logger.warn({ stdout, stderr }, 'Error in read setup file');
+      logger.warn(
+        { stdout: res.stdout, stderr: res.stderr },
+        'Error in read setup file'
+      );
     }
   }
-  return JSON.parse(stdout);
+  return JSON.parse(res.stdout);
 }
 
 async function extractPackageFile(content, packageFile, config) {
@@ -155,6 +137,7 @@ async function extractPackageFile(content, packageFile, config) {
         ? (a.depName > b.depName) - (a.depName < b.depName)
         : a.lineNumber - b.lineNumber
     );
+  // istanbul ignore if
   if (!deps.length) {
     return null;
   }
diff --git a/lib/manager/pipenv/artifacts.js b/lib/manager/pipenv/artifacts.js
index 278b51719dd4362a1945527a399d0bd015496251..ba9a45d05c82008806008cb7e9838e11ca304b70 100644
--- a/lib/manager/pipenv/artifacts.js
+++ b/lib/manager/pipenv/artifacts.js
@@ -70,19 +70,9 @@ async function getArtifacts(
       { seconds, type: 'Pipfile.lock', stdout, stderr },
       'Generated lockfile'
     );
-    // istanbul ignore if
-    if (config.gitFs) {
-      const status = await platform.getRepoStatus();
-      if (!(status && status.modified.includes(lockFileName))) {
-        return null;
-      }
-    } else {
-      const newLockFileContent = await fs.readFile(localLockFileName, 'utf8');
-
-      if (newLockFileContent === existingLockFileContent) {
-        logger.debug('Pipfile.lock is unchanged');
-        return null;
-      }
+    const status = await platform.getRepoStatus();
+    if (!(status && status.modified.includes(lockFileName))) {
+      return null;
     }
     logger.debug('Returning updated Pipfile.lock');
     return [
diff --git a/lib/platform/azure/index.js b/lib/platform/azure/index.js
index 4de7cab5dca5d425dbc90eed1f0da4b56f145360..f69724e21dc30fa978eeed8217f27dc08061f4a9 100644
--- a/lib/platform/azure/index.js
+++ b/lib/platform/azure/index.js
@@ -109,41 +109,8 @@ async function initRepo({ repository, localDir, azureWorkItemId }) {
   config.baseBranch = config.defaultBranch;
   logger.debug(`${repository} default branch = ${config.defaultBranch}`);
   config.baseCommitSHA = await getBranchCommit(config.baseBranch);
-
-  // Todo Azure: Get Merge method
   config.mergeMethod = 'merge';
-  // if (res.body.allow_rebase_merge) {
-  //   config.mergeMethod = 'rebase';
-  // } else if (res.body.allow_squash_merge) {
-  //   config.mergeMethod = 'squash';
-  // } else if (res.body.allow_merge_commit) {
-  //   config.mergeMethod = 'merge';
-  // } else {
-  //   logger.debug('Could not find allowed merge methods for repo');
-  // }
-
-  // Todo Azure: Get getBranchProtection
   config.repoForceRebase = false;
-  // try {
-  //   const branchProtection = await getBranchProtection(config.baseBranch);
-  //   if (branchProtection.strict) {
-  //     logger.debug('Repo has branch protection and needs PRs up-to-date');
-  //     config.repoForceRebase = true;
-  //   } else {
-  //     logger.debug(
-  //       'Repo has branch protection but does not require up-to-date'
-  //     );
-  //   }
-  // } catch (err) {
-  //   if (err.statusCode === 404) {
-  //     logger.debug('Repo has no branch protection');
-  //   } else if (err.statusCode === 403) {
-  //     logger.debug('Do not have permissions to detect branch protection');
-  //   } else {
-  //     throw err;
-  //   }
-  // }
-  // Always gitFs
   config.storage = new GitStorage();
   const [projectName, repoName] = repository.split('/');
   const opts = hostRules.find({
diff --git a/lib/platform/bitbucket-server/index.ts b/lib/platform/bitbucket-server/index.ts
index a39d1d1606da66e1a4424ceb48a75cfe8979b7bf..13541cc41094d70a492253ce7078179c4d217e13 100644
--- a/lib/platform/bitbucket-server/index.ts
+++ b/lib/platform/bitbucket-server/index.ts
@@ -83,29 +83,21 @@ export function cleanRepo() {
 // Initialize GitLab by getting base branch
 export async function initRepo({
   repository,
-  endpoint,
   gitPrivateKey,
-  gitFs,
   localDir,
   bbUseDefaultReviewers,
 }: {
   repository: string;
-  endpoint: string;
   gitPrivateKey?: string;
-  gitFs?: string;
   localDir: string;
   bbUseDefaultReviewers?: boolean;
 }) {
   logger.debug(
-    `initRepo("${JSON.stringify(
-      { repository, endpoint, gitFs, localDir },
-      null,
-      2
-    )}")`
+    `initRepo("${JSON.stringify({ repository, localDir }, null, 2)}")`
   );
   const opts = hostRules.find({
     hostType: defaults.hostType,
-    url: endpoint,
+    url: defaults.endpoint,
   });
   api.reset();
 
@@ -118,10 +110,9 @@ export async function initRepo({
     config.bbUseDefaultReviewers = true;
   }
 
-  // Always gitFs
-  const { host, pathname } = url.parse(endpoint!);
+  const { host, pathname } = url.parse(defaults.endpoint!);
   const gitUrl = GitStorage.getUrl({
-    gitFs: endpoint.split(':')[0] as any,
+    protocol: defaults.endpoint!.split(':')[0] as any,
     auth: `${opts!.username}:${opts!.password}`,
     host: `${host}${pathname}${
       pathname!.endsWith('/') ? '' : /* istanbul ignore next */ '/'
diff --git a/lib/platform/bitbucket/index.ts b/lib/platform/bitbucket/index.ts
index 984860c56133c7d9d804ce2ad7ec53d982b6c81e..67a0374032027ffd831b8664bbee5cc027010e4d 100644
--- a/lib/platform/bitbucket/index.ts
+++ b/lib/platform/bitbucket/index.ts
@@ -80,9 +80,8 @@ export async function initRepo({
   config.repository = repository;
   const platformConfig: any = {};
 
-  // Always gitFs
   const url = GitStorage.getUrl({
-    gitFs: 'https',
+    protocol: 'https',
     auth: `${opts!.username}:${opts!.password}`,
     hostname: 'bitbucket.org',
     repository,
diff --git a/lib/platform/git/storage.ts b/lib/platform/git/storage.ts
index 02baa82d259638f5ca7d1539a024acec3f4e11a9..272928b7e20415d9232f722821937e1caf9b51f4 100644
--- a/lib/platform/git/storage.ts
+++ b/lib/platform/git/storage.ts
@@ -307,13 +307,7 @@ class Storage {
       logger.debug({ branchName }, 'Deleted remote branch');
     } catch (err) /* istanbul ignore next */ {
       logger.info({ branchName, err }, 'Error deleting remote branch');
-      if (err.message.includes('.github/main.workflow')) {
-        logger.warn(
-          'A GitHub bug prevents gitFs + GitHub Actions. Please disable gitFs'
-        );
-      } else {
-        throw new Error('repository-changed');
-      }
+      throw new Error('repository-changed');
     }
     try {
       await this._deleteLocalBranch(branchName);
@@ -414,15 +408,7 @@ class Storage {
       this._config.branchExists[branchName] = true;
     } catch (err) /* istanbul ignore next */ {
       logger.debug({ err }, 'Error commiting files');
-      if (err.message.includes('.github/main.workflow')) {
-        logger.warn(
-          'A GitHub bug prevents gitFs + GitHub Actions. Please disable gitFs'
-        );
-        throw new Error('disable-gitfs');
-      } else if (err.message.includes('[remote rejected]')) {
-        throw new Error('repository-changed');
-      }
-      throw err;
+      throw new Error('repository-changed');
     }
   }
 
@@ -430,28 +416,23 @@ class Storage {
   cleanRepo() {}
 
   static getUrl({
-    gitFs,
+    protocol,
     auth,
     hostname,
     host,
     repository,
   }: {
-    gitFs?: 'ssh' | 'http' | 'https';
+    protocol?: 'ssh' | 'http' | 'https';
     auth?: string;
     hostname?: string;
     host?: string;
     repository: string;
   }) {
-    let protocol = gitFs || 'https';
-    // istanbul ignore if
-    if (protocol.toString() === 'true') {
-      protocol = 'https';
-    }
     if (protocol === 'ssh') {
       return `git@${hostname}:${repository}.git`;
     }
     return URL.format({
-      protocol,
+      protocol: protocol || 'https',
       auth,
       hostname,
       host,
diff --git a/lib/platform/github/index.js b/lib/platform/github/index.js
index 836edbd7630d9605d5c2e391eb81836a1b235028..0985de6ef53e787fb2d846b0166a802d259da559 100644
--- a/lib/platform/github/index.js
+++ b/lib/platform/github/index.js
@@ -305,7 +305,6 @@ async function initRepo({
     }
   }
 
-  // gitFs
   const parsedEndpoint = URL.parse(defaults.endpoint);
   parsedEndpoint.auth =
     config.forkToken || global.appMode
diff --git a/lib/platform/gitlab/index.js b/lib/platform/gitlab/index.js
index 510e6fe7449ddedeb314000bc54d23bf43ded85c..3161bbeacc354d0531e993e34809595db824e1e1 100644
--- a/lib/platform/gitlab/index.js
+++ b/lib/platform/gitlab/index.js
@@ -108,11 +108,10 @@ function cleanRepo() {
 }
 
 // Initialize GitLab by getting base branch
-async function initRepo({ repository, gitFs, localDir }) {
+async function initRepo({ repository, localDir }) {
   config = {};
   get.reset();
   config.repository = urlEscape(repository);
-  config.gitFs = gitFs;
   config.localDir = localDir;
   let res;
   const platformConfig = {};
@@ -135,7 +134,6 @@ async function initRepo({ repository, gitFs, localDir }) {
     config.email = (await get(`user`)).body.email;
     logger.debug('Bot email=' + config.email);
     delete config.prList;
-    // Always gitFs
     logger.debug('Enabling Git FS');
     const { host, protocol } = URL.parse(defaults.endpoint);
     const opts = hostRules.find({
@@ -143,7 +141,7 @@ async function initRepo({ repository, gitFs, localDir }) {
       url: defaults.endpoint,
     });
     const url = GitStorage.getUrl({
-      gitFs: protocol.slice(0, -1),
+      protocol: protocol.slice(0, -1),
       auth: 'oauth2:' + opts.token,
       host,
       repository,
diff --git a/renovate-schema.json b/renovate-schema.json
index 0efd257e2f1b35132dfb0d5324b3f92c0cd4e42d..dd3c27075f972ce70fc11e0477f50cdeb744d7e8 100644
--- a/renovate-schema.json
+++ b/renovate-schema.json
@@ -147,12 +147,6 @@
       "description": "Whether to update (but not create) branches when not scheduled",
       "type": "boolean"
     },
-    "gitFs": {
-      "description": "Use git for FS operations instead of API. GitHub only.",
-      "type": "string",
-      "enum": ["https", "http", "ssh"],
-      "default": null
-    },
     "persistRepoData": {
       "description": "If set to false, repository data will preserved between runs instead of deleted.",
       "type": "boolean",
diff --git a/test/config/__snapshots__/migration.spec.js.snap b/test/config/__snapshots__/migration.spec.js.snap
index e1cca575cd0f21a5c9b9b8db30efc22ee407dedd..b0134cbafd814a0d7c7db256bf713526b8250542 100644
--- a/test/config/__snapshots__/migration.spec.js.snap
+++ b/test/config/__snapshots__/migration.spec.js.snap
@@ -21,7 +21,7 @@ Object {
     "config:js-app",
     "config:js-lib",
   ],
-  "gitFs": null,
+  "gitFs": false,
   "hostRules": Array [
     Object {},
   ],
@@ -31,7 +31,7 @@ Object {
   "includeForks": true,
   "lockFileMaintenance": Object {
     "automerge": true,
-    "gitFs": "https",
+    "gitFs": true,
     "schedule": "before 5am",
     "trustLevel": "low",
   },
diff --git a/test/manager/cargo/artifacts.spec.js b/test/manager/cargo/artifacts.spec.js
index 12b225cacdb7832a1cb513c74573db2f48578837..14ef245cd64ecdce447bc477f7c11fcaf213fc70 100644
--- a/test/manager/cargo/artifacts.spec.js
+++ b/test/manager/cargo/artifacts.spec.js
@@ -7,7 +7,6 @@ const cargo = require('../../../lib/manager/cargo/artifacts');
 
 const config = {
   localDir: '/tmp/github/some/repo',
-  gitFs: true,
 };
 
 describe('.getArtifacts()', () => {
@@ -48,27 +47,6 @@ describe('.getArtifacts()', () => {
       await cargo.getArtifacts('Cargo.toml', updatedDeps, '', config)
     ).toBeNull();
   });
-  it('returns null if gitFs is not enabled', async () => {
-    platform.getFile.mockReturnValueOnce('Current Cargo.lock');
-    exec.mockReturnValueOnce({
-      stdout: '',
-      stderror: '',
-    });
-    fs.readFile = jest.fn(() => 'New Cargo.lock');
-    const noGitFsConfig = {
-      localDir: '/tmp/github/some/repo',
-      gitFs: undefined,
-    };
-    const updatedDeps = [
-      {
-        depName: 'dep1',
-        currentValue: '1.2.3',
-      },
-    ];
-    expect(
-      await cargo.getArtifacts('Cargo.toml', updatedDeps, '', noGitFsConfig)
-    ).toBeNull();
-  });
   it('returns updated Cargo.lock', async () => {
     platform.getFile.mockReturnValueOnce('Old Cargo.lock');
     exec.mockReturnValueOnce({
diff --git a/test/manager/composer/artifacts.spec.js b/test/manager/composer/artifacts.spec.js
index 9d9d3807004de358d93f3dd9843bda25469e673c..19e73d5562581a953b4ba1835faa599ccdc240ce 100644
--- a/test/manager/composer/artifacts.spec.js
+++ b/test/manager/composer/artifacts.spec.js
@@ -31,6 +31,7 @@ describe('.getArtifacts()', () => {
       stderror: '',
     });
     fs.readFile = jest.fn(() => 'Current composer.lock');
+    platform.getRepoStatus.mockResolvedValue({ modified: [] });
     expect(
       await composer.getArtifacts('composer.json', [], '{}', config)
     ).toBeNull();
@@ -54,6 +55,7 @@ describe('.getArtifacts()', () => {
       username: 'some-username',
       password: 'some-password',
     });
+    platform.getRepoStatus.mockResolvedValue({ modified: [] });
     expect(
       await composer.getArtifacts('composer.json', [], '{}', authConfig)
     ).toBeNull();
@@ -66,6 +68,7 @@ describe('.getArtifacts()', () => {
     });
     fs.readFile = jest.fn(() => 'New composer.lock');
     global.trustLevel = 'high';
+    platform.getRepoStatus.mockResolvedValue({ modified: ['composer.lock'] });
     expect(
       await composer.getArtifacts('composer.json', [], '{}', config)
     ).not.toBeNull();
diff --git a/test/manager/gomod/artifacts.spec.js b/test/manager/gomod/artifacts.spec.js
index 53703e6fa17868fc5309791576fad85d1ad06502..ed72d2ea6fec27d385b9dd8f592ce258e8848522 100644
--- a/test/manager/gomod/artifacts.spec.js
+++ b/test/manager/gomod/artifacts.spec.js
@@ -41,7 +41,7 @@ describe('.getArtifacts()', () => {
       stdout: '',
       stderror: '',
     });
-    fs.readFile = jest.fn(() => 'Current go.sum');
+    platform.getRepoStatus.mockResolvedValue({ modified: [] });
     expect(await gomod.getArtifacts('go.mod', [], gomod1, config)).toBeNull();
   });
   it('returns updated go.sum', async () => {
@@ -50,6 +50,7 @@ describe('.getArtifacts()', () => {
       stdout: '',
       stderror: '',
     });
+    platform.getRepoStatus.mockResolvedValue({ modified: ['go.sum'] });
     fs.readFile = jest.fn(() => 'New go.sum');
     expect(
       await gomod.getArtifacts('go.mod', [], gomod1, config)
@@ -61,6 +62,7 @@ describe('.getArtifacts()', () => {
       stdout: '',
       stderror: '',
     });
+    platform.getRepoStatus.mockResolvedValue({ modified: ['go.sum'] });
     fs.readFile = jest.fn(() => 'New go.sum');
     expect(
       await gomod.getArtifacts('go.mod', [], gomod1, {
@@ -78,6 +80,7 @@ describe('.getArtifacts()', () => {
       stdout: '',
       stderror: '',
     });
+    platform.getRepoStatus.mockResolvedValue({ modified: ['go.sum'] });
     fs.readFile = jest.fn(() => 'New go.sum');
     expect(
       await gomod.getArtifacts('go.mod', [], gomod1, {
@@ -91,8 +94,10 @@ describe('.getArtifacts()', () => {
       token: 'some-token',
     });
     platform.getFile.mockResolvedValueOnce('Current go.sum');
-    platform.getRepoStatus.mockResolvedValue({ modified: '' });
-    fs.readFile.mockResolvedValueOnce('New go.sum');
+    platform.getRepoStatus.mockResolvedValue({ modified: ['go.sum'] });
+    fs.readFile.mockResolvedValue('New go.sum 1');
+    fs.readFile.mockResolvedValue('New go.sum 2');
+    fs.readFile.mockResolvedValue('New go.sum 3');
     try {
       global.appMode = true;
       global.trustLevel = 'high';
@@ -101,9 +106,8 @@ describe('.getArtifacts()', () => {
           ...config,
           binarySource: 'docker',
           postUpdateOptions: ['gomodTidy'],
-          gitFs: 'https',
         })
-      ).toBeNull();
+      ).not.toBeNull();
     } finally {
       delete global.appMode;
       delete global.trustLevel;
diff --git a/test/manager/gradle/index.spec.js b/test/manager/gradle/index.spec.js
index dee3f6d94c04dc89b56204a4d61a7a02a6f19d67..51af463737f3b335c47ae9a7a6c7b0aeff0c07de 100644
--- a/test/manager/gradle/index.spec.js
+++ b/test/manager/gradle/index.spec.js
@@ -99,25 +99,8 @@ describe('manager/gradle', () => {
       expect(dependencies).toMatchSnapshot();
     });
 
-    it('should execute gradle with the proper parameters', async () => {
-      await manager.extractAllPackageFiles(config, ['build.gradle']);
-
-      expect(exec.mock.calls[0][0]).toBe(
-        'gradle --init-script renovate-plugin.gradle renovate'
-      );
-      expect(exec.mock.calls[0][1]).toMatchObject({
-        cwd: 'localDir',
-        timeout: 20000,
-      });
-    });
-
     it('should execute gradlew when available', async () => {
-      const configWithgitFs = {
-        gitFs: true,
-        ...config,
-      };
-
-      await manager.extractAllPackageFiles(configWithgitFs, ['build.gradle']);
+      await manager.extractAllPackageFiles(config, ['build.gradle']);
 
       expect(exec.mock.calls[0][0]).toBe(
         'sh gradlew --init-script renovate-plugin.gradle renovate'
@@ -137,30 +120,6 @@ describe('manager/gradle', () => {
       expect(exec).toHaveBeenCalledTimes(0);
     });
 
-    it('should write files before extracting', async () => {
-      const packageFiles = ['build.gradle', 'foo/build.gradle'];
-      await manager.extractAllPackageFiles(config, packageFiles);
-
-      expect(toUnix(fs.outputFile.mock.calls[0][0])).toBe(
-        'localDir/build.gradle'
-      );
-      expect(toUnix(fs.outputFile.mock.calls[1][0])).toBe(
-        'localDir/foo/build.gradle'
-      );
-    });
-
-    it('should not write files if gitFs is enabled', async () => {
-      const configWithgitFs = {
-        gitFs: true,
-        ...config,
-      };
-
-      const packageFiles = ['build.gradle', 'foo/build.gradle'];
-      await manager.extractAllPackageFiles(configWithgitFs, packageFiles);
-
-      expect(fs.outputFile).toHaveBeenCalledTimes(0);
-    });
-
     it('should configure the renovate report plugin', async () => {
       await manager.extractAllPackageFiles(config, ['build.gradle']);
 
@@ -184,7 +143,6 @@ describe('manager/gradle', () => {
     it('should use dcoker even if gradlew is available', async () => {
       const configWithDocker = {
         binarySource: 'docker',
-        gitFs: true,
         ...config,
         gradle: {},
       };
diff --git a/test/manager/pipenv/artifacts.spec.js b/test/manager/pipenv/artifacts.spec.js
index a1b19c20ee519ded21570a4da8df5e1332d7d772..e9bfc68f99681f889b74fb8c85bfd335091c8dcb 100644
--- a/test/manager/pipenv/artifacts.spec.js
+++ b/test/manager/pipenv/artifacts.spec.js
@@ -36,6 +36,7 @@ describe('.getArtifacts()', () => {
       stdout: '',
       stderror: '',
     });
+    platform.getRepoStatus.mockResolvedValue({ modified: ['Pipfile.lock'] });
     fs.readFile = jest.fn(() => 'New Pipfile.lock');
     global.trustLevel = 'high';
     expect(
@@ -48,6 +49,7 @@ describe('.getArtifacts()', () => {
       stdout: '',
       stderror: '',
     });
+    platform.getRepoStatus.mockResolvedValue({ modified: ['Pipfile.lock'] });
     fs.readFile = jest.fn(() => 'New Pipfile.lock');
     expect(
       await pipenv.getArtifacts('Pipfile', [], '{}', {
diff --git a/test/platform/git/storage.spec.ts b/test/platform/git/storage.spec.ts
index 975c935c9b9a0b513216ffefb612c466325d66eb..d9602ee0fcfa6528c552e9d34a89fdc08cd75044 100644
--- a/test/platform/git/storage.spec.ts
+++ b/test/platform/git/storage.spec.ts
@@ -235,7 +235,7 @@ describe('platform/git/storage', () => {
     it('returns https url', () => {
       expect(
         getUrl({
-          gitFs: 'https',
+          protocol: 'https',
           auth: 'user:pass',
           hostname: 'host',
           repository: 'some/repo',
@@ -253,7 +253,7 @@ describe('platform/git/storage', () => {
     it('returns ssh url', () => {
       expect(
         getUrl({
-          gitFs: 'ssh',
+          protocol: 'ssh',
           auth: 'user:pass',
           hostname: 'host',
           repository: 'some/repo',
diff --git a/test/platform/github/index.spec.js b/test/platform/github/index.spec.js
index 00f7c8323cf0f2ec7e8b8e99b8a43c2ec869ac48..e8ddb0eca598032ebdb72ac93efb3ca330c05858 100644
--- a/test/platform/github/index.spec.js
+++ b/test/platform/github/index.spec.js
@@ -350,7 +350,6 @@ describe('platform/github', () => {
       });
       await expect(
         github.initRepo({
-          gitFs: 'https',
           includeForks: true,
           repository: 'some/repo',
         })
diff --git a/test/platform/gitlab/index.spec.js b/test/platform/gitlab/index.spec.js
index f5de3f7f4dc1d7f930fe5761379b32b601acbe18..e6a4e517a5931ddbdffd82f98fab5e2181d3e074 100644
--- a/test/platform/gitlab/index.spec.js
+++ b/test/platform/gitlab/index.spec.js
@@ -759,7 +759,7 @@ These updates have all been created already. Click a checkbox below to force a r
     });
   });
   describe('getCommitMessages()', () => {
-    it('passes to gitFs', async () => {
+    it('passes to git', async () => {
       await initRepo();
       await gitlab.getCommitMessages();
     });
diff --git a/test/static-files.spec.js b/test/static-files.spec.js
index 09c97aa659d7b7631f708075a25bcd5a67799b91..0717e58ff9682b4e9a8e2901e2a9289e087a8faa 100644
--- a/test/static-files.spec.js
+++ b/test/static-files.spec.js
@@ -1,7 +1,7 @@
 const util = require('util');
 const glob = util.promisify(require('glob'));
 
-const ignoredExtensions = ['js', 'ts', 'md'];
+const ignoredExtensions = ['js', 'ts', 'md', 'pyc'];
 
 function filterFiles(files) {
   return files.filter(file =>
diff --git a/test/workers/repository/updates/__snapshots__/flatten.spec.js.snap b/test/workers/repository/updates/__snapshots__/flatten.spec.js.snap
index 3460ecfcc19d4e80cd034cd5c9a0d905964e24cf..5a1887eb57057c01e7f5d6ec2e409bf6b7786c6a 100644
--- a/test/workers/repository/updates/__snapshots__/flatten.spec.js.snap
+++ b/test/workers/repository/updates/__snapshots__/flatten.spec.js.snap
@@ -31,7 +31,6 @@ Array [
     "errors": Array [],
     "excludeCommitPaths": Array [],
     "gitAuthor": null,
-    "gitFs": null,
     "gitPrivateKey": null,
     "group": Object {
       "branchTopic": "{{{groupSlug}}}",
@@ -142,7 +141,6 @@ Array [
     "errors": Array [],
     "excludeCommitPaths": Array [],
     "gitAuthor": null,
-    "gitFs": null,
     "gitPrivateKey": null,
     "group": Object {
       "branchTopic": "{{{groupSlug}}}",
@@ -252,7 +250,6 @@ Array [
     "errors": Array [],
     "excludeCommitPaths": Array [],
     "gitAuthor": null,
-    "gitFs": null,
     "gitPrivateKey": null,
     "group": Object {
       "branchTopic": "{{{groupSlug}}}",
@@ -361,7 +358,6 @@ Array [
     "errors": Array [],
     "excludeCommitPaths": Array [],
     "gitAuthor": null,
-    "gitFs": null,
     "gitPrivateKey": null,
     "group": Object {
       "branchTopic": "{{{groupSlug}}}",
@@ -474,7 +470,6 @@ Array [
     "errors": Array [],
     "excludeCommitPaths": Array [],
     "gitAuthor": null,
-    "gitFs": null,
     "gitPrivateKey": null,
     "group": Object {
       "branchTopic": "{{{groupSlug}}}",
@@ -583,7 +578,6 @@ Array [
     "errors": Array [],
     "excludeCommitPaths": Array [],
     "gitAuthor": null,
-    "gitFs": null,
     "gitPrivateKey": null,
     "group": Object {
       "branchTopic": "{{{groupSlug}}}",
@@ -696,7 +690,6 @@ Array [
     "errors": Array [],
     "excludeCommitPaths": Array [],
     "gitAuthor": null,
-    "gitFs": null,
     "gitPrivateKey": null,
     "group": Object {
       "branchTopic": "{{{groupSlug}}}",
@@ -807,7 +800,6 @@ Array [
     "errors": Array [],
     "excludeCommitPaths": Array [],
     "gitAuthor": null,
-    "gitFs": null,
     "gitPrivateKey": null,
     "group": Object {
       "branchTopic": "{{{groupSlug}}}",
@@ -918,7 +910,6 @@ Array [
     "errors": Array [],
     "excludeCommitPaths": Array [],
     "gitAuthor": null,
-    "gitFs": null,
     "gitPrivateKey": null,
     "group": Object {
       "branchTopic": "{{{groupSlug}}}",
diff --git a/website/docs/golang.md b/website/docs/golang.md
index dc0916603e35f18f560d727429321c237b5a85cd..a621b33a3a8abe868bd8e69d7e51e721cb022cf5 100644
--- a/website/docs/golang.md
+++ b/website/docs/golang.md
@@ -36,7 +36,3 @@ Vendoring of Go Modules is done automatically if `vendor/modules.txt` is present
 #### Go binary version
 
 Currently, Renovate will try to keep up with the very latest version of `go`, and it is not configurable. It is planned though to support a configurable version of `go` per-repository soon.
-
-#### gitFs
-
-Go Modules tidying and vendoring are both only possible if [`gitFs`](https://renovatebot.com/docs/self-hosted-configuration/#gitfs) is in use. If you are using the hosted Renovate App, then it should be enabled by default, but if you are self-hosting your Renovate Bot then you will need to configure this.
diff --git a/website/docs/java.md b/website/docs/java.md
index 6e32ddc3cee863b518bbb5b2bf2ecebb35669acf..36618ad8dad92faf7d9ddbad8e8ec2957dfcc311 100644
--- a/website/docs/java.md
+++ b/website/docs/java.md
@@ -24,8 +24,6 @@ Renovate does not support:
 
 Renovate uses a plugin to search and extract versions from projects. They are then looked up using Maven datasources and patched into PRs the usual way.
 
-It is recommended to use `gitFs` with Gradle, which is not the default setting for GitHub and GitLab. Enabling `gitFs` means that Renovate performs a shallow clone of the repository in order to get access to all the source files necessary, regardless of their name.
-
 ## Maven
 
 Renovate can update dependency versions found in Maven `pom.xml` files.
diff --git a/website/docs/self-hosted-configuration.md b/website/docs/self-hosted-configuration.md
index fdb7e16fd0c2ad36b0d1e1ac87ea763c47f4ce54..53d2bc5b036c3f0c13b7406edd4e4fddff2dd8fa 100644
--- a/website/docs/self-hosted-configuration.md
+++ b/website/docs/self-hosted-configuration.md
@@ -50,11 +50,6 @@ You probably have no need for this option - it is an experimental setting for th
 
 RFC5322-compliant string if you wish to customise the git author for commits.
 
-## gitFs
-
-This setting is experimental, and works for GitHub repositories only. If enabled, Renovate will `git clone` repos and use `git` for file operations such as creating branches and committing files.
-Set it to a string specifing the transport used by Git (`https`, `http` or `ssh`).
-
 ## gitPrivateKey
 
 ## logFile
@@ -73,7 +68,7 @@ Set this to `false` if (a) you configure Renovate entirely on the bot side (i.e.
 
 ## persistRepoData
 
-Set this to true if you wish for Renovate to persist repo data between runs. The intention is that this is used in combination with `gitFs`, allowing Renovate to do a faster `git fetch` between runs rather than `git clone`. It also may mean that ignored directories like `node_modules` can be preserved and save time on operations like `npm install`.
+Set this to true if you wish for Renovate to persist repo data between runs. The intention is that this allows Renovate to do a faster `git fetch` between runs rather than `git clone`. It also may mean that ignored directories like `node_modules` can be preserved and save time on operations like `npm install`.
 
 ## platform