diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -1584,6 +1584,8 @@ def clone(ui, source, dest=None, **opts) ('', 'amend', None, _('amend the parent of the working directory')), ('s', 'secret', None, _('use the secret phase for committing')), ('e', 'edit', None, _('invoke editor on commit messages')), + ('', 'force-close-branch', None, + _('forcibly close branch from a non-head changeset (ADVANCED)')), ('i', 'interactive', None, _('use interactive mode')), ] + walkopts + commitopts + commitopts2 + subrepoopts, _('[OPTION]... [FILE]...'), @@ -1671,7 +1673,7 @@ def _docommit(ui, repo, *pats, **opts): bheads = repo.branchheads(branch) extra = {} - if opts.get('close_branch'): + if opts.get('close_branch') or opts.get('force_close_branch'): extra['close'] = '1' if repo['.'].closesbranch(): @@ -1679,8 +1681,11 @@ def _docommit(ui, repo, *pats, **opts): ' head')) elif not bheads: raise error.Abort(_('branch "%s" has no heads to close') % branch) - elif branch == repo['.'].branch() and repo['.'].node() not in bheads: - raise error.Abort(_('can only close branch heads')) + elif (branch == repo['.'].branch() and repo['.'].node() not in bheads + and not opts.get('force_close_branch')): + hint = _('use --force-close-branch to close branch from a non-head' + ' changeset') + raise error.Abort(_('can only close branch heads'), hint=hint) elif opts.get('amend'): if (repo['.'].p1().branch() != branch and repo['.'].p2().branch() != branch): diff --git a/tests/test-branches.t b/tests/test-branches.t --- a/tests/test-branches.t +++ b/tests/test-branches.t @@ -958,6 +958,7 @@ trying to close branch from a cset which it should abort: $ hg ci -m "closing branch" --close-branch abort: can only close branch heads + (use --force-close-branch to close branch from a non-head changeset) [255] $ hg up 0 @@ -972,3 +973,18 @@ it should abort: @ 0: 9092f1db7931 added a default +Test --force-close-branch to close a branch from a non-head changeset: +--------------------------------------------------------------------- + + $ hg show stack --config extensions.show= + o 1553 added c + o 5f6d added b + @ 9092 added a + + $ hg ci -m "branch closed" --close-branch + abort: can only close branch heads + (use --force-close-branch to close branch from a non-head changeset) + [255] + + $ hg ci -m "branch closed" --force-close-branch + created new head diff --git a/tests/test-completion.t b/tests/test-completion.t --- a/tests/test-completion.t +++ b/tests/test-completion.t @@ -247,7 +247,7 @@ Show all commands + options bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure cat: output, rev, decode, include, exclude, template clone: noupdate, updaterev, rev, branch, pull, uncompressed, stream, ssh, remotecmd, insecure - commit: addremove, close-branch, amend, secret, edit, interactive, include, exclude, message, logfile, date, user, subrepos + commit: addremove, close-branch, amend, secret, edit, force-close-branch, interactive, include, exclude, message, logfile, date, user, subrepos config: untrusted, edit, local, global, template copy: after, force, include, exclude, dry-run debugancestor: