From acb493be95865b0120554696eec9c56549d856ed Mon Sep 17 00:00:00 2001
From: Rhys Arkins <rhys@arkins.net>
Date: Fri, 24 May 2019 11:50:11 +0200
Subject: [PATCH] feat(github): gitFs-only (#3736)

The github platform code will now use git "under the hood" instead of plucking files one by one from GitHub's API.
---
 lib/platform/github/index.js                  |  84 +-
 lib/platform/github/storage.js                | 530 -------------
 .../github/__snapshots__/index.spec.js.snap   | 204 +----
 test/platform/github/index.spec.js            | 715 +-----------------
 test/platform/github/storage.spec.js          |  25 -
 5 files changed, 86 insertions(+), 1472 deletions(-)
 delete mode 100644 lib/platform/github/storage.js
 delete mode 100644 test/platform/github/storage.spec.js

diff --git a/lib/platform/github/index.js b/lib/platform/github/index.js
index fe914e5091..ba376a113c 100644
--- a/lib/platform/github/index.js
+++ b/lib/platform/github/index.js
@@ -4,7 +4,6 @@ const URL = require('url');
 
 const get = require('./gh-got-wrapper');
 const hostRules = require('../../util/host-rules');
-const Storage = require('./storage');
 const GitStorage = require('../git/storage');
 
 const {
@@ -127,7 +126,6 @@ async function initRepo({
   forkMode,
   forkToken,
   gitPrivateKey,
-  gitFs,
   localDir,
   includeForks,
   renovateUsername,
@@ -154,7 +152,7 @@ async function initRepo({
     res = await get(`repos/${repository}`);
     logger.trace({ repositoryDetails: res.body }, 'Repository details');
     // istanbul ignore if
-    if (res.body.fork && gitFs && !includeForks) {
+    if (res.body.fork && !includeForks) {
       try {
         const renovateConfig = JSON.parse(
           Buffer.from(
@@ -230,8 +228,6 @@ async function initRepo({
   config.prList = null;
   config.openPrList = null;
   config.closedPrList = null;
-  config.storage = new Storage();
-  await config.storage.initRepo(config);
   if (forkMode) {
     logger.info('Bot is in forkMode');
     config.forkToken = forkToken;
@@ -290,31 +286,25 @@ async function initRepo({
       // Wait an arbitrary 30s to hopefully give GitHub enough time for forking to complete
       await delay(30000);
     }
-    await config.storage.initRepo(config);
   }
 
-  // istanbul ignore if
-  if (gitFs) {
-    logger.debug('Enabling Git FS');
-    let { host } = URL.parse(defaults.endpoint);
-    if (host === 'api.github.com') {
-      host = null;
-    }
-    host = host || 'github.com';
-    const url = GitStorage.getUrl({
-      gitFs,
-      auth:
-        config.forkToken ||
-        (global.appMode ? `x-access-token:${opts.token}` : opts.token),
-      hostname: host,
-      repository: config.repository,
-    });
-    config.storage = new GitStorage();
-    await config.storage.initRepo({
-      ...config,
-      url,
-    });
-  }
+  // gitFs
+  const parsedEndpoint = URL.parse(defaults.endpoint);
+  parsedEndpoint.auth =
+    config.forkToken || global.appMode
+      ? `x-access-token:${opts.token}`
+      : opts.token;
+  parsedEndpoint.host = parsedEndpoint.host.replace(
+    'api.github.com',
+    'github.com'
+  );
+  parsedEndpoint.pathname = config.repository + '.git';
+  const url = URL.format(parsedEndpoint);
+  config.storage = new GitStorage();
+  await config.storage.initRepo({
+    ...config,
+    url,
+  });
 
   return platformConfig;
 }
@@ -364,11 +354,28 @@ async function getRepoForceRebase() {
   return config.repoForceRebase;
 }
 
+// Return the commit SHA for a branch
+async function getBranchCommit(branchName) {
+  try {
+    const res = await get(
+      `repos/${config.repository}/git/refs/heads/${branchName}`
+    );
+    return res.body.object.sha;
+  } catch (err) /* istanbul ignore next */ {
+    logger.debug({ err }, 'Error getting branch commit');
+    if (err.statusCode === 404) {
+      throw new Error('repository-changed');
+    }
+    if (err.statusCode === 409) {
+      throw new Error('empty');
+    }
+    throw err;
+  }
+}
+
 async function getBaseCommitSHA() {
   if (!config.baseCommitSHA) {
-    config.baseCommitSHA = await config.storage.getBranchCommit(
-      config.baseBranch
-    );
+    config.baseCommitSHA = await getBranchCommit(config.baseBranch);
   }
   return config.baseCommitSHA;
 }
@@ -384,12 +391,12 @@ async function getBranchProtection(branchName) {
   return res.body;
 }
 
+// istanbul ignore next
 async function setBaseBranch(branchName = config.baseBranch) {
   logger.debug(`Setting baseBranch to ${branchName}`);
   config.baseBranch = branchName;
   config.baseCommitSHA = null;
   await config.storage.setBaseBranch(branchName);
-  await getFileList(branchName);
 }
 
 // istanbul ignore next
@@ -399,34 +406,39 @@ function setBranchPrefix(branchPrefix) {
 
 // Search
 
-// Get full file list
+// istanbul ignore next
 function getFileList(branchName = config.baseBranch) {
   return config.storage.getFileList(branchName);
 }
 
 // Branch
 
-// Returns true if branch exists, otherwise false
+// istanbul ignore next
 function branchExists(branchName) {
   return config.storage.branchExists(branchName);
 }
 
+// istanbul ignore next
 function getAllRenovateBranches(branchPrefix) {
   return config.storage.getAllRenovateBranches(branchPrefix);
 }
 
+// istanbul ignore next
 function isBranchStale(branchName) {
   return config.storage.isBranchStale(branchName);
 }
 
+// istanbul ignore next
 function getFile(filePath, branchName) {
   return config.storage.getFile(filePath, branchName);
 }
 
+// istanbul ignore next
 function deleteBranch(branchName) {
   return config.storage.deleteBranch(branchName);
 }
 
+// istanbul ignore next
 function getBranchLastCommitTime(branchName) {
   return config.storage.getBranchLastCommitTime(branchName);
 }
@@ -436,8 +448,8 @@ function getRepoStatus() {
   return config.storage.getRepoStatus();
 }
 
+// istanbul ignore next
 function mergeBranch(branchName) {
-  // istanbul ignore if
   if (config.pushProtection) {
     logger.info(
       { branch: branchName },
@@ -447,6 +459,7 @@ function mergeBranch(branchName) {
   return config.storage.mergeBranch(branchName);
 }
 
+// istanbul ignore next
 function commitFilesToBranch(
   branchName,
   files,
@@ -461,6 +474,7 @@ function commitFilesToBranch(
   );
 }
 
+// istanbul ignore next
 function getCommitMessages() {
   return config.storage.getCommitMessages();
 }
diff --git a/lib/platform/github/storage.js b/lib/platform/github/storage.js
deleted file mode 100644
index 7dbd19c6ec..0000000000
--- a/lib/platform/github/storage.js
+++ /dev/null
@@ -1,530 +0,0 @@
-const moment = require('moment');
-const openpgp = require('openpgp');
-const path = require('path');
-const get = require('./gh-got-wrapper');
-
-class Storage {
-  constructor() {
-    // config
-    let config = {};
-    // cache
-    let branchFiles = {};
-    let branchList = null;
-
-    Object.assign(this, {
-      initRepo,
-      cleanRepo,
-      getRepoStatus: () => ({}),
-      branchExists,
-      commitFilesToBranch,
-      createBranch,
-      deleteBranch,
-      getAllRenovateBranches,
-      getBranchCommit,
-      getBranchLastCommitTime,
-      getCommitMessages,
-      getFile,
-      getFileList,
-      isBranchStale,
-      mergeBranch,
-      setBaseBranch,
-      setBranchPrefix,
-    });
-
-    function initRepo(args) {
-      cleanRepo();
-      config = { ...args };
-    }
-
-    function cleanRepo() {
-      branchFiles = {};
-      branchList = null;
-    }
-
-    async function getBranchList() {
-      if (!branchList) {
-        logger.debug('Retrieving branchList');
-        branchList = (await get(
-          `repos/${config.repository}/branches?per_page=100`,
-          {
-            paginate: true,
-          }
-        )).body.map(branch => branch.name);
-      }
-      return branchList;
-    }
-
-    // Returns true if branch exists, otherwise false
-    async function branchExists(branchName) {
-      const res = (await getBranchList()).includes(branchName);
-      logger.debug(`branchExists(${branchName})=${res}`);
-      return res;
-    }
-
-    function setBaseBranch(branchName) {
-      if (branchName) {
-        logger.debug(`Setting baseBranch to ${branchName}`);
-        config.baseBranch = branchName;
-      }
-    }
-
-    // istanbul ignore next
-    function setBranchPrefix() {
-      // Do nothing
-    }
-
-    // Get full file list
-    async function getFileList(branchName) {
-      const branch = branchName || config.baseBranch;
-      if (branchFiles[branch]) {
-        return branchFiles[branch];
-      }
-      try {
-        const res = await get(
-          `repos/${config.repository}/git/trees/${branch}?recursive=true`
-        );
-        if (res.body.truncated) {
-          logger.warn(
-            { repository: config.repository },
-            'repository tree is truncated'
-          );
-        }
-        const fileList = res.body.tree
-          .filter(item => item.type === 'blob' && item.mode !== '120000')
-          .map(item => item.path)
-          .sort();
-        logger.debug(`Retrieved fileList with length ${fileList.length}`);
-        branchFiles[branch] = fileList;
-        return fileList;
-      } catch (err) /* istanbul ignore next */ {
-        if (err.statusCode === 409) {
-          logger.debug('Repository is not initiated');
-          throw new Error('uninitiated');
-        }
-        logger.info(
-          { branchName, err, repository: config.repository },
-          'Error retrieving git tree - no files detected'
-        );
-        throw err;
-      }
-    }
-
-    async function getAllRenovateBranches(branchPrefix) {
-      logger.trace('getAllRenovateBranches');
-      const allBranches = await getBranchList();
-      if (branchPrefix.endsWith('/')) {
-        const branchPrefixPrefix = branchPrefix.slice(0, -1);
-        if (allBranches.includes(branchPrefixPrefix)) {
-          logger.warn(
-            `Pruning branch "${branchPrefixPrefix}" so that it does not block PRs`
-          );
-          await deleteBranch(branchPrefixPrefix);
-        }
-      }
-      return allBranches.filter(branchName =>
-        branchName.startsWith(branchPrefix)
-      );
-    }
-
-    async function isBranchStale(branchName) {
-      // Check if branch's parent SHA = master SHA
-      logger.debug(`isBranchStale(${branchName})`);
-      const branchCommit = await getBranchCommit(branchName);
-      logger.debug(`branchCommit=${branchCommit}`);
-      const commitDetails = await getCommitDetails(branchCommit);
-      logger.trace({ commitDetails }, `commitDetails`);
-      const parentSha = commitDetails.parents[0].sha;
-      logger.debug(`parentSha=${parentSha}`);
-      const baseCommitSHA = await getBranchCommit(config.baseBranch);
-      logger.debug(`baseCommitSHA=${baseCommitSHA}`);
-      // Return true if the SHAs don't match
-      return parentSha !== baseCommitSHA;
-    }
-
-    async function deleteBranch(branchName) {
-      delete branchFiles[branchName];
-      const options = config.forkToken
-        ? { token: config.forkToken }
-        : undefined;
-      try {
-        await get.delete(
-          `repos/${config.repository}/git/refs/heads/${branchName}`,
-          options
-        );
-      } catch (err) /* istanbul ignore next */ {
-        if (err.message.startsWith('Reference does not exist')) {
-          logger.info(
-            { branch: branchName },
-            'Branch to delete does not exist'
-          );
-        } else if (err.message.startsWith('Cannot delete protected branch')) {
-          logger.info({ branch: branchName }, 'Cannot delete protected branch');
-        } else {
-          logger.warn({ err, branch: branchName }, 'Error deleting branch');
-        }
-      }
-    }
-
-    async function mergeBranch(branchName) {
-      logger.debug(`mergeBranch(${branchName})`);
-      const url = `repos/${config.repository}/git/refs/heads/${
-        config.baseBranch
-      }`;
-      const options = {
-        body: {
-          sha: await getBranchCommit(branchName),
-        },
-      };
-      try {
-        await get.patch(url, options);
-        logger.debug({ branch: branchName }, 'Branch merged');
-      } catch (err) {
-        if (
-          err.message.startsWith('Required status check') ||
-          err.message.includes('required status checks are expected')
-        ) {
-          logger.debug('Branch is not ready for merge: ' + err.message);
-          throw new Error('not ready');
-        }
-        logger.info({ err }, `Error pushing branch merge for ${branchName}`);
-        throw new Error('Branch automerge failed');
-      }
-      // Delete branch
-      await deleteBranch(branchName);
-    }
-
-    async function getBranchLastCommitTime(branchName) {
-      try {
-        const res = await get(
-          `repos/${config.repository}/commits?sha=${branchName}`
-        );
-        return new Date(res.body[0].commit.committer.date);
-      } catch (err) {
-        logger.error({ err }, `getBranchLastCommitTime error`);
-        return new Date();
-      }
-    }
-
-    // Generic File operations
-
-    async function getFile(filePath, branchName) {
-      logger.trace(`getFile(filePath=${filePath}, branchName=${branchName})`);
-      if (!(await getFileList(branchName)).includes(filePath)) {
-        return null;
-      }
-      let res;
-      try {
-        res = await get(
-          `repos/${config.repository}/contents/${encodeURI(
-            filePath
-          )}?ref=${branchName || config.baseBranch}`
-        );
-      } catch (error) {
-        if (error.statusCode === 404) {
-          // If file not found, then return null JSON
-          logger.info({ filePath, branch: branchName }, 'getFile 404');
-          return null;
-        }
-        if (
-          error.statusCode === 403 &&
-          error.message &&
-          error.message.startsWith('This API returns blobs up to 1 MB in size')
-        ) {
-          logger.info('Large file');
-          // istanbul ignore if
-          if (branchName && branchName !== config.baseBranch) {
-            logger.info('Cannot retrieve large files from non-master branch');
-            return null;
-          }
-          // istanbul ignore if
-          if (path.dirname(filePath) !== '.') {
-            logger.info(
-              'Cannot retrieve large files from non-root directories'
-            );
-            return null;
-          }
-          const treeUrl = `repos/${config.repository}/git/trees/${
-            config.baseBranch
-          }`;
-          const baseName = path.basename(filePath);
-          let fileSha;
-          (await get(treeUrl)).body.tree.forEach(file => {
-            if (file.path === baseName) {
-              fileSha = file.sha;
-            }
-          });
-          if (!fileSha) {
-            logger.warn('Could not locate file blob');
-            throw error;
-          }
-          res = await get(`repos/${config.repository}/git/blobs/${fileSha}`);
-        } else {
-          // Propagate if it's any other error
-          throw error;
-        }
-      }
-      if (res && res.body) {
-        if (res.body.content) {
-          return Buffer.from(res.body.content, 'base64').toString();
-        }
-        // istanbul ignore next
-        return '';
-      }
-      return null;
-    }
-
-    // Add a new commit, create branch if not existing
-    async function commitFilesToBranch(
-      branchName,
-      files,
-      message,
-      parentBranch = config.baseBranch
-    ) {
-      logger.debug(
-        `commitFilesToBranch('${branchName}', files, message, '${parentBranch})'`
-      );
-      try {
-        delete branchFiles[branchName];
-        const parentCommit = await getBranchCommit(parentBranch);
-        const parentTree = await getCommitTree(parentCommit);
-        const fileBlobs = [];
-        // Create blobs
-        for (const file of files) {
-          const blob = await createBlob(file.contents);
-          fileBlobs.push({
-            name: file.name,
-            blob,
-          });
-        }
-        // Create tree
-        const tree = await createTree(parentTree, fileBlobs);
-        const commit = await createCommit(parentCommit, tree, message);
-        const isBranchExisting = await branchExists(branchName);
-        if (isBranchExisting) {
-          await updateBranch(branchName, commit);
-          logger.debug({ branch: branchName }, 'Branch updated');
-          return 'updated';
-        }
-        await createBranch(branchName, commit);
-        logger.debug({ branch: branchName }, 'Branch created');
-        // istanbul ignore if
-        if (branchList) {
-          branchList.push(branchName);
-        }
-        return 'created';
-      } catch (err) /* istanbul ignore next */ {
-        if (err.statusCode === 404) {
-          throw new Error('repository-changed');
-        }
-        throw err;
-      }
-    }
-
-    // Internal branch operations
-
-    // Creates a new branch with provided commit
-    async function createBranch(branchName, sha) {
-      logger.debug(`createBranch(${branchName})`);
-      const options = {
-        body: {
-          ref: `refs/heads/${branchName}`,
-          sha,
-        },
-      };
-      // istanbul ignore if
-      if (config.forkToken) {
-        options.token = config.forkToken;
-      }
-      try {
-        // istanbul ignore if
-        if (branchName.includes('/')) {
-          const [blockingBranch] = branchName.split('/');
-          if (await branchExists(blockingBranch)) {
-            logger.warn({ blockingBranch }, 'Deleting blocking branch');
-            await deleteBranch(blockingBranch);
-          }
-        }
-        logger.debug({ options, branch: branchName }, 'Creating branch');
-        await get.post(`repos/${config.repository}/git/refs`, options);
-        branchList.push(branchName);
-        logger.debug('Created branch');
-      } catch (err) /* istanbul ignore next */ {
-        const headers = err.response.req.getHeaders();
-        delete headers.token;
-        logger.warn(
-          {
-            err,
-            options,
-          },
-          'Error creating branch'
-        );
-        if (err.statusCode === 422) {
-          throw new Error('repository-changed');
-        }
-        throw err;
-      }
-    }
-
-    // Return the commit SHA for a branch
-    async function getBranchCommit(branchName) {
-      try {
-        const res = await get(
-          `repos/${config.repository}/git/refs/heads/${branchName}`
-        );
-        return res.body.object.sha;
-      } catch (err) /* istanbul ignore next */ {
-        logger.debug({ err }, 'Error getting branch commit');
-        if (err.statusCode === 404) {
-          throw new Error('repository-changed');
-        }
-        if (err.statusCode === 409) {
-          throw new Error('empty');
-        }
-        throw err;
-      }
-    }
-
-    async function getCommitMessages() {
-      logger.debug('getCommitMessages');
-      const res = await get(`repos/${config.repository}/commits`);
-      return res.body.map(commit => commit.commit.message);
-    }
-
-    // Internal: Updates an existing branch to new commit sha
-    async function updateBranch(branchName, commit) {
-      logger.debug(`Updating branch ${branchName} with commit ${commit}`);
-      const options = {
-        body: {
-          sha: commit,
-          force: true,
-        },
-      };
-      // istanbul ignore if
-      if (config.forkToken) {
-        options.token = config.forkToken;
-      }
-      try {
-        await get.patch(
-          `repos/${config.repository}/git/refs/heads/${branchName}`,
-          options
-        );
-      } catch (err) /* istanbul ignore next */ {
-        if (err.statusCode === 422) {
-          logger.info({ err }, 'Branch no longer exists - exiting');
-          throw new Error('repository-changed');
-        }
-        throw err;
-      }
-    }
-    // Low-level commit operations
-
-    // Create a blob with fileContents and return sha
-    async function createBlob(fileContents) {
-      logger.debug('Creating blob');
-      const options = {
-        body: {
-          encoding: 'base64',
-          content: Buffer.from(fileContents).toString('base64'),
-        },
-      };
-      // istanbul ignore if
-      if (config.forkToken) {
-        options.token = config.forkToken;
-      }
-      return (await get.post(`repos/${config.repository}/git/blobs`, options))
-        .body.sha;
-    }
-
-    // Return the tree SHA for a commit
-    async function getCommitTree(commit) {
-      logger.debug(`getCommitTree(${commit})`);
-      return (await get(`repos/${config.repository}/git/commits/${commit}`))
-        .body.tree.sha;
-    }
-
-    // Create a tree and return SHA
-    async function createTree(baseTree, files) {
-      logger.debug(`createTree(${baseTree}, files)`);
-      const body = {
-        base_tree: baseTree,
-        tree: [],
-      };
-      files.forEach(file => {
-        body.tree.push({
-          path: file.name,
-          mode: '100644',
-          type: 'blob',
-          sha: file.blob,
-        });
-      });
-      logger.trace({ body }, 'createTree body');
-      const options = { body };
-      // istanbul ignore if
-      if (config.forkToken) {
-        options.token = config.forkToken;
-      }
-      return (await get.post(`repos/${config.repository}/git/trees`, options))
-        .body.sha;
-    }
-
-    // Create a commit and return commit SHA
-    async function createCommit(parent, tree, message) {
-      logger.debug(`createCommit(${parent}, ${tree}, ${message})`);
-      const { gitPrivateKey } = config;
-      const now = moment();
-      let author;
-      if (global.gitAuthor) {
-        logger.trace('Setting gitAuthor');
-        author = {
-          name: global.gitAuthor.name,
-          email: global.gitAuthor.email,
-          date: now.format(),
-        };
-      }
-      const body = {
-        message,
-        parents: [parent],
-        tree,
-      };
-      if (author) {
-        body.author = author;
-        // istanbul ignore if
-        if (gitPrivateKey) {
-          logger.debug('Found gitPrivateKey');
-          const privKeyObj = openpgp.key.readArmored(gitPrivateKey).keys[0];
-          const commit = `tree ${tree}\nparent ${parent}\nauthor ${
-            author.name
-          } <${author.email}> ${now.format('X ZZ')}\ncommitter ${
-            author.name
-          } <${author.email}> ${now.format('X ZZ')}\n\n${message}`;
-          const { signature } = await openpgp.sign({
-            data: openpgp.util.str2Uint8Array(commit),
-            privateKeys: privKeyObj,
-            detached: true,
-            armor: true,
-          });
-          body.signature = signature;
-        }
-      }
-      const options = {
-        body,
-      };
-      // istanbul ignore if
-      if (config.forkToken) {
-        options.token = config.forkToken;
-      }
-      return (await get.post(`repos/${config.repository}/git/commits`, options))
-        .body.sha;
-    }
-
-    async function getCommitDetails(commit) {
-      logger.debug(`getCommitDetails(${commit})`);
-      const results = await get(
-        `repos/${config.repository}/git/commits/${commit}`
-      );
-      return results.body;
-    }
-  }
-}
-
-module.exports = Storage;
diff --git a/test/platform/github/__snapshots__/index.spec.js.snap b/test/platform/github/__snapshots__/index.spec.js.snap
index 0b903b4eca..ebecc55c24 100644
--- a/test/platform/github/__snapshots__/index.spec.js.snap
+++ b/test/platform/github/__snapshots__/index.spec.js.snap
@@ -32,46 +32,6 @@ Array [
 ]
 `;
 
-exports[`platform/github commitFilesToBranch(branchName, files, message, parentBranch) should add a commit to a new branch if the branch does not already exist 1`] = `
-Array [
-  Array [
-    "repos/some/repo",
-  ],
-  Array [
-    "repos/some/repo/git/refs/heads/master",
-  ],
-  Array [
-    "repos/some/repo/git/commits/1111",
-  ],
-  Array [
-    "repos/some/repo/branches?per_page=100",
-    Object {
-      "paginate": true,
-    },
-  ],
-]
-`;
-
-exports[`platform/github commitFilesToBranch(branchName, files, message, parentBranch) should add a new commit to the branch 1`] = `
-Array [
-  Array [
-    "repos/some/repo",
-  ],
-  Array [
-    "repos/some/repo/git/refs/heads/master",
-  ],
-  Array [
-    "repos/some/repo/git/commits/1111",
-  ],
-  Array [
-    "repos/some/repo/branches?per_page=100",
-    Object {
-      "paginate": true,
-    },
-  ],
-]
-`;
-
 exports[`platform/github createPr() should create and return a PR object 1`] = `
 Object {
   "branchName": "some-branch",
@@ -103,7 +63,7 @@ Array [
     },
   ],
   Array [
-    "repos/some/repo/statuses/some-sha",
+    "repos/some/repo/statuses/0d9c7726c3d628b7e28af234595cfd20febdbf8e",
     Object {
       "body": Object {
         "context": "renovate/verify",
@@ -178,8 +138,6 @@ content",
 ]
 `;
 
-exports[`platform/github getBranchLastCommitTime should return a Date 1`] = `2011-04-14T16:00:49.000Z`;
-
 exports[`platform/github getBranchPr(branchName) should return the PR object 1`] = `
 Array [
   Array [
@@ -196,86 +154,6 @@ Array [
 
 exports[`platform/github getBranchPr(branchName) should return the PR object 2`] = `null`;
 
-exports[`platform/github getCommitMessages() returns commits messages 1`] = `
-Array [
-  "foo",
-  "bar",
-]
-`;
-
-exports[`platform/github getFile() should return large file via git API 1`] = `
-Array [
-  Array [
-    "repos/some/repo",
-  ],
-  Array [
-    "repos/some/repo/git/trees/master?recursive=true",
-  ],
-  Array [
-    "repos/some/repo/contents/package-lock.json?ref=master",
-  ],
-  Array [
-    "repos/some/repo/git/trees/master",
-  ],
-  Array [
-    "repos/some/repo/git/blobs/some-sha",
-  ],
-]
-`;
-
-exports[`platform/github getFile() should return large file via git API 2`] = `"{\\"hello\\":\\"workd\\"}"`;
-
-exports[`platform/github getFile() should return null if GitHub returns a 404 1`] = `
-Array [
-  Array [
-    "repos/some/repo",
-  ],
-  Array [
-    "repos/some/repo/git/trees/master?recursive=true",
-  ],
-  Array [
-    "repos/some/repo/contents/package.json?ref=master",
-  ],
-]
-`;
-
-exports[`platform/github getFile() should return null if getFile returns nothing 1`] = `
-Array [
-  Array [
-    "repos/some/repo",
-  ],
-  Array [
-    "repos/some/repo/git/trees/master?recursive=true",
-  ],
-  Array [
-    "repos/some/repo/contents/package.json?ref=master",
-  ],
-]
-`;
-
-exports[`platform/github getFile() should return the encoded file content 1`] = `
-Array [
-  Array [
-    "repos/some/repo",
-  ],
-  Array [
-    "repos/some/repo/git/trees/master?recursive=true",
-  ],
-  Array [
-    "repos/some/repo/contents/package.json?ref=master",
-  ],
-]
-`;
-
-exports[`platform/github getFileList should return the files matching the fileName 1`] = `
-Array [
-  "package.json",
-  "some-dir/package.json.some-thing-else",
-  "src/app/package.json",
-  "src/otherapp/package.json",
-]
-`;
-
 exports[`platform/github getPr(prNo) should return PR from closed graphql result 1`] = `
 Object {
   "body": "dummy body",
@@ -527,86 +405,6 @@ Object {
 }
 `;
 
-exports[`platform/github mergeBranch(branchName) should perform a branch merge 1`] = `
-Array [
-  Array [
-    "repos/some/repo",
-  ],
-  Array [
-    "repos/some/repo/git/refs/heads/thebranchname",
-  ],
-]
-`;
-
-exports[`platform/github mergeBranch(branchName) should perform a branch merge 2`] = `
-Array [
-  Array [
-    "repos/some/repo/git/refs/heads/master",
-    Object {
-      "body": Object {
-        "sha": "1235",
-      },
-    },
-  ],
-]
-`;
-
-exports[`platform/github mergeBranch(branchName) should perform a branch merge 3`] = `Array []`;
-
-exports[`platform/github mergeBranch(branchName) should perform a branch merge 4`] = `Array []`;
-
-exports[`platform/github mergeBranch(branchName) should perform a branch merge 5`] = `
-Array [
-  Array [
-    "repos/some/repo/git/refs/heads/thebranchname",
-    undefined,
-  ],
-]
-`;
-
-exports[`platform/github mergeBranch(branchName) should throw if branch merge throws 1`] = `[Error: Branch automerge failed]`;
-
-exports[`platform/github mergeBranch(branchName) should throw if branch merge throws 2`] = `
-Array [
-  Array [
-    "repos/some/repo",
-  ],
-  Array [
-    "repos/some/repo/git/refs/heads/thebranchname",
-  ],
-]
-`;
-
-exports[`platform/github mergeBranch(branchName) should throw if branch merge throws 3`] = `
-Array [
-  Array [
-    "repos/some/repo/git/refs/heads/master",
-    Object {
-      "body": Object {
-        "sha": "1235",
-      },
-    },
-  ],
-]
-`;
-
-exports[`platform/github mergeBranch(branchName) should throw if branch merge throws 4`] = `Array []`;
-
-exports[`platform/github mergeBranch(branchName) should throw if branch merge throws 5`] = `Array []`;
-
-exports[`platform/github mergeBranch(branchName) should throw if branch merge throws 6`] = `Array []`;
-
-exports[`platform/github setBaseBranch(branchName) sets the base branch 1`] = `
-Array [
-  Array [
-    "repos/some/repo",
-  ],
-  Array [
-    "repos/some/repo/git/trees/some-branch?recursive=true",
-  ],
-]
-`;
-
 exports[`platform/github updatePr(prNo, title, body) should update the PR 1`] = `
 Array [
   Array [
diff --git a/test/platform/github/index.spec.js b/test/platform/github/index.spec.js
index bccb006dc7..e2a2cd9ca4 100644
--- a/test/platform/github/index.spec.js
+++ b/test/platform/github/index.spec.js
@@ -4,6 +4,7 @@ describe('platform/github', () => {
   let github;
   let get;
   let hostRules;
+  let GitStorage;
   beforeEach(() => {
     // reset module
     jest.resetModules();
@@ -13,6 +14,27 @@ describe('platform/github', () => {
     get = require('../../../lib/platform/github/gh-got-wrapper');
     github = require('../../../lib/platform/github');
     hostRules = require('../../../lib/util/host-rules');
+    jest.mock('../../../lib/platform/git/storage');
+    GitStorage = require('../../../lib/platform/git/storage');
+    GitStorage.mockImplementation(() => ({
+      initRepo: jest.fn(),
+      cleanRepo: jest.fn(),
+      getFileList: jest.fn(),
+      branchExists: jest.fn(() => true),
+      isBranchStale: jest.fn(() => false),
+      setBaseBranch: jest.fn(),
+      getBranchLastCommitTime: jest.fn(),
+      getAllRenovateBranches: jest.fn(),
+      getCommitMessages: jest.fn(),
+      getFile: jest.fn(),
+      commitFilesToBranch: jest.fn(),
+      mergeBranch: jest.fn(),
+      deleteBranch: jest.fn(),
+      getRepoStatus: jest.fn(),
+      getBranchCommit: jest.fn(
+        () => '0d9c7726c3d628b7e28af234595cfd20febdbf8e'
+      ),
+    }));
     delete global.gitAuthor;
     hostRules.find.mockReturnValue({
       hostType: 'github',
@@ -97,7 +119,14 @@ describe('platform/github', () => {
         allow_merge_commit: true,
       },
     }));
-    return github.initRepo(...args);
+    if (args.length) {
+      return github.initRepo(...args);
+    }
+    return github.initRepo({
+      endpoint: 'https://github.com',
+      repository: 'some/repo',
+      token: 'token',
+    });
   }
 
   describe('initRepo', () => {
@@ -345,184 +374,6 @@ describe('platform/github', () => {
       });
     });
   });
-  describe('setBaseBranch(branchName)', () => {
-    it('sets the base branch', async () => {
-      await initRepo({
-        repository: 'some/repo',
-      });
-      get.mockImplementationOnce(() => ({
-        body: {
-          truncated: true,
-          tree: [],
-        },
-      }));
-      // getBranchCommit
-      get.mockImplementationOnce(() => ({
-        body: {
-          object: {
-            sha: '1238',
-          },
-        },
-      }));
-      await github.setBaseBranch('some-branch');
-      expect(get.mock.calls).toMatchSnapshot();
-    });
-  });
-  describe('getFileList', () => {
-    beforeEach(async () => {
-      await initRepo({
-        repository: 'some/repo',
-      });
-    });
-    it('throws if error', async () => {
-      get.mockImplementationOnce(() => {
-        throw new Error('some error');
-      });
-      await expect(github.getFileList('error-branch')).rejects.toThrow();
-    });
-    it('warns if truncated result', async () => {
-      get.mockImplementationOnce(() => ({
-        body: {
-          truncated: true,
-          tree: [],
-        },
-      }));
-      const files = await github.getFileList('truncated-branch');
-      expect(files).toHaveLength(0);
-    });
-    it('caches the result', async () => {
-      get.mockImplementationOnce(() => ({
-        body: {
-          truncated: true,
-          tree: [],
-        },
-      }));
-      let files = await github.getFileList('cached-branch');
-      expect(files).toHaveLength(0);
-      files = await github.getFileList('cached-branch');
-      expect(files).toHaveLength(0);
-    });
-    it('should return the files matching the fileName', async () => {
-      get.mockImplementationOnce(() => ({
-        body: {
-          tree: [
-            { type: 'blob', path: 'symlinks/package.json', mode: '120000' },
-            { type: 'blob', path: 'package.json' },
-            {
-              type: 'blob',
-              path: 'some-dir/package.json.some-thing-else',
-            },
-            { type: 'blob', path: 'src/app/package.json' },
-            { type: 'blob', path: 'src/otherapp/package.json' },
-          ],
-        },
-      }));
-      const files = await github.getFileList('npm-branch');
-      expect(files).toMatchSnapshot();
-    });
-  });
-  describe('branchExists(branchName)', () => {
-    it('should return true if the branch exists (one result)', async () => {
-      await initRepo({
-        repository: 'some/repo',
-      });
-      get.mockImplementationOnce(() => ({
-        body: [
-          {
-            name: 'thebranchname',
-          },
-        ],
-      }));
-      const exists = await github.branchExists('thebranchname');
-      expect(exists).toBe(true);
-    });
-  });
-  describe('getAllRenovateBranches()', () => {
-    it('should return all renovate branches', async () => {
-      await initRepo({
-        repository: 'some/repo',
-      });
-      get.mockImplementationOnce(() => ({
-        body: [
-          {
-            name: 'thebranchname',
-          },
-          {
-            name: 'renovate',
-          },
-          {
-            name: 'renovate/abc-1.x',
-          },
-        ],
-      }));
-      const res = await github.getAllRenovateBranches('renovate/');
-      expect(res).toHaveLength(1);
-    });
-  });
-  describe('isBranchStale(branchName)', () => {
-    it('should return false if same SHA as master', async () => {
-      await initRepo({
-        repository: 'some/repo',
-      }); // getBranchCommit
-      get.mockImplementationOnce(() => ({
-        body: {
-          object: {
-            sha: '1235',
-          },
-        },
-      }));
-      // getCommitDetails - same as master
-      get.mockImplementationOnce(() => ({
-        body: {
-          parents: [
-            {
-              sha: '1234',
-            },
-          ],
-        },
-      }));
-      // getBranchCommit
-      get.mockImplementationOnce(() => ({
-        body: {
-          object: {
-            sha: '1234',
-          },
-        },
-      }));
-      expect(await github.isBranchStale('thebranchname')).toBe(false);
-    });
-    it('should return true if SHA different from master', async () => {
-      await initRepo({
-        repository: 'some/repo',
-      }); // getBranchCommit
-      get.mockImplementationOnce(() => ({
-        body: {
-          object: {
-            sha: '1235',
-          },
-        },
-      }));
-      // getCommitDetails - different
-      get.mockImplementationOnce(() => ({
-        body: {
-          parents: [
-            {
-              sha: '12345678',
-            },
-          ],
-        },
-      }));
-      // getBranchCommit
-      get.mockImplementationOnce(() => ({
-        body: {
-          object: {
-            sha: '1234',
-          },
-        },
-      }));
-      expect(await github.isBranchStale('thebranchname')).toBe(true);
-    });
-  });
   describe('getBranchPr(branchName)', () => {
     it('should return null if no PR exists', async () => {
       await initRepo({
@@ -696,14 +547,8 @@ describe('platform/github', () => {
     it('returns state if found', async () => {
       await initRepo({
         repository: 'some/repo',
-      }); // getBranchCommit
-      get.mockImplementationOnce(() => ({
-        body: {
-          object: {
-            sha: '1235',
-          },
-        },
-      }));
+        token: 'token',
+      });
       get.mockImplementationOnce(() => ({
         body: [
           {
@@ -720,21 +565,16 @@ describe('platform/github', () => {
           },
         ],
       }));
-      const res = await github.getBranchStatusCheck('somebranch', 'context-2');
+      const res = await github.getBranchStatusCheck(
+        'renovate/future_branch',
+        'context-2'
+      );
       expect(res).toEqual('state-2');
     });
     it('returns null', async () => {
       await initRepo({
         repository: 'some/repo',
       });
-      // getBranchCommit
-      get.mockImplementationOnce(() => ({
-        body: {
-          object: {
-            sha: '1235',
-          },
-        },
-      }));
       get.mockImplementationOnce(() => ({
         body: [
           {
@@ -760,14 +600,6 @@ describe('platform/github', () => {
       await initRepo({
         repository: 'some/repo',
       });
-      // getBranchCommit
-      get.mockImplementationOnce(() => ({
-        body: {
-          object: {
-            sha: '1235',
-          },
-        },
-      }));
       get.mockImplementationOnce(() => ({
         body: [
           {
@@ -789,14 +621,6 @@ describe('platform/github', () => {
       await initRepo({
         repository: 'some/repo',
       });
-      // getBranchCommit
-      get.mockImplementationOnce(() => ({
-        body: {
-          object: {
-            sha: '1235',
-          },
-        },
-      }));
       get.mockImplementationOnce(() => ({
         body: [
           {
@@ -831,112 +655,6 @@ describe('platform/github', () => {
       expect(get.post).toHaveBeenCalledTimes(1);
     });
   });
-  describe('mergeBranch(branchName)', () => {
-    it('should perform a branch merge', async () => {
-      await initRepo({
-        repository: 'some/repo',
-      }); // getBranchCommit
-      get.mockImplementationOnce(() => ({
-        body: {
-          object: {
-            sha: '1235',
-          },
-        },
-      }));
-      get.patch.mockImplementationOnce();
-      // getBranchCommit
-      get.mockImplementationOnce(() => ({
-        body: {
-          object: {
-            sha: '1235',
-          },
-        },
-      }));
-      // deleteBranch
-      get.delete.mockImplementationOnce();
-      await github.mergeBranch('thebranchname', 'branch');
-      expect(get.mock.calls).toMatchSnapshot();
-      expect(get.patch.mock.calls).toMatchSnapshot();
-      expect(get.post.mock.calls).toMatchSnapshot();
-      expect(get.put.mock.calls).toMatchSnapshot();
-      expect(get.delete.mock.calls).toMatchSnapshot();
-    });
-    it('should throw if branch merge throws', async () => {
-      await initRepo({
-        repository: 'some/repo',
-      }); // getBranchCommit
-      get.mockImplementationOnce(() => ({
-        body: {
-          object: {
-            sha: '1235',
-          },
-        },
-      }));
-      get.patch.mockImplementationOnce(() => {
-        throw new Error('branch failed');
-      });
-      let e;
-      try {
-        await github.mergeBranch('thebranchname', 'branch');
-      } catch (err) {
-        e = err;
-      }
-      expect(e).toMatchSnapshot();
-      expect(get.mock.calls).toMatchSnapshot();
-      expect(get.patch.mock.calls).toMatchSnapshot();
-      expect(get.post.mock.calls).toMatchSnapshot();
-      expect(get.put.mock.calls).toMatchSnapshot();
-      expect(get.delete.mock.calls).toMatchSnapshot();
-    });
-    it('should throw not ready', async () => {
-      await initRepo({
-        repository: 'some/repo',
-      }); // getBranchCommit
-      get.mockImplementationOnce(() => ({
-        body: {
-          object: {
-            sha: '1235',
-          },
-        },
-      }));
-      get.patch.mockImplementationOnce(() => {
-        throw new Error('3 of 3 required status checks are expected.');
-      });
-      await expect(
-        github.mergeBranch('thebranchname', 'branch')
-      ).rejects.toThrow(Error('not ready'));
-    });
-  });
-  describe('getBranchLastCommitTime', () => {
-    it('should return a Date', async () => {
-      await initRepo({
-        repository: 'some/repo',
-      });
-      get.mockReturnValueOnce({
-        body: [
-          {
-            commit: {
-              committer: {
-                date: '2011-04-14T16:00:49Z',
-              },
-            },
-          },
-        ],
-      });
-      const res = await github.getBranchLastCommitTime('some-branch');
-      expect(res).toMatchSnapshot();
-    });
-    it('handles error', async () => {
-      await initRepo({
-        repository: 'some/repo',
-      });
-      get.mockReturnValueOnce({
-        body: [],
-      });
-      const res = await github.getBranchLastCommitTime('some-branch');
-      expect(res).toBeDefined();
-    });
-  });
   describe('findIssue()', () => {
     it('returns null if no issue', async () => {
       get.mockReturnValueOnce({
@@ -1281,14 +999,6 @@ describe('platform/github', () => {
           number: 123,
         },
       }));
-      // getBranchCommit
-      get.mockImplementationOnce(() => ({
-        body: {
-          object: {
-            sha: '1235',
-          },
-        },
-      }));
       get.mockImplementationOnce(() => ({
         body: [],
       }));
@@ -1645,7 +1355,6 @@ describe('platform/github', () => {
       };
       expect(await github.mergePr(pr)).toBe(true);
       expect(get.put).toHaveBeenCalledTimes(1);
-      expect(get.delete).toHaveBeenCalledTimes(1);
       expect(get).toHaveBeenCalledTimes(1);
     });
     it('should handle merge error', async () => {
@@ -1661,7 +1370,6 @@ describe('platform/github', () => {
       });
       expect(await github.mergePr(pr)).toBe(false);
       expect(get.put).toHaveBeenCalledTimes(1);
-      expect(get.delete).toHaveBeenCalledTimes(0);
       expect(get).toHaveBeenCalledTimes(1);
     });
   });
@@ -1744,7 +1452,6 @@ describe('platform/github', () => {
       };
       expect(await github.mergePr(pr)).toBe(true);
       expect(get.put).toHaveBeenCalledTimes(1);
-      expect(get.delete).toHaveBeenCalledTimes(1);
     });
     it('should try squash after rebase', async () => {
       const pr = {
@@ -1758,7 +1465,6 @@ describe('platform/github', () => {
       });
       await github.mergePr(pr);
       expect(get.put).toHaveBeenCalledTimes(2);
-      expect(get.delete).toHaveBeenCalledTimes(1);
     });
     it('should try merge after squash', async () => {
       const pr = {
@@ -1775,7 +1481,6 @@ describe('platform/github', () => {
       });
       expect(await github.mergePr(pr)).toBe(true);
       expect(get.put).toHaveBeenCalledTimes(3);
-      expect(get.delete).toHaveBeenCalledTimes(1);
     });
     it('should give up', async () => {
       const pr = {
@@ -1795,354 +1500,6 @@ describe('platform/github', () => {
       });
       expect(await github.mergePr(pr)).toBe(false);
       expect(get.put).toHaveBeenCalledTimes(3);
-      expect(get.delete).toHaveBeenCalledTimes(0);
-    });
-  });
-  describe('getFile()', () => {
-    it('should return the encoded file content', async () => {
-      await initRepo({ repository: 'some/repo', token: 'token' });
-      // getFileList
-      get.mockImplementationOnce(() => ({
-        body: {
-          tree: [
-            {
-              type: 'blob',
-              path: 'package.json',
-            },
-            {
-              type: 'blob',
-              path: 'package-lock.json',
-            },
-          ],
-        },
-      }));
-      get.mockImplementationOnce(() => ({
-        body: {
-          content: Buffer.from('hello world').toString('base64'),
-        },
-      }));
-      const content = await github.getFile('package.json');
-      expect(get.mock.calls).toMatchSnapshot();
-      expect(content).toBe('hello world');
-    });
-    it('should return null if not in file list', async () => {
-      await initRepo({ repository: 'some/repo', token: 'token' });
-      // getFileList
-      get.mockImplementationOnce(() => ({
-        body: {
-          tree: [
-            {
-              type: 'blob',
-              path: 'package.json',
-            },
-            {
-              type: 'blob',
-              path: 'package-lock.json',
-            },
-          ],
-        },
-      }));
-      const content = await github.getFile('.npmrc');
-      expect(content).toBeNull();
-    });
-    it('should return null if GitHub returns a 404', async () => {
-      await initRepo({ repository: 'some/repo', token: 'token' });
-      // getFileList
-      get.mockImplementationOnce(() => ({
-        body: {
-          tree: [
-            {
-              type: 'blob',
-              path: 'package.json',
-            },
-            {
-              type: 'blob',
-              path: 'package-lock.json',
-            },
-          ],
-        },
-      }));
-      get.mockImplementationOnce(() =>
-        Promise.reject({
-          statusCode: 404,
-        })
-      );
-      const content = await github.getFile('package.json');
-      expect(get.mock.calls).toMatchSnapshot();
-      expect(content).toBeNull();
-    });
-    it('should return large file via git API', async () => {
-      await initRepo({ repository: 'some/repo', token: 'token' });
-      // getFileList
-      get.mockImplementationOnce(() => ({
-        body: {
-          tree: [
-            {
-              type: 'blob',
-              path: 'package.json',
-            },
-            {
-              type: 'blob',
-              path: 'package-lock.json',
-            },
-          ],
-        },
-      }));
-      get.mockImplementationOnce(() =>
-        Promise.reject({
-          statusCode: 403,
-          message: 'This API returns blobs up to 1 MB in size, OK?',
-        })
-      );
-      get.mockImplementationOnce(() => ({
-        body: {
-          tree: [
-            {
-              path: 'package-lock.json',
-              sha: 'some-sha',
-            },
-          ],
-        },
-      }));
-      get.mockImplementationOnce(() => ({
-        body: {
-          content: Buffer.from('{"hello":"workd"}').toString('base64'),
-        },
-      }));
-      const content = await github.getFile('package-lock.json');
-      expect(get.mock.calls).toMatchSnapshot();
-      expect(content).toMatchSnapshot();
-    });
-    it('should throw if cannot find large file via git API', async () => {
-      await initRepo({ repository: 'some/repo', token: 'token' });
-      // getFileList
-      get.mockImplementationOnce(() => ({
-        body: {
-          tree: [
-            {
-              type: 'blob',
-              path: 'package.json',
-            },
-            {
-              type: 'blob',
-              path: 'package-lock.json',
-            },
-          ],
-        },
-      }));
-      get.mockImplementationOnce(() =>
-        Promise.reject({
-          statusCode: 403,
-          message: 'This API returns blobs up to 1 MB in size, OK?',
-        })
-      );
-      get.mockImplementationOnce(() => ({
-        body: {
-          tree: [],
-        },
-      }));
-      await expect(github.getFile('package-lock.json')).rejects.toEqual({
-        statusCode: 403,
-        message: 'This API returns blobs up to 1 MB in size, OK?',
-      });
-    });
-    it('should return null if getFile returns nothing', async () => {
-      await initRepo({ repository: 'some/repo', token: 'token' });
-      // getFileList
-      get.mockImplementationOnce(() => ({
-        body: {
-          tree: [
-            {
-              type: 'blob',
-              path: 'package.json',
-            },
-            {
-              type: 'blob',
-              path: 'package-lock.json',
-            },
-          ],
-        },
-      }));
-      get.mockImplementationOnce(() => ({}));
-      const content = await github.getFile('package.json');
-      expect(get.mock.calls).toMatchSnapshot();
-      expect(content).toBeNull();
-    });
-    it('should return propagate unknown errors', async () => {
-      await initRepo({ repository: 'some/repo', token: 'token' });
-      // getFileList
-      get.mockImplementationOnce(() => ({
-        body: {
-          tree: [
-            {
-              type: 'blob',
-              path: 'package.json',
-            },
-            {
-              type: 'blob',
-              path: 'package-lock.json',
-            },
-          ],
-        },
-      }));
-      get.mockImplementationOnce(() => {
-        throw new Error('Something went wrong');
-      });
-      await expect(github.getFile('package.json')).rejects.toThrow(
-        Error('Something went wrong')
-      );
-    });
-  });
-  describe('commitFilesToBranch(branchName, files, message, parentBranch)', () => {
-    beforeEach(async () => {
-      global.gitAuthor = {
-        name: 'Renovate Bot',
-        email: 'bot@renovatebot.com',
-      };
-      await initRepo({
-        repository: 'some/repo',
-      });
-
-      // getBranchCommit
-      get.mockImplementationOnce(() => ({
-        body: {
-          object: {
-            sha: '1111',
-          },
-        },
-      }));
-
-      // getCommitTree
-      get.mockImplementationOnce(() => ({
-        body: {
-          tree: {
-            sha: '2222',
-          },
-        },
-      }));
-
-      // createBlob
-      get.post.mockImplementationOnce(() => ({
-        body: {
-          sha: '3333',
-        },
-      }));
-
-      // createTree
-      get.post.mockImplementationOnce(() => ({
-        body: {
-          sha: '4444',
-        },
-      }));
-
-      // createCommit
-      get.post.mockImplementationOnce(() => ({
-        body: {
-          sha: '5555',
-        },
-      }));
-    });
-    it('should add a new commit to the branch', async () => {
-      // branchExists
-      get.mockImplementationOnce(() => ({
-        body: [
-          {
-            name: 'master',
-          },
-          {
-            name: 'the-branch',
-          },
-        ],
-      }));
-      const files = [
-        {
-          name: 'package.json',
-          contents: 'hello world',
-        },
-      ];
-      await github.commitFilesToBranch(
-        'the-branch',
-        files,
-        'my commit message'
-      );
-      expect(get.mock.calls).toMatchSnapshot();
-      expect(get.post).toHaveBeenCalledTimes(3);
-      expect(get.patch).toHaveBeenCalledTimes(1);
-    });
-    it('should add a commit to a new branch if the branch does not already exist', async () => {
-      // branchExists
-      get.mockImplementationOnce(() => ({
-        body: [
-          {
-            name: 'master',
-          },
-        ],
-      }));
-      const files = [
-        {
-          name: 'package.json',
-          contents: 'hello world',
-        },
-      ];
-      await github.commitFilesToBranch(
-        'the-branch',
-        files,
-        'my other commit message'
-      );
-      expect(get.mock.calls).toMatchSnapshot();
-      expect(get.post).toHaveBeenCalledTimes(4);
-      expect(get.patch).toHaveBeenCalledTimes(0);
-    });
-    it('should parse valid gitAuthor', async () => {
-      // branchExists
-      get.mockImplementationOnce(() => ({
-        body: [
-          {
-            name: 'master',
-          },
-        ],
-      }));
-      const files = [
-        {
-          name: 'package.json',
-          contents: 'hello world',
-        },
-      ];
-      global.gitAuthor = {
-        name: 'Renovate Bot',
-        email: 'bot@renovatebot.com',
-      };
-      await github.commitFilesToBranch(
-        'the-branch',
-        files,
-        'my other commit message'
-      );
-      expect(get.post.mock.calls[2][1].body.author.name).toEqual(
-        'Renovate Bot'
-      );
-      expect(get.post.mock.calls[2][1].body.author.email).toEqual(
-        'bot@renovatebot.com'
-      );
-    });
-  });
-  describe('getCommitMessages()', () => {
-    it('returns commits messages', async () => {
-      await initRepo({
-        repository: 'some/repo',
-        gitAuthor: 'Renovate Bot <bot@renovatebot.com>',
-      });
-      get.mockReturnValueOnce({
-        body: [
-          {
-            commit: { message: 'foo' },
-          },
-          {
-            commit: { message: 'bar' },
-          },
-        ],
-      });
-      const res = await github.getCommitMessages();
-      expect(res).toMatchSnapshot();
     });
   });
   describe('getVulnerabilityAlerts()', () => {
diff --git a/test/platform/github/storage.spec.js b/test/platform/github/storage.spec.js
deleted file mode 100644
index c12b039ca3..0000000000
--- a/test/platform/github/storage.spec.js
+++ /dev/null
@@ -1,25 +0,0 @@
-describe('platform/github/storage', () => {
-  const GithubStorage = require('../../../lib/platform/github/storage');
-  const GitStorage = require('../../../lib/platform/git/storage');
-
-  function getAllPropertyNames(obj) {
-    let props = [];
-    let obj2 = obj;
-
-    while (obj2 != null) {
-      props = props.concat(Object.getOwnPropertyNames(obj2));
-      obj2 = Object.getPrototypeOf(obj2);
-    }
-
-    return props.filter(p => !p.startsWith('_'));
-  }
-
-  it('has same API for git storage', () => {
-    const githubMethods = getAllPropertyNames(new GithubStorage()).sort();
-    const gitMethods = getAllPropertyNames(new GitStorage()).sort();
-    expect(githubMethods).toMatchObject(gitMethods);
-  });
-  it('getRepoStatus exists', async () => {
-    expect((await new GithubStorage()).getRepoStatus()).toEqual({});
-  });
-});
-- 
GitLab