diff --git a/index.js b/index.js index 1c7ad4b9..15572a88 100644 --- a/index.js +++ b/index.js @@ -56,17 +56,16 @@ async function run(options, plugins) { options.repositoryUrl = await getGitAuthUrl(options); - if (!(await isBranchUpToDate(options.branch))) { - logger.log( - "The local branch %s is behind the remote one, therefore a new version won't be published.", - options.branch - ); - return false; - } - try { await verifyAuth(options.repositoryUrl, options.branch); } catch (err) { + if (!(await isBranchUpToDate(options.repositoryUrl, options.branch))) { + logger.log( + "The local branch %s is behind the remote one, therefore a new version won't be published.", + options.branch + ); + return false; + } logger.error(`The command "${err.cmd}" failed with the error message %s.`, err.stderr); throw getError('EGITNOPERMISSION', {options}); } diff --git a/lib/git.js b/lib/git.js index 4a6c537b..a9bc77ac 100644 --- a/lib/git.js +++ b/lib/git.js @@ -141,12 +141,19 @@ async function verifyTagName(tagName) { /** * Verify the local branch is up to date with the remote one. * + * @param {String} repositoryUrl The remote repository URL. * @param {String} branch The repository branch for which to verify status. * * @return {Boolean} `true` is the HEAD of the current local branch is the same as the HEAD of the remote branch, falsy otherwise. */ -async function isBranchUpToDate(branch) { - return isRefInHistory(await execa.stdout('git', ['rev-parse', `origin/${branch}`])); +async function isBranchUpToDate(repositoryUrl, branch) { + try { + return await isRefInHistory( + (await execa.stdout('git', ['ls-remote', '--heads', repositoryUrl, branch])).match(/^(\w+)?/)[1] + ); + } catch (err) { + debug(err); + } } module.exports = { diff --git a/test/git.test.js b/test/git.test.js index 849914d0..95e1516f 100644 --- a/test/git.test.js +++ b/test/git.test.js @@ -24,7 +24,6 @@ import { gitCommitTag, gitRemoteTagHead, push as pushUtil, - reset, } from './helpers/git-utils'; // Save the current working diretory @@ -189,31 +188,35 @@ test.serial('Throws error if obtaining the tags fails', async t => { }); test.serial('Return "true" if repository is up to date', async t => { - await gitRepo(true); + const repositoryUrl = await gitRepo(true); await gitCommits(['First']); await pushUtil(); - t.true(await isBranchUpToDate('master')); + t.true(await isBranchUpToDate(repositoryUrl, 'master')); }); test.serial('Return falsy if repository is not up to date', async t => { - await gitRepo(true); + const repositoryUrl = await gitRepo(true); + const repoDir = process.cwd(); await gitCommits(['First']); await gitCommits(['Second']); await pushUtil(); - t.true(await isBranchUpToDate('master')); + t.true(await isBranchUpToDate(repositoryUrl, 'master')); - await reset(); + await gitShallowClone(repositoryUrl); + await gitCommits(['Third']); + await pushUtil(); + process.chdir(repoDir); - t.falsy(await isBranchUpToDate('master')); + t.falsy(await isBranchUpToDate(repositoryUrl, 'master')); }); test.serial('Return "true" if local repository is ahead', async t => { - await gitRepo(true); + const repositoryUrl = await gitRepo(true); await gitCommits(['First']); await pushUtil(); await gitCommits(['Second']); - t.true(await isBranchUpToDate('master')); + t.true(await isBranchUpToDate(repositoryUrl, 'master')); }); diff --git a/test/helpers/git-utils.js b/test/helpers/git-utils.js index f80a2584..fa28481b 100644 --- a/test/helpers/git-utils.js +++ b/test/helpers/git-utils.js @@ -212,12 +212,3 @@ export async function gitCommitTag(gitHead) { export async function push(repositoryUrl = 'origin', branch = 'master') { await execa('git', ['push', '--tags', repositoryUrl, `HEAD:${branch}`]); } - -/** - * Reset repository to a commit. - * - * @param {String} [commit='HEAD~1'] Commit reference to reset the repo to. - */ -export async function reset(commit = 'HEAD~1') { - await execa('git', ['reset', commit]); -} diff --git a/test/index.test.js b/test/index.test.js index 305e066b..fd431f44 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -15,7 +15,6 @@ import { gitRemoteTagHead, push, gitShallowClone, - reset, } from './helpers/git-utils'; // Save the current process.env @@ -645,11 +644,15 @@ test.serial('Returns falsy value if triggered by a PR', async t => { test.serial('Returns falsy value if triggered on an outdated clone', async t => { // Create a git repository, set the current working directory at the root of the repo const repositoryUrl = await gitRepo(true); + const repoDir = process.cwd(); // Add commits to the master branch await gitCommits(['First']); await gitCommits(['Second']); await push(); - await reset(); + await gitShallowClone(repositoryUrl); + await gitCommits(['Third']); + await push(); + process.chdir(repoDir); const semanticRelease = proxyquire('..', { './lib/logger': t.context.logger, diff --git a/test/integration.test.js b/test/integration.test.js index df12853b..fc35d3c7 100644 --- a/test/integration.test.js +++ b/test/integration.test.js @@ -2,7 +2,7 @@ import test from 'ava'; import {writeJson, readJson} from 'fs-extra'; import {stub} from 'sinon'; import execa from 'execa'; -import {gitHead as getGitHead, gitTagHead, gitRepo, gitCommits, gitRemoteTagHead} from './helpers/git-utils'; +import {gitHead as getGitHead, gitTagHead, gitRepo, gitCommits, gitRemoteTagHead, push} from './helpers/git-utils'; import gitbox from './helpers/gitbox'; import mockServer from './helpers/mockserver'; import npmRegistry from './helpers/npm-registry'; @@ -609,6 +609,7 @@ test.serial('Exit with 1 if missing permission to push to the remote repository' /* Initial release */ t.log('Commit a feature'); await gitCommits(['feat: Initial commit']); + await push(); t.log('$ semantic-release'); const {stdout, code} = await execa( cli,