diff --git a/README.md b/README.md index f924f6073..88a03eb81 100644 --- a/README.md +++ b/README.md @@ -313,6 +313,10 @@ If you do not want to have any tag prefix you can use the `-t` flag and provide > Note: simply -t or --tag-prefix without any value will fallback to the default 'v' +### Tag replacement + +If you've already run `standard-version` when creating your release, you may want to alter the release content and changelog without bumping the version, by using `standard-version --skip.bump`. By default, tagging with an already existing tag make `git` fails. You can add the `--tag-force` flag to make use of `-f` option when calling `git tag`, then the existing version tag will be replaced. + ### CLI Help ```sh diff --git a/command.js b/command.js index d65d5ea06..da3d44372 100755 --- a/command.js +++ b/command.js @@ -68,6 +68,11 @@ const yargs = require('yargs') type: 'string', default: defaults.tagPrefix }) + .option('tag-force', { + describe: 'Allow tag replacement', + type: 'boolean', + default: defaults.tagForce + }) .option('scripts', { describe: 'Provide scripts to execute for lifecycle events (prebump, precommit, etc.,)', default: defaults.scripts diff --git a/defaults.js b/defaults.js index 614c453e4..67bb92a06 100644 --- a/defaults.js +++ b/defaults.js @@ -11,6 +11,7 @@ const defaults = { scripts: {}, skip: {}, dryRun: false, + tagForce: false, gitTagFallback: true, preset: require.resolve('conventional-changelog-conventionalcommits') } diff --git a/lib/lifecycles/tag.js b/lib/lifecycles/tag.js index ec1b88e53..e0e52018c 100644 --- a/lib/lifecycles/tag.js +++ b/lib/lifecycles/tag.js @@ -14,14 +14,17 @@ module.exports = async function (newVersion, pkgPrivate, args) { } async function execTag (newVersion, pkgPrivate, args) { - let tagOption + const tagOption = [] if (args.sign) { - tagOption = '-s' + tagOption.push('-s') } else { - tagOption = '-a' + tagOption.push('-a') + } + if (args.tagForce) { + tagOption.push('-f') } checkpoint(args, 'tagging release %s%s', [args.tagPrefix, newVersion]) - await runExecFile(args, 'git', ['tag', tagOption, args.tagPrefix + newVersion, '-m', `${formatCommitMessage(args.releaseCommitMessageFormat, newVersion)}`]) + await runExecFile(args, 'git', ['tag', ...tagOption, args.tagPrefix + newVersion, '-m', `${formatCommitMessage(args.releaseCommitMessageFormat, newVersion)}`]) const currentBranch = await runExecFile('', 'git', ['rev-parse', '--abbrev-ref', 'HEAD']) let message = 'git push --follow-tags origin ' + currentBranch.trim() if (pkgPrivate !== true && bump.getUpdatedConfigs()['package.json']) { diff --git a/test/core.spec.js b/test/core.spec.js index 448e107ae..7f270a8e1 100644 --- a/test/core.spec.js +++ b/test/core.spec.js @@ -752,6 +752,26 @@ describe('with mocked git', function () { gitArgs.should.have.lengthOf(0) }) + it('--tag-force forces tag replacement', async function () { + const gitArgs = [ + ['add', 'CHANGELOG.md', 'package.json'], + ['commit', 'CHANGELOG.md', 'package.json', '-m', 'chore(release): 1.0.1'], + ['tag', '-a', '-f', 'v1.0.1', '-m', 'chore(release): 1.0.1'], + ['rev-parse', '--abbrev-ref', 'HEAD'] + ] + const execFile = (_args, cmd, cmdArgs) => { + cmd.should.equal('git') + const expected = gitArgs.shift() + cmdArgs.should.deep.equal(expected) + if (expected[0] === 'rev-parse') return Promise.resolve('master') + return Promise.resolve('') + } + mock({ bump: 'patch', changelog: 'foo\n', execFile }) + + await exec('--tag-force', true) + gitArgs.should.have.lengthOf(0) + }) + it('fails if git add fails', async function () { const gitArgs = [ ['add', 'CHANGELOG.md', 'package.json'] diff --git a/test/git.spec.js b/test/git.spec.js index dfa9dbb11..4708c0d1b 100644 --- a/test/git.spec.js +++ b/test/git.spec.js @@ -167,6 +167,14 @@ describe('git', function () { await exec('-n') }) + it('replaces tags if version not bumped', async function () { + mock({ bump: 'minor', tags: ['v1.0.0'] }) + await exec({}) + shell.exec('git describe').stdout.should.match(/v1\.1\.0/) + await exec('--tag-force --skip.bump') + shell.exec('git describe').stdout.should.match(/v1\.1\.0/) + }) + it('allows the commit phase to be skipped', async function () { const changelogContent = 'legacy header format\n' writePackageJson('1.0.0')