Read about our upcoming Code of Conduct on this issue

Commit 480f82fd authored by Pierre-Yves David's avatar Pierre-Yves David 🐙
Browse files

branching: merge default into stable to prepare 9.1.0

Time for a new version!

--HG--
branch : stable
Changelog
=========
9.1.0 - in progress
-------------------
* evolve: use the same wording as core in case of unresolved conflict
* evolve: minor output message improvements
* evolve: improve `hg evolve --all` behavior when "." is obsolete
* topic: fix confusion in branch heads checking logic
* touch: now works on merge commit too
* rewind: fix behavior for merge commit
* fold: allow fold with merge commit
* metaedit: now also operates on merge commit
9.0.1 - in progress
-------------------
......
......@@ -278,6 +278,7 @@ from mercurial import (
lock as lockmod,
node,
patch,
pycompat,
revset,
scmutil,
)
......@@ -298,7 +299,6 @@ from . import (
obshashtree,
obshistory,
rewind,
rewriteutil,
safeguard,
templatekw,
utility,
......@@ -329,10 +329,7 @@ colortable = {'evolve.node': 'yellow',
_pack = struct.pack
_unpack = struct.unpack
aliases, entry = cmdutil.findcmd('commit', commands.table)
commitopts3 = cmdrewrite.commitopts3
interactiveopt = cmdrewrite.interactiveopt
rewrite = rewriteutil.rewrite
aliases, entry = cmdutil.findcmd(b'commit', commands.table)
# This extension contains the following code
#
......@@ -403,7 +400,7 @@ def _configureoptions(ui, repo):
repo.ui.setconfig('experimental', 'evolution', evolveopts, 'evolve')
if obsolete.isenabled(repo, 'exchange'):
# if no config explicitly set, disable bundle1
if not isinstance(repo.ui.config('server', 'bundle1'), str):
if not isinstance(repo.ui.config('server', 'bundle1'), bytes):
repo.ui.setconfig('server', 'bundle1', False)
class trdescrepo(repo.__class__):
......@@ -433,8 +430,9 @@ def _configurecmdoptions(ui):
if not matchingevolvecommands:
raise error.Abort(_('unknown command: %s') % cmd)
elif len(matchingevolvecommands) > 1:
msg = _('ambiguous command specification: "%s" matches %r')
raise error.Abort(msg % (cmd, matchingevolvecommands))
matchstr = ', '.join(matchingevolvecommands)
msg = _("ambiguous command specification: '%s' matches [%s]")
raise error.Abort(msg % (cmd, matchstr))
else:
whitelist.add(matchingevolvecommands[0])
for disabledcmd in set(cmdtable) - whitelist:
......@@ -467,7 +465,7 @@ def setupparentcommand(ui):
_alias, statuscmd = cmdutil.findcmd('status', commands.table)
pstatusopts = [o for o in statuscmd[1] if o[1] != 'rev']
@eh.command('pstatus', pstatusopts)
@eh.command(b'pstatus', pstatusopts)
def pstatus(ui, repo, *args, **kwargs):
"""show status combining committed and uncommited changes
......@@ -482,7 +480,7 @@ def setupparentcommand(ui):
_alias, diffcmd = cmdutil.findcmd('diff', commands.table)
pdiffopts = [o for o in diffcmd[1] if o[1] != 'rev']
@eh.command('pdiff', pdiffopts)
@eh.command(b'pdiff', pdiffopts)
def pdiff(ui, repo, *args, **kwargs):
"""show diff combining committed and uncommited changes
......@@ -503,7 +501,7 @@ def _installalias(ui):
### Unstable revset symbol
@eh.revsetpredicate('unstable()')
@eh.revsetpredicate(b'unstable()')
def revsetunstable(repo, subset, x):
"""Changesets with instabilities.
"""
......@@ -516,7 +514,7 @@ def revsetunstable(repo, subset, x):
troubled.sort() # set is non-ordered, enforce order
return subset & troubled
@eh.revsetpredicate('troubled()') # legacy name
@eh.revsetpredicate(b'troubled()') # legacy name
def revsettroubled(repo, subset, x):
return revsetunstable(repo, subset, x)
......@@ -615,7 +613,7 @@ def _allsuccessors(repo, s, haltonflags=0): # XXX we need a better naming
### XXX I'm not sure this revset is useful
@eh.revsetpredicate('suspended()')
@eh.revsetpredicate(b'suspended()')
def revsetsuspended(repo, subset, x):
"""Obsolete changesets with non-obsolete descendants.
"""
......@@ -625,7 +623,7 @@ def revsetsuspended(repo, subset, x):
return subset & suspended
@eh.revsetpredicate('predecessors(set)')
@eh.revsetpredicate(b'predecessors(set)')
def revsetpredecessors(repo, subset, x):
"""Immediate predecessors of changesets in set.
"""
......@@ -635,12 +633,12 @@ def revsetpredecessors(repo, subset, x):
return subset & s
@eh.revsetpredicate('precursors(set)') # legacy name for predecessors
@eh.revsetpredicate(b'precursors(set)') # legacy name for predecessors
def revsetprecursors(repo, subset, x):
return revsetpredecessors(repo, subset, x)
@eh.revsetpredicate('allpredecessors(set)')
@eh.revsetpredicate(b'allpredecessors(set)')
def revsetallpredecessors(repo, subset, x):
"""Transitive predecessors of changesets in set.
"""
......@@ -650,12 +648,12 @@ def revsetallpredecessors(repo, subset, x):
return subset & s
@eh.revsetpredicate('allprecursors(set)') # legacy name for allpredecessors
@eh.revsetpredicate(b'allprecursors(set)') # legacy name for allpredecessors
def revsetallprecursors(repo, subset, x):
return revsetallpredecessors(repo, subset, x)
@eh.revsetpredicate('successors(set)')
@eh.revsetpredicate(b'successors(set)')
def revsetsuccessors(repo, subset, x):
"""Immediate successors of changesets in set.
"""
......@@ -664,7 +662,7 @@ def revsetsuccessors(repo, subset, x):
s.sort()
return subset & s
@eh.revsetpredicate('allsuccessors(set)')
@eh.revsetpredicate(b'allsuccessors(set)')
def revsetallsuccessors(repo, subset, x):
"""Transitive successors of changesets in set.
"""
......@@ -890,7 +888,7 @@ def _deprecatealias(oldalias, newalias):
ui = args[0]
ui.warn(deprecationwarning)
util.checksignature(fn)(*args, **kwargs)
newfn.__doc__ = deprecationwarning + ' (DEPRECATED)'
newfn.__doc__ = pycompat.sysstr(deprecationwarning + ' (DEPRECATED)')
cmdwrapper = eh.command(oldalias, opts, synopsis)
cmdwrapper(newfn)
......@@ -983,14 +981,14 @@ def _findprevtarget(repo, displayer, movebookmark=False, topic=True):
return target, bookmark
@eh.command(
'previous',
[('B', 'move-bookmark', False,
_('move active bookmark after update')),
('m', 'merge', False, _('bring uncommitted change along')),
('', 'no-topic', False, _('ignore topic and move topologically')),
('n', 'dry-run', False,
_('do not perform actions, just print what would be done'))],
'[OPTION]...',
b'previous',
[(b'B', b'move-bookmark', False,
_(b'move active bookmark after update')),
(b'm', b'merge', False, _(b'bring uncommitted change along')),
(b'', b'no-topic', False, _(b'ignore topic and move topologically')),
(b'n', b'dry-run', False,
_(b'do not perform actions, just print what would be done'))],
b'[OPTION]...',
helpbasic=True)
def cmdprevious(ui, repo, **opts):
"""update to parent revision
......@@ -1041,15 +1039,15 @@ def cmdprevious(ui, repo, **opts):
lockmod.release(wlock)
@eh.command(
'next',
[('B', 'move-bookmark', False,
_('move active bookmark after update')),
('m', 'merge', False, _('bring uncommitted change along')),
('', 'evolve', True, _('evolve the next changeset if necessary')),
('', 'no-topic', False, _('ignore topic and move topologically')),
('n', 'dry-run', False,
_('do not perform actions, just print what would be done'))],
'[OPTION]...',
b'next',
[(b'B', b'move-bookmark', False,
_(b'move active bookmark after update')),
(b'm', b'merge', False, _(b'bring uncommitted change along')),
(b'', b'evolve', True, _(b'evolve the next changeset if necessary')),
(b'', b'no-topic', False, _(b'ignore topic and move topologically')),
(b'n', b'dry-run', False,
_(b'do not perform actions, just print what would be done'))],
b'[OPTION]...',
helpbasic=True)
def cmdnext(ui, repo, **opts):
"""update to next child revision
......@@ -1175,18 +1173,18 @@ def _nextevolve(ui, repo, aspchildren, opts):
# making sure a next commit is formed
if result[0] and result[1]:
ui.status(_('working directory is now at %s\n')
% ui.label(str(repo['.']), 'evolve.node'))
% ui.label(bytes(repo['.']), 'evolve.node'))
return 0
def _updatetonext(ui, repo, children, displayer, opts):
def _updatetonext(ui, repo, child, displayer, opts):
""" logic for `hg next` command to update to children and move bookmarks if
required """
bm = repo._activebookmark
shouldmove = opts.get('move_bookmark') and bm is not None
if opts.get('dry_run'):
ui.write(_('hg update %s;\n') % children)
ui.write(_('hg update %s;\n') % child)
if shouldmove:
ui.write(_('hg bookmark %s -r %s;\n') % (bm, children))
ui.write(_('hg bookmark %s -r %s;\n') % (bm, child))
else:
updatecheck = None
# --merge is passed, we don't need to care about commands.update.check
......@@ -1194,7 +1192,7 @@ def _updatetonext(ui, repo, children, displayer, opts):
if opts['merge']:
updatecheck = 'none'
try:
ret = hg.updatetotally(ui, repo, children.node(), None,
ret = hg.updatetotally(ui, repo, child.node(), None,
updatecheck=updatecheck)
except error.Abort as exc:
# replace the hint to mention about --merge option
......@@ -1207,7 +1205,7 @@ def _updatetonext(ui, repo, children, displayer, opts):
lock = repo.lock()
tr = repo.transaction('next')
if shouldmove:
bmchanges = [(bm, children.node())]
bmchanges = [(bm, child.node())]
repo._bookmarks.applychanges(repo, tr, bmchanges)
else:
bookmarksmod.deactivate(repo)
......@@ -1215,7 +1213,7 @@ def _updatetonext(ui, repo, children, displayer, opts):
finally:
lockmod.release(tr, lock)
if not ui.quiet:
displayer.show(children)
displayer.show(child)
return 0
@eh.wrapcommand('commit')
......@@ -1302,9 +1300,9 @@ def _checkinvalidmarkers(orig, markers):
raise error.Abort(msg, hint=hint)
@eh.command(
'debugobsconvert',
[('', 'new-format', obsexchange._bestformat, _('Destination format for markers.'))],
'')
b'debugobsconvert',
[(b'', b'new-format', obsexchange._bestformat, _(b'Destination format for markers.'))],
b'')
def debugobsconvert(ui, repo, new_format):
origmarkers = repo.obsstore._all # settle version
if new_format == repo.obsstore._version:
......@@ -1326,7 +1324,8 @@ def debugobsconvert(ui, repo, new_format):
markers.append(m)
ui.write(_('Old store is version %d, will rewrite in version %d\n') % (
repo.obsstore._version, new_format))
map(f.write, obsolete.encodemarkers(markers, True, new_format))
for data in obsolete.encodemarkers(markers, True, new_format):
f.write(data)
f.close()
ui.write(_('Done!\n'))
......@@ -1355,17 +1354,29 @@ def _evolvemessage():
@eh.uisetup
def setupevolveunfinished(ui):
estate = ('evolvestate', False, False, _('evolve in progress'),
_("use 'hg evolve --continue' or 'hg evolve --abort' to abort"))
cmdutil.unfinishedstates.append(estate)
pstate = ('pickstate', False, False, _('pick in progress'),
_("use 'hg pick --continue' or 'hg pick --abort' to abort"))
cmdutil.unfinishedstates.append(pstate)
afterresolved = ('evolvestate', _('hg evolve --continue'))
pickresolved = ('pickstate', _('hg pick --continue'))
cmdutil.afterresolvedstates.append(afterresolved)
cmdutil.afterresolvedstates.append(pickresolved)
if not util.safehasattr(cmdutil, 'unfinishedstates'):
from mercurial import state as statemod
_msg = _('To continue: hg evolve --continue\n'
'To abort: hg evolve --abort\n'
'To stop: hg evolve --stop\n'
'(also see `hg help evolve.interrupted`)')
statemod.addunfinished('evolve', fname='evolvestate',
continueflag=True, stopflag=True,
statushint=_msg)
statemod.addunfinished('pick', fname='pickstate', continueflag=True)
else:
# compat <= hg-5.0 (5f2f6912c9e6)
estate = ('evolvestate', False, False, _('evolve in progress'),
_("use 'hg evolve --continue' or 'hg evolve --abort' to abort"))
cmdutil.unfinishedstates.append(estate)
pstate = ('pickstate', False, False, _('pick in progress'),
_("use 'hg pick --continue' or 'hg pick --abort' to abort"))
cmdutil.unfinishedstates.append(pstate)
afterresolved = ('evolvestate', _('hg evolve --continue'))
pickresolved = ('pickstate', _('hg pick --continue'))
cmdutil.afterresolvedstates.append(afterresolved)
cmdutil.afterresolvedstates.append(pickresolved)
if util.safehasattr(cmdutil, 'STATES'):
statedata = ('evolve', cmdutil.fileexistspredicate('evolvestate'),
......
This diff is collapsed.
......@@ -7,6 +7,7 @@ Compatibility module
"""
import inspect
import array
from mercurial import (
context,
......@@ -15,6 +16,7 @@ from mercurial import (
mdiff,
obsolete,
obsutil,
pycompat,
repair,
scmutil,
util,
......@@ -22,6 +24,13 @@ from mercurial import (
)
from mercurial.hgweb import hgweb_mod
if pycompat.ispy3:
arraytobytes = array.array.tobytes
arrayfrombytes = array.array.frombytes
else:
arraytobytes = array.array.tostring
arrayfrombytes = array.array.fromstring
# hg < 4.6 compat (c8e2d6ed1f9e)
try:
from mercurial import logcmdutil
......@@ -105,9 +114,14 @@ def strdiff(a, b, fn1, fn2):
args = [a, '', b, '', fn1, fn2]
# hg < 4.6 compat 8b6dd3922f70
argspec = inspect.getargspec(mdiff.unidiff)
if util.safehasattr(inspect, 'signature'):
signature = inspect.signature(mdiff.unidiff)
needsbinary = 'binary' in signature.parameters
else:
argspec = inspect.getargspec(mdiff.unidiff)
needsbinary = 'binary' in argspec.args
if 'binary' in argspec.args:
if needsbinary:
args.append(False)
return mdiff.unidiff(*args)
......@@ -123,7 +137,7 @@ except ImportError:
makedate = mercurial.util.makedate
parsedate = mercurial.util.parsedate
def wireprotocommand(exthelper, name, args='', permission='pull'):
def wireprotocommand(exthelper, name, args=b'', permission=b'pull'):
try:
# Since b4d85bc1
from mercurial.wireprotov1server import wireprotocommand
......@@ -371,7 +385,7 @@ def fixedcopytracing(repo, c1, c2, base):
# examine each file copy for a potential directory move, which is
# when all the files in a directory are moved to a new directory
for dst, src in fullcopy.iteritems():
for dst, src in fullcopy.items():
dsrc, ddst = pathutil.dirname(src), pathutil.dirname(dst)
if dsrc in invalid:
# already seen to be uninteresting
......
......@@ -21,7 +21,7 @@ from . import exthelper
eh = exthelper.exthelper()
@eh.command('debugobsstorestat', [], '')
@eh.command(b'debugobsstorestat', [], b'')
def cmddebugobsstorestat(ui, repo):
"""print statistics about obsolescence markers in the repo"""
def _updateclustermap(nodes, mark, clustersmap):
......
......@@ -35,12 +35,12 @@ def simpledepth(repo, rev):
return len(repo.revs('::%d', rev))
@eh.command(
'debugdepth',
b'debugdepth',
[
('r', 'rev', [], 'revs to print depth for'),
('', 'method', 'cached', "one of 'simple', 'cached', 'compare'"),
(b'r', b'rev', [], b'revs to print depth for'),
(b'', b'method', b'cached', b"one of 'simple', 'cached', 'compare'"),
],
_('REVS'))
_(b'REVS'))
def debugdepth(ui, repo, **opts):
"""display depth of REVS
"""
......@@ -112,16 +112,17 @@ class depthcache(genericcaches.changelogsourcebase):
cl = repo.unfiltered().changelog
total = len(data)
def progress(pos, rev):
def progress(pos, rev=None):
revstr = '' if rev is None else ('rev %d' % rev)
compat.progress(repo.ui, 'updating depth cache',
pos, 'rev %s' % rev, unit='revision', total=total)
progress(0, '')
pos, revstr, unit='revision', total=total)
progress(0)
for idx, rev in enumerate(data, 1):
assert rev == len(self._data), (rev, len(self._data))
self._data.append(self._depth(cl, rev))
if not (idx % 10000): # progress as a too high performance impact
progress(idx, rev)
progress(None, '')
progress(None)
def _depth(self, changelog, rev):
cl = changelog
......@@ -185,7 +186,7 @@ class depthcache(genericcaches.changelogsourcebase):
else:
headerdata = data[:self._cachekeysize]
self._cachekey = self._deserializecachekey(headerdata)
self._data.fromstring(data[self._cachekeysize:])
compat.arrayfrombytes(self._data, data[self._cachekeysize:])
self._ondiskkey = self._cachekey
def save(self, repo):
......@@ -201,7 +202,7 @@ class depthcache(genericcaches.changelogsourcebase):
cachefile = repo.cachevfs(self._filepath, 'w', atomictemp=True)
headerdata = self._serializecachekey()
cachefile.write(headerdata)
cachefile.write(self._data.tostring())
cachefile.write(compat.arraytobytes(self._data))
cachefile.close()
self._ondiskkey = self._cachekey
except (IOError, OSError) as exc:
......
......@@ -26,6 +26,7 @@ from mercurial import (
obsolete,
obsutil,
phases,
pycompat,
repair,
scmutil,
simplemerge,
......@@ -204,7 +205,7 @@ def _solvephasedivergence(ui, repo, bumped, evolvestate, displayer,
msg = _('skipping %s : we do not handle merge yet\n') % bumped
ui.write_err(msg)
return (False, ".")
prec = repo.set('last(allpredecessors(%d) and public())', bumped.rev()).next()
prec = next(repo.set('last(allpredecessors(%d) and public())', bumped.rev()))
# For now we deny target merge
if len(prec.parents()) > 1:
msg = _('skipping: %s: public version is a merge, '
......@@ -217,7 +218,7 @@ def _solvephasedivergence(ui, repo, bumped, evolvestate, displayer,
displayer.show(bumped)
repo.ui.write(_('atop:'))
displayer.show(prec)
if confirm and ui.prompt('perform evolve? [Ny]', 'n') != 'y':
if confirm and ui.prompt(_('perform evolve? [Ny]'), 'n') != 'y':
raise error.Abort(_('evolve aborted by user'))
if dryrun:
todo = 'hg rebase --rev %s --dest %s;\n' % (bumped, prec.p1())
......@@ -336,7 +337,7 @@ def _solvedivergent(ui, repo, divergent, evolvestate, displayer, dryrun=False,
# we don't handle split in content-divergence yet
if len(others) > 1:
othersstr = "[%s]" % (','.join([str(i) for i in others]))
othersstr = "[%s]" % (','.join([bytes(i) for i in others]))
msg = _("skipping %s: %s with a changeset that got split"
" into multiple ones:\n"
"|[%s]\n"
......@@ -542,8 +543,8 @@ def _solvedivergent(ui, repo, divergent, evolvestate, displayer, dryrun=False,
return (res, newnode)
if newnode == publicdiv.node():
# case 2)
pubstr = str(publicdiv)
othstr = str(other)
pubstr = bytes(publicdiv)
othstr = bytes(other)
msg = _('content divergence resolution between %s '
'(public) and %s has same content as %s, '
'discarding %s\n')
......@@ -578,8 +579,9 @@ def _mergecontentdivergents(repo, progresscb, divergent, other, base,
# conflicts while merging content-divergent changesets
if compat.hasconflict(stats):
evolvestate.save()
raise error.InterventionRequired(_("fix conflicts and see `hg help "
"evolve.interrupted`"))
hint = _("see 'hg help evolve.interrupted'")
raise error.InterventionRequired(_("unresolved merge conflicts"),
hint=hint)
def _completecontentdivergent(ui, repo, progresscb, divergent, other,
base, evolvestate):
......@@ -690,8 +692,8 @@ def warnmetadataloss(repo, local, other):
if aspects:
# warn user
locstr = str(local)
othstr = str(other)
locstr = bytes(local)
othstr = bytes(other)
if 'close' in aspects:
filteredasp = aspects - {'close'}
if filteredasp:
......@@ -984,8 +986,9 @@ def relocate(repo, orig, dest, evolvestate, pctx=None, keepbranch=False,
copies.duplicatecopies(repo, repo[None], dest.rev(),
orig.p1().rev())
dirstatedance(repo, dest, orig.node(), None)
raise error.InterventionRequired(_("fix conflicts and see `hg help "
"evolve.interrupted`"))
hint = _("see 'hg help evolve.interrupted'")
raise error.InterventionRequired(_("unresolved merge conflicts"),
hint=hint)
nodenew = _relocatecommit(repo, orig, commitmsg)
_finalizerelocate(repo, orig, dest, nodenew, tr, category, evolvestate)
return nodenew
......@@ -1089,7 +1092,7 @@ def _selectrevs(repo, allopt, revopt, anyopt, targetcat):
if 1 < len(revs):
msg = "multiple evolve candidates"
hint = (_("select one of %s with --rev")
% ', '.join([str(repo[r]) for r in sorted(revs)]))
% ', '.join([bytes(repo[r]) for r in sorted(revs)]))
raise error.Abort(msg, hint=hint)
elif instabilities_map.get(targetcat, targetcat) in repo['.'].instabilities():
revs = set([repo['.'].rev()])
......@@ -1314,7 +1317,7 @@ def listtroubles(ui, repo, troublecategories, **opts):
if opts.get('rev'):
revs = scmutil.revrange(repo, opts.get('rev'))
fm = ui.formatter('evolvelist', opts)
fm = ui.formatter('evolvelist', pycompat.byteskwargs(opts))
for rev in revs:
ctx = repo[rev]
unpars = _preparelistctxs(ctx.parents(), lambda p: p.orphan())
......@@ -1476,7 +1479,7 @@ def divergentsets(repo, ctx):
continue
base[tuple(nsuccset)] = n
divergence = []
for divset, b in base.iteritems():
for divset, b in base.items():
divergence.append({
'divergentnodes': divset,
'commonprecursor': b
......@@ -1593,8 +1596,8 @@ def evolve(ui, repo, **opts):
situation:
- `hg evolve --continue`:
fix all the conflicts using `hg resolve` and then run this to continue the
interrupted evolve
resolve all the conflicts using `hg resolve` and then run this to
continue the interrupted evolve
- `hg evolve --stop`:
stops the current interrupted evolve, keeping all the successful steps,
......@@ -1620,25 +1623,23 @@ def evolve(ui, repo, **opts):
abortopt = opts['abort']
shouldupdate = opts['update']
troublecategories = ['phase_divergent', 'content_divergent', 'orphan']
specifiedcategories = [t.replace('_', '')
for t in troublecategories
if opts[t]]
troublecategories = {
'phasedivergent': 'phase_divergent',
'contentdivergent': 'content_divergent',
'orphan': 'orphan',
}
specifiedcategories = [k for k, v in troublecategories.items() if opts[v]]
if opts['list']:
ui.pager('evolve')
listtroubles(ui, repo, specifiedcategories, **opts)
return
targetcat = 'orphan'
has_some_opts = bool(revopt or anyopt or allopt or contopt or stopopt or abortopt)
if 1 < len(specifiedcategories):
msg = _('cannot specify more than one trouble category to solve (yet)')
raise error.Abort(msg)
elif len(specifiedcategories) == 1:
targetcat = specifiedcategories[0]
elif repo['.'].obsolete() and not has_some_opts:
# if no args and parent is obsolete, update to successors
return solveobswdp(ui, repo, opts)
ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'evolve')
......@@ -1665,7 +1666,7 @@ def evolve(ui, repo, **opts):
return
elif abortopt:
if not evolvestate:
raise error.Abort(_('no interrupted evolve to stop'))
raise error.Abort(_('no interrupted evolve to abort'))
evolvestate.load()
# `hg next --evolve` in play
if evolvestate['command'] != 'evolve':
......@@ -1680,19 +1681,20 @@ def evolve(ui, repo, **opts):
else:
cmdutil.bailifchanged(repo)