diff --git a/lib/config/templates/buildkite/pr-body.hbs b/lib/config/templates/buildkite/pr-body.hbs
index 966a86d44a6084b7e21478ded1c7cf4fbe13dcbc..343aad7a2e7025ec6a1da3117837292b78883beb 100644
--- a/lib/config/templates/buildkite/pr-body.hbs
+++ b/lib/config/templates/buildkite/pr-body.hbs
@@ -17,7 +17,9 @@ This Pull Request updates buildkite plugin {{#if repositoryUrl}}[{{{depName}}}](
 {{#each releases as |release|}}
 {{#if release.releaseNotes}}
 ### [`v{{{release.version}}}`]({{{release.releaseNotes.url}}})
-
+{{#if release.compare.url}}
+[Compare Source]({{release.compare.url}})
+{{/if}}
 {{{release.releaseNotes.body}}}
 
 ---
@@ -27,22 +29,6 @@ This Pull Request updates buildkite plugin {{#if repositoryUrl}}[{{{depName}}}](
 </details>
 {{/if}}
 
-{{#if hasCommits}}
-
-<details>
-<summary>Commits</summary>
-
-{{#each releases as |release|}}
-{{#if release.hasCommits}}
-#### v{{{release.version}}}
-{{#each release.commits as |commit|}}
--   [`{{commit.shortSha}}`]({{commit.url}}) {{commit.message}}
-{{/each}}
-{{/if}}
-{{/each}}
-
-</details>
-{{/if}}
 {{/if}}
 
 {{#if hasErrors}}
diff --git a/lib/config/templates/default/pr-body.hbs b/lib/config/templates/default/pr-body.hbs
index 9bddc2860a0287b4128d10e278f0625e177847d5..e28fde5a59d18388e4c04eae7113579edcf0e801 100644
--- a/lib/config/templates/default/pr-body.hbs
+++ b/lib/config/templates/default/pr-body.hbs
@@ -20,7 +20,9 @@ This PR also includes an upgrade to the corresponding [@types/{{{depName}}}](htt
 {{#each releases as |release|}}
 {{#if release.releaseNotes}}
 ### [`v{{{release.version}}}`]({{{release.releaseNotes.url}}})
-
+{{#if release.compare.url}}
+[Compare Source]({{release.compare.url}})
+{{/if}}
 {{{release.releaseNotes.body}}}
 
 ---
@@ -30,22 +32,6 @@ This PR also includes an upgrade to the corresponding [@types/{{{depName}}}](htt
 </details>
 {{/if}}
 
-{{#if hasCommits}}
-
-<details>
-<summary>Commits</summary>
-
-{{#each releases as |release|}}
-{{#if release.hasCommits}}
-#### v{{{release.version}}}
-{{#each release.commits as |commit|}}
--   [`{{commit.shortSha}}`]({{commit.url}}) {{commit.message}}
-{{/each}}
-{{/if}}
-{{/each}}
-
-</details>
-{{/if}}
 {{/if}}
 
 {{#if hasErrors}}
diff --git a/lib/config/templates/group/pr-body.hbs b/lib/config/templates/group/pr-body.hbs
index 7bf9ebfe3e2c30cdebed4608af0b1aa8cc33c218..a1b3931848d559c92ea86c4bde06b92323b65bed 100644
--- a/lib/config/templates/group/pr-body.hbs
+++ b/lib/config/templates/group/pr-body.hbs
@@ -18,7 +18,9 @@ This Pull Request renovates the package group "{{{groupName}}}".
 {{#each upgrade.releases as |release|}}
 {{#if release.releaseNotes}}
 ### [`v{{{release.version}}}`]({{{release.releaseNotes.url}}})
-
+{{#if release.compare.url}}
+[Compare Source]({{release.compare.url}})
+{{/if}}
 {{{release.releaseNotes.body}}}
 
 ---
@@ -31,29 +33,6 @@ This Pull Request renovates the package group "{{{groupName}}}".
 {{/each}}
 {{/if}}
 
-{{#if hasCommits}}
-# Commits
-
-{{#each upgrades as |upgrade|}}
-{{#if upgrade.releases.length}}
-<details>
-<summary>{{upgrade.githubName}}</summary>
-{{#each upgrade.releases as |release|}}
-{{#if release.hasCommits}}
-
-#### v{{{release.version}}}
-{{#each release.commits as |commit|}}
--   [`{{commit.shortSha}}`]({{commit.url}}){{commit.message}}
-{{/each}}
-{{/if}}
-{{/each}}
-
-</details>
-
-{{/if}}
-{{/each}}
-{{/if}}
-
 {{#if isPin}}
 **Important**: Renovate will wait until you have merged this Pin request before creating PRs for any *upgrades*. If you do not wish to pin anything, please update your config accordingly instead of leaving this PR open.
 {{/if}}
diff --git a/lib/workers/pr/changelog.js b/lib/workers/pr/changelog.js
deleted file mode 100644
index 0278bccbba13245c725a56464efa844f4d01161a..0000000000000000000000000000000000000000
--- a/lib/workers/pr/changelog.js
+++ /dev/null
@@ -1,83 +0,0 @@
-const os = require('os');
-const changelog = require('changelog');
-const cacache = require('cacache/en');
-const npmRegistry = require('../../datasource/npm');
-const { addReleaseNotes } = require('./release-notes');
-const { matchesSemver } = require('../../util/semver');
-
-module.exports = {
-  getChangeLogJSON,
-};
-
-async function getChangeLogJSON(depName, fromVersion, newVersion) {
-  logger.debug(`getChangeLogJSON(${depName}, ${fromVersion}, ${newVersion})`);
-  if (!fromVersion || fromVersion === newVersion) {
-    return null;
-  }
-  const semverString = `>${fromVersion} <=${newVersion}`;
-  logger.trace(`semverString: ${semverString}`);
-  const cachePath =
-    (process.env.RENOVATE_TMPDIR || os.tmpdir()) + '/renovate-changelog-cache';
-  const cacheKey = `${depName}-${fromVersion}-${newVersion}`;
-
-  // Return from cache if present
-  try {
-    const cacheVal = await cacache.get(cachePath, cacheKey);
-    logger.trace(`Returning cached version of ${depName}`);
-    const cachedResult = JSON.parse(cacheVal.data.toString());
-    return addReleaseNotes(cachedResult);
-  } catch (err) {
-    logger.debug('Cache miss');
-  }
-  let res = null;
-  try {
-    res = await changelog.generate(depName, semverString);
-    if (!res) {
-      logger.info({ depName, fromVersion, newVersion }, 'No changelog found');
-      return null;
-    }
-    // Sort from oldest to newest
-    if (Array.isArray(res.versions)) {
-      res.versions.reverse();
-      res.versions.forEach(version => {
-        if (Array.isArray(version.changes)) {
-          version.changes.reverse();
-        }
-      });
-    }
-    await cacache.put(cachePath, cacheKey, JSON.stringify(res));
-  } catch (err) {
-    logger.debug({ err }, `getChangeLogJSON error`);
-  }
-  if (!res) {
-    logger.debug('Checking for github source URL manually');
-    const dep = await npmRegistry.getDependency(depName);
-    // istanbul ignore if
-    if (
-      dep &&
-      dep.repositoryUrl &&
-      dep.repositoryUrl.startsWith('https://github.com/')
-    ) {
-      logger.info({ url: dep.repositoryUrl }, 'Found github URL manually');
-      const github = dep.repositoryUrl
-        .replace('https://github.com/', '')
-        .replace(/#.*/, '');
-      if (github.split('/').length === 2) {
-        res = {
-          project: {
-            github,
-          },
-          versions: Object.keys(dep.versions)
-            .filter(v => matchesSemver(v, semverString))
-            .map(version => ({ version, changes: [] })),
-        };
-        logger.debug({ res }, 'Manual res');
-      } else {
-        logger.debug('Invalid github URL found');
-      }
-    } else {
-      logger.debug('No repo found manually');
-    }
-  }
-  return addReleaseNotes(res);
-}
diff --git a/lib/workers/pr/changelog/index.js b/lib/workers/pr/changelog/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..bfea732692250ba7c4b2289bec25699fb5d97a2f
--- /dev/null
+++ b/lib/workers/pr/changelog/index.js
@@ -0,0 +1,28 @@
+const { addReleaseNotes } = require('../release-notes');
+
+const sourceCache = require('./source-cache');
+const sourceGithub = require('./source-github');
+
+module.exports = {
+  getChangeLogJSON,
+};
+
+async function getChangeLogJSON(depName, fromVersion, newVersion) {
+  logger.debug(`getChangeLogJSON(${depName}, ${fromVersion}, ${newVersion})`);
+  if (!fromVersion || fromVersion === newVersion) {
+    return null;
+  }
+
+  const args = [depName, fromVersion, newVersion];
+
+  // Return from cache if present
+  let res = await sourceCache.getChangeLogJSON(...args);
+  if (res) {
+    return addReleaseNotes(res);
+  }
+
+  res = await sourceGithub.getChangeLogJSON(...args);
+
+  await sourceCache.setChangeLogJSON(...args, res);
+  return addReleaseNotes(res);
+}
diff --git a/lib/workers/pr/changelog/source-cache.js b/lib/workers/pr/changelog/source-cache.js
new file mode 100644
index 0000000000000000000000000000000000000000..ebcad5f6cd3ab1d23b81b40b7828732a55ca3886
--- /dev/null
+++ b/lib/workers/pr/changelog/source-cache.js
@@ -0,0 +1,38 @@
+const os = require('os');
+const cacache = require('cacache/en');
+
+module.exports = {
+  getChangeLogJSON,
+  setChangeLogJSON,
+  rmAllCache,
+};
+
+function getCache(depName, fromVersion, newVersion) {
+  const tmpdir = process.env.RENOVATE_TMPDIR || os.tmpdir();
+  const cachePath = tmpdir + '/renovate-changelog-cache';
+  const cacheKey = `${depName}-${fromVersion}-${newVersion}`;
+  return [cachePath, cacheKey];
+}
+
+async function getChangeLogJSON(depName, fromVersion, newVersion) {
+  const cache = getCache(depName, fromVersion, newVersion);
+  try {
+    const cacheVal = await cacache.get(...cache);
+    logger.trace(`Returning cached version of ${depName}`);
+    const cachedResult = JSON.parse(cacheVal.data.toString());
+    return cachedResult;
+  } catch (err) {
+    logger.debug('Cache miss');
+    return null;
+  }
+}
+
+async function setChangeLogJSON(depName, fromVersion, newVersion, res) {
+  const cache = getCache(depName, fromVersion, newVersion);
+  await cacache.put(...cache, JSON.stringify(res));
+}
+
+async function rmAllCache() {
+  const cache = getCache();
+  await cacache.rm.all(cache[0]);
+}
diff --git a/lib/workers/pr/changelog/source-github.js b/lib/workers/pr/changelog/source-github.js
new file mode 100644
index 0000000000000000000000000000000000000000..99d70e725de0158a7ab87a9a4879ee4c0362d582
--- /dev/null
+++ b/lib/workers/pr/changelog/source-github.js
@@ -0,0 +1,136 @@
+const npmRegistry = require('../../../datasource/npm');
+const {
+  matchesSemver,
+  isPinnedVersion,
+  semverSort,
+} = require('../../../util/semver');
+const ghGot = require('../../../platform/github/gh-got-wrapper');
+
+module.exports = {
+  getChangeLogJSON,
+};
+
+async function getTags(repository) {
+  try {
+    const versions = {};
+
+    const res = await ghGot(
+      `https://api.github.com/repos/${repository}/tags?per_page=100`,
+      { paginate: true }
+    );
+
+    const tags = (res && res.body) || [];
+
+    if (!tags.length) {
+      logger.debug({ repository }, 'repository has no Github tags');
+    }
+
+    tags.forEach(tag => {
+      const version = isPinnedVersion(tag.name);
+      if (version) {
+        versions[version] = { gitHead: tag.name };
+      }
+    });
+    return versions;
+  } catch (err) {
+    logger.warn(
+      {
+        err,
+        message: err.message,
+        body: err.response ? err.response.body : undefined,
+        repository,
+      },
+      'Failed to fetch Github tags'
+    );
+    return {};
+  }
+}
+
+async function getRepositoryHead(repository, version) {
+  if (version.gitHead) {
+    return version.gitHead;
+  }
+  if (!version.time) {
+    return null;
+  }
+  logger.info({ repository, version }, 'Looking for commit SHA by time');
+  try {
+    const res = await ghGot(
+      `https://api.github.com/repos/${repository}/commits/@{${version.time}}`
+    );
+    const commit = res && res.body;
+    return commit && commit.sha;
+  } catch (err) {
+    logger.debug({ err, repository }, 'Failed to fetch Github commit by time');
+    return null;
+  }
+}
+
+async function getChangeLogJSON(depName, fromVersion, newVersion) {
+  logger.debug('Checking for github source URL manually');
+  const semverString = `>${fromVersion} <=${newVersion}`;
+  logger.trace(`semverString: ${semverString}`);
+  const dep = await npmRegistry.getDependency(depName);
+  if (
+    !(
+      dep &&
+      dep.repositoryUrl &&
+      dep.repositoryUrl.startsWith('https://github.com/')
+    )
+  ) {
+    logger.debug('No repo found manually');
+    return null;
+  }
+  logger.info({ url: dep.repositoryUrl }, 'Found github URL manually');
+  const repository = dep.repositoryUrl
+    .replace('https://github.com/', '')
+    .replace(/#.*/, '');
+  if (repository.split('/').length !== 2) {
+    logger.debug('Invalid github URL found');
+    return null;
+  }
+
+  const tags = await getTags(repository);
+  const releases = Object.keys(dep.versions);
+  releases.sort(semverSort);
+
+  function getHead(name) {
+    return getRepositoryHead(repository, {
+      ...dep.versions[name],
+      ...tags[name],
+    });
+  }
+
+  const versions = [];
+  // compare versions
+  for (let i = 1; i < releases.length; i += 1) {
+    const prev = releases[i - 1];
+    const next = releases[i];
+    if (matchesSemver(next, semverString)) {
+      const version = {
+        version: next,
+        date: dep.versions[next].time,
+        // put empty changes so that existing templates won't break
+        changes: [],
+        compare: {},
+      };
+      const prevHead = await getHead(prev);
+      const nextHead = await getHead(next);
+      if (prevHead && nextHead) {
+        version.compare.url = `https://github.com/${repository}/compare/${prevHead}...${nextHead}`;
+      }
+      versions.unshift(version);
+    }
+  }
+
+  const res = {
+    project: {
+      github: repository,
+      repository: dep.repositoryUrl,
+    },
+    versions,
+  };
+
+  logger.debug({ res }, 'Manual res');
+  return res;
+}
diff --git a/lib/workers/pr/index.js b/lib/workers/pr/index.js
index ab07093e4a62112e440a0bc7bc60f47fc3ceb426..aec92e5a0c5a1305ad57e5f6a96069aeafab358d 100644
--- a/lib/workers/pr/index.js
+++ b/lib/workers/pr/index.js
@@ -144,26 +144,9 @@ async function ensurePr(prConfig) {
         commitRepos.push(upgrade.githubName);
         logJSON.versions.forEach(version => {
           const release = { ...version };
+          // TODO: remove once hasCommits is deprecated
           release.commits = [];
-          if (release.changes) {
-            release.changes.forEach(change => {
-              const commit = { ...change };
-              delete commit.date;
-              commit.shortSha = change.sha.slice(0, 7);
-              commit.url = `${logJSON.project.repository}/commit/${change.sha}`;
-              if (change.message) {
-                [commit.message] = change.message.split('\n');
-                if (!config.isGitHub || config.privateRepo === true) {
-                  commit.message = commit.message.replace(
-                    issueRe,
-                    `$1[#$2](${upgrade.repositoryUrl}/issues/$2)$3`
-                  );
-                }
-              }
-              release.commits.push(commit);
-            });
-          }
-          release.hasCommits = release.commits.length > 0;
+          release.hasCommits = false;
           upgrade.hasCommits = upgrade.hasCommits || release.hasCommits;
           upgrade.releases.push(release);
         });
diff --git a/lib/workers/pr/release-notes.js b/lib/workers/pr/release-notes.js
index 2890e0966e7b752d25c8d6263f52ebca6a315733..acd6ab31b4f22ee0607ec2d4018d89f771343903 100644
--- a/lib/workers/pr/release-notes.js
+++ b/lib/workers/pr/release-notes.js
@@ -172,6 +172,10 @@ async function addReleaseNotes(input) {
       logger.trace('No markdown release notes found for v' + v.version);
       releaseNotes = await getReleaseNotes(input.project.github, v.version);
     }
+    // Small hack to force display of release notes when there is a compare url
+    if (!releaseNotes && v.compare.url) {
+      releaseNotes = { url: v.compare.url };
+    }
     output.versions.push({
       ...v,
       releaseNotes,
diff --git a/package.json b/package.json
index f4eadc206161ced12e32691739391573c6a87c81..b7f31adf73fe6431d9eb2d706f3d3da0a4f66588 100644
--- a/package.json
+++ b/package.json
@@ -52,7 +52,6 @@
     "bunyan": "1.8.12",
     "cacache": "11.0.1",
     "chalk": "2.4.1",
-    "changelog": "1.4.2",
     "changelog-filename-regex": "1.1.1",
     "child-process-promise": "2.2.1",
     "clean-git-ref": "1.0.3",
diff --git a/test/config/__snapshots__/index.spec.js.snap b/test/config/__snapshots__/index.spec.js.snap
index 2a1cb22b6034838092c975b4b4b272bf8500b829..c5bbe680e419ce0f63e50b341f65cf81303bd713 100644
--- a/test/config/__snapshots__/index.spec.js.snap
+++ b/test/config/__snapshots__/index.spec.js.snap
@@ -31,7 +31,7 @@ Object {
       "\\\\.buildkite/.+\\\\.yml$",
     ],
     "managerBranchPrefix": "buildkite-",
-    "prBody": "This Pull Request updates buildkite plugin {{#if repositoryUrl}}[{{{depName}}}]({{{repositoryUrl}}}){{else}}\`{{{depName}}}\`{{/if}} from \`{{{currentVersion}}}\` to \`{{{newVersion}}}\`.\\n\\n{{#if releases.length}}\\n\\n{{#if schedule}}\\n**Note**: This PR was created on a configured schedule (\\"{{{schedule}}}\\"{{#if timezone}} in timezone \`{{{timezone}}}\`{{/if}}) and will not receive updates outside those times.\\n{{/if}}\\n\\n{{#if isPin}}\\n**Important**: Renovate will wait until you have merged this Pin request before creating PRs for any *upgrades*. If you do not wish to pin anything, please update your config accordingly instead of leaving this PR open.\\n{{/if}}\\n{{#if hasReleaseNotes}}\\n\\n<details>\\n<summary>Release Notes</summary>\\n\\n{{#each releases as |release|}}\\n{{#if release.releaseNotes}}\\n### [\`v{{{release.version}}}\`]({{{release.releaseNotes.url}}})\\n\\n{{{release.releaseNotes.body}}}\\n\\n---\\n\\n{{/if}}\\n{{/each}}\\n</details>\\n{{/if}}\\n\\n{{#if hasCommits}}\\n\\n<details>\\n<summary>Commits</summary>\\n\\n{{#each releases as |release|}}\\n{{#if release.hasCommits}}\\n#### v{{{release.version}}}\\n{{#each release.commits as |commit|}}\\n-   [\`{{commit.shortSha}}\`]({{commit.url}}) {{commit.message}}\\n{{/each}}\\n{{/if}}\\n{{/each}}\\n\\n</details>\\n{{/if}}\\n{{/if}}\\n\\n{{#if hasErrors}}\\n\\n---\\n\\n# Errors\\n\\nRenovate encountered some errors when processing your repository, so you are being notified here even if they do not directly apply to this PR.\\n\\n{{#each errors as |error|}}\\n-   \`{{error.depName}}\`: {{error.message}}\\n{{/each}}\\n{{/if}}\\n\\n{{#if hasWarnings}}\\n\\n---\\n\\n# Warnings\\n\\nPlease make sure the following warnings are safe to ignore:\\n\\n{{#each warnings as |warning|}}\\n-   \`{{warning.depName}}\`: {{warning.message}}\\n{{/each}}\\n{{/if}}",
+    "prBody": "This Pull Request updates buildkite plugin {{#if repositoryUrl}}[{{{depName}}}]({{{repositoryUrl}}}){{else}}\`{{{depName}}}\`{{/if}} from \`{{{currentVersion}}}\` to \`{{{newVersion}}}\`.\\n\\n{{#if releases.length}}\\n\\n{{#if schedule}}\\n**Note**: This PR was created on a configured schedule (\\"{{{schedule}}}\\"{{#if timezone}} in timezone \`{{{timezone}}}\`{{/if}}) and will not receive updates outside those times.\\n{{/if}}\\n\\n{{#if isPin}}\\n**Important**: Renovate will wait until you have merged this Pin request before creating PRs for any *upgrades*. If you do not wish to pin anything, please update your config accordingly instead of leaving this PR open.\\n{{/if}}\\n{{#if hasReleaseNotes}}\\n\\n<details>\\n<summary>Release Notes</summary>\\n\\n{{#each releases as |release|}}\\n{{#if release.releaseNotes}}\\n### [\`v{{{release.version}}}\`]({{{release.releaseNotes.url}}})\\n{{#if release.compare.url}}\\n[Compare Source]({{release.compare.url}})\\n{{/if}}\\n{{{release.releaseNotes.body}}}\\n\\n---\\n\\n{{/if}}\\n{{/each}}\\n</details>\\n{{/if}}\\n\\n{{/if}}\\n\\n{{#if hasErrors}}\\n\\n---\\n\\n# Errors\\n\\nRenovate encountered some errors when processing your repository, so you are being notified here even if they do not directly apply to this PR.\\n\\n{{#each errors as |error|}}\\n-   \`{{error.depName}}\`: {{error.message}}\\n{{/each}}\\n{{/if}}\\n\\n{{#if hasWarnings}}\\n\\n---\\n\\n# Warnings\\n\\nPlease make sure the following warnings are safe to ignore:\\n\\n{{#each warnings as |warning|}}\\n-   \`{{warning.depName}}\`: {{warning.message}}\\n{{/each}}\\n{{/if}}",
   },
   "bumpVersion": null,
   "circleci": Object {
@@ -107,7 +107,7 @@ Object {
   "group": Object {
     "branchTopic": "{{{groupSlug}}}",
     "commitMessageTopic": "{{{groupName}}}",
-    "prBody": "This Pull Request renovates the package group \\"{{{groupName}}}\\".\\n\\n{{#if schedule}}\\n**Note**: This PR was created on a configured schedule (\\"{{{schedule}}}\\"{{#if timezone}} in timezone \`{{{timezone}}}\`{{/if}}) and will not receive updates outside those times.\\n{{/if}}\\n\\n{{#each upgrades as |upgrade|}}\\n-   {{#if repositoryUrl}}[{{{upgrade.depName}}}]({{upgrade.repositoryUrl}}){{else}}\`{{{depName}}}\`{{/if}} (\`{{{depType}}}\`): from \`{{{upgrade.currentVersion}}}\` to \`{{{upgrade.newVersion}}}\`\\n{{/each}}\\n\\n{{#if hasReleaseNotes}}\\n# Release Notes\\n{{#each upgrades as |upgrade|}}\\n{{#if upgrade.hasReleaseNotes}}\\n<details>\\n<summary>{{upgrade.githubName}}</summary>\\n\\n{{#each upgrade.releases as |release|}}\\n{{#if release.releaseNotes}}\\n### [\`v{{{release.version}}}\`]({{{release.releaseNotes.url}}})\\n\\n{{{release.releaseNotes.body}}}\\n\\n---\\n\\n{{/if}}\\n{{/each}}\\n\\n</details>\\n{{/if}}\\n{{/each}}\\n{{/if}}\\n\\n{{#if hasCommits}}\\n# Commits\\n\\n{{#each upgrades as |upgrade|}}\\n{{#if upgrade.releases.length}}\\n<details>\\n<summary>{{upgrade.githubName}}</summary>\\n{{#each upgrade.releases as |release|}}\\n{{#if release.hasCommits}}\\n\\n#### v{{{release.version}}}\\n{{#each release.commits as |commit|}}\\n-   [\`{{commit.shortSha}}\`]({{commit.url}}){{commit.message}}\\n{{/each}}\\n{{/if}}\\n{{/each}}\\n\\n</details>\\n\\n{{/if}}\\n{{/each}}\\n{{/if}}\\n\\n{{#if isPin}}\\n**Important**: Renovate will wait until you have merged this Pin request before creating PRs for any *upgrades*. If you do not wish to pin anything, please update your config accordingly instead of leaving this PR open.\\n{{/if}}\\n\\n{{#if hasErrors}}\\n\\n---\\n\\n# Errors\\n\\nRenovate encountered some errors when processing your repository, so you are being notified here even if they do not directly apply to this PR.\\n\\n{{#each errors as |error|}}\\n-   \`{{error.depName}}\`: {{error.message}}\\n{{/each}}\\n{{/if}}\\n\\n{{#if hasWarnings}}\\n\\n---\\n\\n# Warnings\\n\\nPlease make sure the following warnings are safe to ignore:\\n\\n{{#each warnings as |warning|}}\\n-   \`{{warning.depName}}\`: {{warning.message}}\\n{{/each}}\\n{{/if}}",
+    "prBody": "This Pull Request renovates the package group \\"{{{groupName}}}\\".\\n\\n{{#if schedule}}\\n**Note**: This PR was created on a configured schedule (\\"{{{schedule}}}\\"{{#if timezone}} in timezone \`{{{timezone}}}\`{{/if}}) and will not receive updates outside those times.\\n{{/if}}\\n\\n{{#each upgrades as |upgrade|}}\\n-   {{#if repositoryUrl}}[{{{upgrade.depName}}}]({{upgrade.repositoryUrl}}){{else}}\`{{{depName}}}\`{{/if}} (\`{{{depType}}}\`): from \`{{{upgrade.currentVersion}}}\` to \`{{{upgrade.newVersion}}}\`\\n{{/each}}\\n\\n{{#if hasReleaseNotes}}\\n# Release Notes\\n{{#each upgrades as |upgrade|}}\\n{{#if upgrade.hasReleaseNotes}}\\n<details>\\n<summary>{{upgrade.githubName}}</summary>\\n\\n{{#each upgrade.releases as |release|}}\\n{{#if release.releaseNotes}}\\n### [\`v{{{release.version}}}\`]({{{release.releaseNotes.url}}})\\n{{#if release.compare.url}}\\n[Compare Source]({{release.compare.url}})\\n{{/if}}\\n{{{release.releaseNotes.body}}}\\n\\n---\\n\\n{{/if}}\\n{{/each}}\\n\\n</details>\\n{{/if}}\\n{{/each}}\\n{{/if}}\\n\\n{{#if isPin}}\\n**Important**: Renovate will wait until you have merged this Pin request before creating PRs for any *upgrades*. If you do not wish to pin anything, please update your config accordingly instead of leaving this PR open.\\n{{/if}}\\n\\n{{#if hasErrors}}\\n\\n---\\n\\n# Errors\\n\\nRenovate encountered some errors when processing your repository, so you are being notified here even if they do not directly apply to this PR.\\n\\n{{#each errors as |error|}}\\n-   \`{{error.depName}}\`: {{error.message}}\\n{{/each}}\\n{{/if}}\\n\\n{{#if hasWarnings}}\\n\\n---\\n\\n# Warnings\\n\\nPlease make sure the following warnings are safe to ignore:\\n\\n{{#each warnings as |warning|}}\\n-   \`{{warning.depName}}\`: {{warning.message}}\\n{{/each}}\\n{{/if}}",
   },
   "groupName": null,
   "groupSlug": null,
@@ -195,7 +195,7 @@ Object {
     ],
   },
   "platform": "github",
-  "prBody": "This Pull Request {{#if isRollback}}rolls back{{else}}updates{{/if}} dependency {{#if repositoryUrl}}[{{{depName}}}]({{{repositoryUrl}}}){{else}}\`{{{depName}}}\`{{/if}} from \`{{#unless isRange}}{{#unless isPin}}v{{/unless}}{{/unless}}{{{currentVersion}}}\` to \`{{#unless isRange}}v{{/unless}}{{{newVersion}}}\`{{#if isRollback}}. This is necessary and important because \`v{{{currentVersion}}}\` cannot be found in the npm registry - probably because of it being unpublished.{{/if}}\\n{{#if hasTypes}}\\n\\nThis PR also includes an upgrade to the corresponding [@types/{{{depName}}}](https://npmjs.com/package/@types/{{{depName}}}) package.\\n{{/if}}\\n{{#if releases.length}}\\n\\n{{#if schedule}}\\n**Note**: This PR was created on a configured schedule (\\"{{{schedule}}}\\"{{#if timezone}} in timezone \`{{{timezone}}}\`{{/if}}) and will not receive updates outside those times.\\n{{/if}}\\n\\n{{#if isPin}}\\n**Important**: Renovate will wait until you have merged this Pin request before creating PRs for any *upgrades*. If you do not wish to pin anything, please update your config accordingly instead of leaving this PR open.\\n{{/if}}\\n{{#if hasReleaseNotes}}\\n\\n<details>\\n<summary>Release Notes</summary>\\n\\n{{#each releases as |release|}}\\n{{#if release.releaseNotes}}\\n### [\`v{{{release.version}}}\`]({{{release.releaseNotes.url}}})\\n\\n{{{release.releaseNotes.body}}}\\n\\n---\\n\\n{{/if}}\\n{{/each}}\\n</details>\\n{{/if}}\\n\\n{{#if hasCommits}}\\n\\n<details>\\n<summary>Commits</summary>\\n\\n{{#each releases as |release|}}\\n{{#if release.hasCommits}}\\n#### v{{{release.version}}}\\n{{#each release.commits as |commit|}}\\n-   [\`{{commit.shortSha}}\`]({{commit.url}}) {{commit.message}}\\n{{/each}}\\n{{/if}}\\n{{/each}}\\n\\n</details>\\n{{/if}}\\n{{/if}}\\n\\n{{#if hasErrors}}\\n\\n---\\n\\n# Errors\\n\\nRenovate encountered some errors when processing your repository, so you are being notified here even if they do not directly apply to this PR.\\n\\n{{#each errors as |error|}}\\n-   \`{{error.depName}}\`: {{error.message}}\\n{{/each}}\\n{{/if}}\\n\\n{{#if hasWarnings}}\\n\\n---\\n\\n# Warnings\\n\\nPlease make sure the following warnings are safe to ignore:\\n\\n{{#each warnings as |warning|}}\\n-   \`{{warning.depName}}\`: {{warning.message}}\\n{{/each}}\\n{{/if}}",
+  "prBody": "This Pull Request {{#if isRollback}}rolls back{{else}}updates{{/if}} dependency {{#if repositoryUrl}}[{{{depName}}}]({{{repositoryUrl}}}){{else}}\`{{{depName}}}\`{{/if}} from \`{{#unless isRange}}{{#unless isPin}}v{{/unless}}{{/unless}}{{{currentVersion}}}\` to \`{{#unless isRange}}v{{/unless}}{{{newVersion}}}\`{{#if isRollback}}. This is necessary and important because \`v{{{currentVersion}}}\` cannot be found in the npm registry - probably because of it being unpublished.{{/if}}\\n{{#if hasTypes}}\\n\\nThis PR also includes an upgrade to the corresponding [@types/{{{depName}}}](https://npmjs.com/package/@types/{{{depName}}}) package.\\n{{/if}}\\n{{#if releases.length}}\\n\\n{{#if schedule}}\\n**Note**: This PR was created on a configured schedule (\\"{{{schedule}}}\\"{{#if timezone}} in timezone \`{{{timezone}}}\`{{/if}}) and will not receive updates outside those times.\\n{{/if}}\\n\\n{{#if isPin}}\\n**Important**: Renovate will wait until you have merged this Pin request before creating PRs for any *upgrades*. If you do not wish to pin anything, please update your config accordingly instead of leaving this PR open.\\n{{/if}}\\n{{#if hasReleaseNotes}}\\n\\n<details>\\n<summary>Release Notes</summary>\\n\\n{{#each releases as |release|}}\\n{{#if release.releaseNotes}}\\n### [\`v{{{release.version}}}\`]({{{release.releaseNotes.url}}})\\n{{#if release.compare.url}}\\n[Compare Source]({{release.compare.url}})\\n{{/if}}\\n{{{release.releaseNotes.body}}}\\n\\n---\\n\\n{{/if}}\\n{{/each}}\\n</details>\\n{{/if}}\\n\\n{{/if}}\\n\\n{{#if hasErrors}}\\n\\n---\\n\\n# Errors\\n\\nRenovate encountered some errors when processing your repository, so you are being notified here even if they do not directly apply to this PR.\\n\\n{{#each errors as |error|}}\\n-   \`{{error.depName}}\`: {{error.message}}\\n{{/each}}\\n{{/if}}\\n\\n{{#if hasWarnings}}\\n\\n---\\n\\n# Warnings\\n\\nPlease make sure the following warnings are safe to ignore:\\n\\n{{#each warnings as |warning|}}\\n-   \`{{warning.depName}}\`: {{warning.message}}\\n{{/each}}\\n{{/if}}",
   "prConcurrentLimit": 0,
   "prCreation": "immediate",
   "prFooter": "This PR has been generated by [Renovate Bot](https://renovatebot.com).",
diff --git a/test/workers/pr/__snapshots__/changelog.spec.js.snap b/test/workers/pr/__snapshots__/changelog.spec.js.snap
index 268593f10c07ef52a1e2a19d15a3d8bd22bb57c4..fbee82d429c2785b20843376ba56b090c10bf1ae 100644
--- a/test/workers/pr/__snapshots__/changelog.spec.js.snap
+++ b/test/workers/pr/__snapshots__/changelog.spec.js.snap
@@ -1,67 +1,201 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`workers/pr/changelog getChangeLogJSON sorts JSON 1`] = `
+exports[`workers/pr/changelog getChangeLogJSON falls back to commit from release time 1`] = `
 Object {
-  "hasReleaseNotes": false,
+  "hasReleaseNotes": true,
   "project": Object {
     "github": "chalk/chalk",
     "repository": "https://github.com/chalk/chalk",
   },
   "versions": Array [
     Object {
-      "changes": Array [
-        Object {
-          "date": "2017-10-22T08:20:23.000Z",
-          "message": "Add Awesome mentioned badge",
-          "sha": "4372d27f7eb887c4d33cdca1f9484f321ceab3dd",
-        },
-        Object {
-          "date": "2017-10-24T02:44:46.000Z",
-          "message": "Add .visible for emitting text only when enabled (fixes #192)",
-          "sha": "dc092b4a5f5ca77dd1e22607cdf2fdd388803064",
-        },
-        Object {
-          "date": "2017-10-24T02:46:10.000Z",
-          "message": "2.2.1",
-          "sha": "6adf5794a38552923ea474c4b60c372ef0582035",
-        },
-        Object {
-          "date": "2017-10-24T03:12:16.000Z",
-          "message": "add failing test for .visible bug",
-          "sha": "ede310303b9893146bd7cc24261a50e3b47c633a",
-        },
-        Object {
-          "date": "2017-10-24T03:12:34.000Z",
-          "message": "fix .visible when called after .enable is set to false",
-          "sha": "e2a4aa427568ff1c5d649739c4d1f8319cf0d072",
-        },
-        Object {
-          "date": "2017-10-24T03:15:51.000Z",
-          "message": "2.2.2",
-          "sha": "e1177ec3628f6d0d37489c1e1accd2c389a376a8",
-        },
-      ],
+      "changes": Array [],
+      "compare": Object {},
+      "date": undefined,
+      "releaseNotes": undefined,
+      "version": "2.5.2",
+    },
+    Object {
+      "changes": Array [],
+      "compare": Object {
+        "url": "https://github.com/chalk/chalk/compare/npm_2.3.0...sha_from_time",
+      },
+      "date": "2017-12-24T03:20:46.238Z",
+      "releaseNotes": Object {
+        "url": "https://github.com/chalk/chalk/compare/npm_2.3.0...sha_from_time",
+      },
+      "version": "2.4.2",
+    },
+    Object {
+      "changes": Array [],
+      "compare": Object {
+        "url": "https://github.com/chalk/chalk/compare/npm_2.2.2...npm_2.3.0",
+      },
       "date": "2017-10-24T03:20:46.238Z",
+      "releaseNotes": Object {
+        "url": "https://github.com/chalk/chalk/compare/npm_2.2.2...npm_2.3.0",
+      },
+      "version": "2.3.0",
+    },
+    Object {
+      "changes": Array [],
+      "compare": Object {
+        "url": "https://github.com/chalk/chalk/compare/npm_1.0.0...npm_2.2.2",
+      },
+      "date": undefined,
+      "releaseNotes": Object {
+        "url": "https://github.com/chalk/chalk/compare/npm_1.0.0...npm_2.2.2",
+      },
+      "version": "2.2.2",
+    },
+  ],
+}
+`;
+
+exports[`workers/pr/changelog getChangeLogJSON filters unnecessary warns 1`] = `
+Object {
+  "hasReleaseNotes": true,
+  "project": Object {
+    "github": "chalk/chalk",
+    "repository": "https://github.com/chalk/chalk",
+  },
+  "versions": Array [
+    Object {
+      "changes": Array [],
+      "compare": Object {},
+      "date": undefined,
       "releaseNotes": undefined,
+      "version": "2.5.2",
+    },
+    Object {
+      "changes": Array [],
+      "compare": Object {},
+      "date": "2017-12-24T03:20:46.238Z",
+      "releaseNotes": undefined,
+      "version": "2.4.2",
+    },
+    Object {
+      "changes": Array [],
+      "compare": Object {
+        "url": "https://github.com/chalk/chalk/compare/npm_2.2.2...npm_2.3.0",
+      },
+      "date": "2017-10-24T03:20:46.238Z",
+      "releaseNotes": Object {
+        "url": "https://github.com/chalk/chalk/compare/npm_2.2.2...npm_2.3.0",
+      },
+      "version": "2.3.0",
+    },
+    Object {
+      "changes": Array [],
+      "compare": Object {
+        "url": "https://github.com/chalk/chalk/compare/npm_1.0.0...npm_2.2.2",
+      },
+      "date": undefined,
+      "releaseNotes": Object {
+        "url": "https://github.com/chalk/chalk/compare/npm_1.0.0...npm_2.2.2",
+      },
       "version": "2.2.2",
     },
+  ],
+}
+`;
+
+exports[`workers/pr/changelog getChangeLogJSON uses GitHub tags 1`] = `
+Object {
+  "hasReleaseNotes": true,
+  "project": Object {
+    "github": "chalk/chalk",
+    "repository": "https://github.com/chalk/chalk",
+  },
+  "versions": Array [
     Object {
-      "changes": Array [
-        Object {
-          "date": "2017-10-24T04:02:36.000Z",
-          "message": "TypeScript fixes (#217)",
-          "sha": "7be154c074026f77b99e7d854b3a4cdd5e4ae502",
-        },
-        Object {
-          "date": "2017-10-24T04:12:53.000Z",
-          "message": "2.3.0",
-          "sha": "14e0aa97727019b22f0a003fdc631aeec5e2e24c",
-        },
-      ],
-      "date": "2017-10-24T04:12:55.953Z",
+      "changes": Array [],
+      "compare": Object {},
+      "date": undefined,
       "releaseNotes": undefined,
+      "version": "2.5.2",
+    },
+    Object {
+      "changes": Array [],
+      "compare": Object {
+        "url": "https://github.com/chalk/chalk/compare/v2.3.0...v2.4.2",
+      },
+      "date": "2017-12-24T03:20:46.238Z",
+      "releaseNotes": Object {
+        "url": "https://github.com/chalk/chalk/compare/v2.3.0...v2.4.2",
+      },
+      "version": "2.4.2",
+    },
+    Object {
+      "changes": Array [],
+      "compare": Object {
+        "url": "https://github.com/chalk/chalk/compare/2.2.2...v2.3.0",
+      },
+      "date": "2017-10-24T03:20:46.238Z",
+      "releaseNotes": Object {
+        "url": "https://github.com/chalk/chalk/compare/2.2.2...v2.3.0",
+      },
       "version": "2.3.0",
     },
+    Object {
+      "changes": Array [],
+      "compare": Object {
+        "url": "https://github.com/chalk/chalk/compare/1.0.0...2.2.2",
+      },
+      "date": undefined,
+      "releaseNotes": Object {
+        "url": "https://github.com/chalk/chalk/compare/1.0.0...2.2.2",
+      },
+      "version": "2.2.2",
+    },
+  ],
+}
+`;
+
+exports[`workers/pr/changelog getChangeLogJSON works without Github 1`] = `
+Object {
+  "hasReleaseNotes": true,
+  "project": Object {
+    "github": "chalk/chalk",
+    "repository": "https://github.com/chalk/chalk",
+  },
+  "versions": Array [
+    Object {
+      "changes": Array [],
+      "compare": Object {},
+      "date": undefined,
+      "releaseNotes": undefined,
+      "version": "2.5.2",
+    },
+    Object {
+      "changes": Array [],
+      "compare": Object {},
+      "date": "2017-12-24T03:20:46.238Z",
+      "releaseNotes": undefined,
+      "version": "2.4.2",
+    },
+    Object {
+      "changes": Array [],
+      "compare": Object {
+        "url": "https://github.com/chalk/chalk/compare/npm_2.2.2...npm_2.3.0",
+      },
+      "date": "2017-10-24T03:20:46.238Z",
+      "releaseNotes": Object {
+        "url": "https://github.com/chalk/chalk/compare/npm_2.2.2...npm_2.3.0",
+      },
+      "version": "2.3.0",
+    },
+    Object {
+      "changes": Array [],
+      "compare": Object {
+        "url": "https://github.com/chalk/chalk/compare/npm_1.0.0...npm_2.2.2",
+      },
+      "date": undefined,
+      "releaseNotes": Object {
+        "url": "https://github.com/chalk/chalk/compare/npm_1.0.0...npm_2.2.2",
+      },
+      "version": "2.2.2",
+    },
   ],
 }
 `;
diff --git a/test/workers/pr/__snapshots__/index.spec.js.snap b/test/workers/pr/__snapshots__/index.spec.js.snap
index e48bde230c6f8c95dcefc7bb3853c9bbeafa3523..9580360786af5ca003b0a94e2d71b79a8de80cf6 100644
--- a/test/workers/pr/__snapshots__/index.spec.js.snap
+++ b/test/workers/pr/__snapshots__/index.spec.js.snap
@@ -31,11 +31,10 @@ Array [
   "renovate/dummy-1.x",
   "Update dependency dummy to v1.1.0",
   "<p>This Merge Request updates dependency <a href=\\"https://github.com/renovateapp/dummy\\">dummy</a> from <code>v1.0.0</code> to <code>v1.1.0</code></p>
-<h1>Commits</h1>
-**v1.1.0**
-<ul>
-<li><a href=\\"https://github.com/renovateapp/dummy/commit/abcdefghijklmnopqrstuvwxyz\\"><code>abcdefg</code></a> foo <a href=\\"https://github.com/renovateapp/dummy/issues/3\\">#3</a></li>
-</ul>
+<h1>Release Notes</h1>
+<h3 id=\\"v110httpsgithubcomrenovateappdummycomparev100v110\\"><a href=\\"https://github.com/renovateapp/dummy/compare/v1.0.0…v1.1.0\\"><code>v1.1.0</code></a></h3>
+<p><a href=\\"https://github.com/renovateapp/dummy/compare/v1.0.0…v1.1.0\\">Compare Source</a></p>
+<hr />
 <p></details></p>",
   Array [],
   false,
@@ -51,12 +50,14 @@ Array [
 
 
 
-
 <details>
-<summary>Commits</summary>
+<summary>Release Notes</summary>
+
+### [\`v1.1.0\`](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)
+[Compare Source](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)
 
-#### v1.1.0
--   [\`abcdefg\`](https://github.com/renovateapp/dummy/commit/abcdefghijklmnopqrstuvwxyz) foo [#3](https://github.com/renovateapp/dummy/issues/3)
+
+---
 
 </details>",
   Array [],
@@ -73,12 +74,14 @@ Array [
 
 
 
-
 <details>
-<summary>Commits</summary>
+<summary>Release Notes</summary>
 
-#### v1.1.0
--   [\`abcdefg\`](https://github.com/renovateapp/dummy/commit/abcdefghijklmnopqrstuvwxyz) foo #&#8203;3
+### [\`v1.1.0\`](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)
+[Compare Source](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)
+
+
+---
 
 </details>",
   Array [],
@@ -93,12 +96,14 @@ Object {
 
 
 
-
 <details>
-<summary>Commits</summary>
+<summary>Release Notes</summary>
 
-#### v1.1.0
--   [\`abcdefg\`](https://github.com/renovateapp/dummy/commit/abcdefghijklmnopqrstuvwxyz) foo [#3](https://github.com/renovateapp/dummy/issues/3)
+### [\`v1.1.0\`](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)
+[Compare Source](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)
+
+
+---
 
 </details>",
   "displayNumber": "Existing PR",
@@ -112,12 +117,14 @@ Object {
 
 
 
-
 <details>
-<summary>Commits</summary>
+<summary>Release Notes</summary>
 
-#### v1.1.0
--   [\`abcdefg\`](https://github.com/renovateapp/dummy/commit/abcdefghijklmnopqrstuvwxyz) foo [#3](https://github.com/renovateapp/dummy/issues/3)
+### [\`v1.1.0\`](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)
+[Compare Source](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)
+
+
+---
 
 </details>",
   "displayNumber": "Existing PR",
@@ -136,11 +143,13 @@ Array [
 
 
 
+**Release Notes**
+
+### [\`v1.1.0\`](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)
+[Compare Source](https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0)
 
-**Commits**
 
-#### v1.1.0
--   [\`abcdefg\`](https://github.com/renovateapp/dummy/commit/abcdefghijklmnopqrstuvwxyz) foo [#3](https://github.com/renovateapp/dummy/issues/3)
+---
 
 ",
   Array [],
diff --git a/test/workers/pr/changelog.spec.js b/test/workers/pr/changelog.spec.js
index 2aa9bd1e8deb64da7d40e318b275e735f41e663d..d2f62e88307c7500fe19c9216d298f1c139b5a66 100644
--- a/test/workers/pr/changelog.spec.js
+++ b/test/workers/pr/changelog.spec.js
@@ -1,112 +1,111 @@
-const changelog = require('changelog');
-const changelogHelper = require('../../../lib/workers/pr/changelog');
-
-jest.mock('changelog');
+jest.mock('../../../lib/platform/github/gh-got-wrapper');
 jest.mock('../../../lib/datasource/npm');
 
+const ghGot = require('../../../lib/platform/github/gh-got-wrapper');
+const npmRegistry = require('../../../lib/datasource/npm');
+
+const { getChangeLogJSON } = require('../../../lib/workers/pr/changelog');
+const {
+  rmAllCache,
+} = require('../../../lib/workers/pr/changelog/source-cache');
+
+function npmResponse() {
+  return {
+    repositoryUrl: 'https://github.com/chalk/chalk',
+    versions: {
+      '0.9.0': {},
+      '1.0.0': { gitHead: 'npm_1.0.0' },
+      '2.3.0': { gitHead: 'npm_2.3.0', time: '2017-10-24T03:20:46.238Z' },
+      '2.2.2': { gitHead: 'npm_2.2.2' },
+      '2.4.2': { time: '2017-12-24T03:20:46.238Z' },
+      '2.5.2': {},
+    },
+  };
+}
+
 describe('workers/pr/changelog', () => {
   describe('getChangeLogJSON', () => {
+    beforeEach(async () => {
+      npmRegistry.getDependency.mockClear();
+      ghGot.mockClear();
+
+      npmRegistry.getDependency.mockReturnValueOnce(
+        Promise.resolve(npmResponse())
+      );
+
+      await rmAllCache();
+    });
     it('returns null if no fromVersion', async () => {
-      expect(
-        await changelogHelper.getChangeLogJSON('renovate', null, '1.0.0')
-      ).toBe(null);
+      expect(await getChangeLogJSON('renovate', null, '1.0.0')).toBe(null);
+      expect(npmRegistry.getDependency.mock.calls).toHaveLength(0);
+      expect(ghGot.mock.calls).toHaveLength(0);
     });
     it('returns null if fromVersion equals newVersion', async () => {
-      expect(
-        await changelogHelper.getChangeLogJSON('renovate', '1.0.0', '1.0.0')
-      ).toBe(null);
+      expect(await getChangeLogJSON('renovate', '1.0.0', '1.0.0')).toBe(null);
+      expect(ghGot.mock.calls).toHaveLength(0);
     });
     it('logs when no JSON', async () => {
-      changelog.generate = jest.fn(() => null);
+      // clear the mock
+      npmRegistry.getDependency.mockReset();
+      expect(await getChangeLogJSON('renovate', '1.0.0', '3.0.0')).toBe(null);
+    });
+    it('skips invalid repos', async () => {
+      // clear the mock
+      npmRegistry.getDependency.mockReset();
+      const res = npmResponse();
+      res.repositoryUrl = 'https://github.com/about';
+      npmRegistry.getDependency.mockReturnValueOnce(Promise.resolve(res));
+
+      expect(await getChangeLogJSON('renovate', '1.0.0', '3.0.0')).toBe(null);
+    });
+    it('works without Github', async () => {
       expect(
-        await changelogHelper.getChangeLogJSON('renovate', '1.0.0', '1.5.0')
-      ).toBe(null);
+        await getChangeLogJSON('renovate', '1.0.0', '3.0.0')
+      ).toMatchSnapshot();
     });
-    it('returns JSON', async () => {
-      changelog.generate = jest.fn(() => ({ a: 1 }));
+    it('uses GitHub tags', async () => {
+      ghGot.mockReturnValueOnce(
+        Promise.resolve({
+          body: [
+            { name: '0.9.0' },
+            { name: '1.0.0' },
+            { name: '1.4.0' },
+            { name: 'v2.3.0' },
+            { name: '2.2.2' },
+            { name: 'v2.4.2' },
+          ],
+        })
+      );
       expect(
-        await changelogHelper.getChangeLogJSON('renovate', '1.0.0', '2.0.0')
-      ).toMatchObject({ a: 1 });
+        await getChangeLogJSON('renovate', '1.0.0', '3.0.0')
+      ).toMatchSnapshot();
     });
-    it('returns cached JSON', async () => {
-      changelog.generate = jest.fn(() => ({ a: 2 }));
+    it('falls back to commit from release time', async () => {
+      // mock tags response
+      ghGot.mockReturnValueOnce(Promise.resolve());
+      // mock commit time response
+      ghGot.mockReturnValue(
+        Promise.resolve({
+          body: { sha: 'sha_from_time' },
+        })
+      );
       expect(
-        await changelogHelper.getChangeLogJSON('renovate', '1.0.0', '2.0.0')
-      ).toMatchObject({ a: 1 });
+        await getChangeLogJSON('@renovate/no', '1.0.0', '3.0.0')
+      ).toMatchSnapshot();
+    });
+    it('returns cached JSON', async () => {
+      const first = await getChangeLogJSON('renovate', '1.0.0', '3.0.0');
+      npmRegistry.getDependency.mockClear();
+      const second = await getChangeLogJSON('renovate', '1.0.0', '3.0.0');
+      expect(first).toEqual(second);
+      expect(npmRegistry.getDependency.mock.calls).toHaveLength(0);
     });
     it('filters unnecessary warns', async () => {
-      changelog.generate = jest.fn(() => {
+      ghGot.mockImplementation(() => {
         throw new Error('Unknown Github Repo');
       });
       expect(
-        await changelogHelper.getChangeLogJSON('@renovate/no', '1.0.0', '3.0.0')
-      ).toBe(null);
-    });
-    it('sorts JSON', async () => {
-      changelog.generate = jest.fn(() => ({
-        project: {
-          github: 'chalk/chalk',
-          repository: 'https://github.com/chalk/chalk',
-        },
-        versions: [
-          {
-            version: '2.3.0',
-            date: '2017-10-24T04:12:55.953Z',
-            changes: [
-              {
-                sha: '14e0aa97727019b22f0a003fdc631aeec5e2e24c',
-                date: '2017-10-24T04:12:53.000Z',
-                message: '2.3.0',
-              },
-              {
-                sha: '7be154c074026f77b99e7d854b3a4cdd5e4ae502',
-                date: '2017-10-24T04:02:36.000Z',
-                message: 'TypeScript fixes (#217)',
-              },
-            ],
-          },
-          {
-            version: '2.2.2',
-            date: '2017-10-24T03:20:46.238Z',
-            changes: [
-              {
-                sha: 'e1177ec3628f6d0d37489c1e1accd2c389a376a8',
-                date: '2017-10-24T03:15:51.000Z',
-                message: '2.2.2',
-              },
-              {
-                sha: 'e2a4aa427568ff1c5d649739c4d1f8319cf0d072',
-                date: '2017-10-24T03:12:34.000Z',
-                message:
-                  'fix .visible when called after .enable is set to false',
-              },
-              {
-                sha: 'ede310303b9893146bd7cc24261a50e3b47c633a',
-                date: '2017-10-24T03:12:16.000Z',
-                message: 'add failing test for .visible bug',
-              },
-              {
-                sha: '6adf5794a38552923ea474c4b60c372ef0582035',
-                date: '2017-10-24T02:46:10.000Z',
-                message: '2.2.1',
-              },
-              {
-                sha: 'dc092b4a5f5ca77dd1e22607cdf2fdd388803064',
-                date: '2017-10-24T02:44:46.000Z',
-                message:
-                  'Add .visible for emitting text only when enabled (fixes #192)',
-              },
-              {
-                sha: '4372d27f7eb887c4d33cdca1f9484f321ceab3dd',
-                date: '2017-10-22T08:20:23.000Z',
-                message: 'Add Awesome mentioned badge',
-              },
-            ],
-          },
-        ],
-      }));
-      expect(
-        await changelogHelper.getChangeLogJSON('chalk', '2.2.2', '2.3.0')
+        await getChangeLogJSON('@renovate/no', '1.0.0', '3.0.0')
       ).toMatchSnapshot();
     });
   });
diff --git a/test/workers/pr/index.spec.js b/test/workers/pr/index.spec.js
index c321185e9665679d8a2a2cfecbe1cfa6900b4ed1..18ea14a6695648ab9ab4460ab4e7c1e8bb0cb42b 100644
--- a/test/workers/pr/index.spec.js
+++ b/test/workers/pr/index.spec.js
@@ -9,6 +9,7 @@ changelogHelper.getChangeLogJSON.mockReturnValue({
     github: 'renovateapp/dummy',
     repository: 'https://github.com/renovateapp/dummy',
   },
+  hasReleaseNotes: true,
   versions: [
     {
       date: new Date('2017-01-01'),
@@ -20,6 +21,12 @@ changelogHelper.getChangeLogJSON.mockReturnValue({
           message: 'foo #3\nbar',
         },
       ],
+      releaseNotes: {
+        url: 'https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0',
+      },
+      compare: {
+        url: 'https://github.com/renovateapp/dummy/compare/v1.0.0...v1.1.0',
+      },
     },
   ],
 });