evolvecmd: move more functions from __init__.py to evolvecmd.py
If things are looking ugly, hold on.
--- a/hgext3rd/evolve/__init__.py Fri Jan 19 14:42:46 2018 +0530
+++ b/hgext3rd/evolve/__init__.py Fri Jan 19 15:04:12 2018 +0530
@@ -285,7 +285,6 @@
cmdutil,
commands,
context,
- copies,
dirstate,
error,
extensions,
@@ -1701,329 +1700,6 @@
result.add(unstable)
return sorted(result - target)
-def _solveunstable(ui, repo, orig, dryrun=False, confirm=False,
- progresscb=None):
- """ Tries to stabilize the changeset orig which is orphan.
-
- returns a tuple (bool, newnode) where,
- bool: a boolean value indicating whether the instability was solved
- newnode: if bool is True, then the newnode of the resultant commit
- formed. newnode can be node, when resolution led to no new
- commit. If bool is False, this is ''.
- """
- pctx = orig.p1()
- keepbranch = orig.p1().branch() != orig.branch()
- if len(orig.parents()) == 2:
- if not pctx.obsolete():
- pctx = orig.p2() # second parent is obsolete ?
- keepbranch = orig.p2().branch() != orig.branch()
- elif orig.p2().obsolete():
- hint = _("Redo the merge (%s) and use `hg prune <old> "
- "--succ <new>` to obsolete the old one") % orig.hex()[:12]
- ui.warn(_("warning: no support for evolving merge changesets "
- "with two obsolete parents yet\n") +
- _("(%s)\n") % hint)
- return (False, '')
-
- if not pctx.obsolete():
- ui.warn(_("cannot solve instability of %s, skipping\n") % orig)
- return (False, '')
- obs = pctx
- newer = compat.successorssets(repo, obs.node())
- # search of a parent which is not killed
- while not newer or newer == [()]:
- ui.debug("stabilize target %s is plain dead,"
- " trying to stabilize on its parent\n" %
- obs)
- obs = obs.parents()[0]
- newer = compat.successorssets(repo, obs.node())
- if len(newer) > 1:
- msg = _("skipping %s: divergent rewriting. can't choose "
- "destination\n") % obs
- ui.write_err(msg)
- return (False, '')
- targets = newer[0]
- assert targets
- if len(targets) > 1:
- # split target, figure out which one to pick, are they all in line?
- targetrevs = [repo[r].rev() for r in targets]
- roots = repo.revs('roots(%ld)', targetrevs)
- heads = repo.revs('heads(%ld)', targetrevs)
- if len(roots) > 1 or len(heads) > 1:
- msg = "cannot solve split across two branches\n"
- ui.write_err(msg)
- return (False, '')
- target = repo[heads.first()]
- else:
- target = targets[0]
- displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate})
- target = repo[target]
- if not ui.quiet or confirm:
- repo.ui.write(_('move:'))
- displayer.show(orig)
- repo.ui.write(_('atop:'))
- displayer.show(target)
- if confirm and ui.prompt('perform evolve? [Ny]', 'n') != 'y':
- raise error.Abort(_('evolve aborted by user'))
- if progresscb:
- progresscb()
- todo = 'hg rebase -r %s -d %s\n' % (orig, target)
- if dryrun:
- repo.ui.write(todo)
- return (False, '')
- else:
- repo.ui.note(todo)
- if progresscb:
- progresscb()
- try:
- newid = relocate(repo, orig, target, pctx, keepbranch)
- return (True, newid)
- except MergeFailure:
- ops = {'current': orig.node()}
- evolvestate = state.cmdstate(repo, opts=ops)
- evolvestate.save()
- repo.ui.write_err(_('evolve failed!\n'))
- repo.ui.write_err(
- _("fix conflict and run 'hg evolve --continue'"
- " or use 'hg update -C .' to abort\n"))
- raise
-
-def _solvebumped(ui, repo, bumped, dryrun=False, confirm=False,
- progresscb=None):
- """Stabilize a bumped changeset
-
- returns a tuple (bool, newnode) where,
- bool: a boolean value indicating whether the instability was solved
- newnode: if bool is True, then the newnode of the resultant commit
- formed. newnode can be node, when resolution led to no new
- commit. If bool is False, this is ''.
- """
- repo = repo.unfiltered()
- bumped = repo[bumped.rev()]
- # For now we deny bumped merge
- if len(bumped.parents()) > 1:
- msg = _('skipping %s : we do not handle merge yet\n') % bumped
- ui.write_err(msg)
- return (False, '')
- prec = repo.set('last(allprecursors(%d) and public())', bumped).next()
- # For now we deny target merge
- if len(prec.parents()) > 1:
- msg = _('skipping: %s: public version is a merge, '
- 'this is not handled yet\n') % prec
- ui.write_err(msg)
- return (False, '')
-
- displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate})
- if not ui.quiet or confirm:
- repo.ui.write(_('recreate:'))
- displayer.show(bumped)
- repo.ui.write(_('atop:'))
- displayer.show(prec)
- 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())
- repo.ui.write(todo)
- repo.ui.write(('hg update %s;\n' % prec))
- repo.ui.write(('hg revert --all --rev %s;\n' % bumped))
- repo.ui.write(('hg commit --msg "%s update to %s"\n' %
- (TROUBLES['PHASEDIVERGENT'], bumped)))
- return (False, '')
- if progresscb:
- progresscb()
- newid = tmpctx = None
- tmpctx = bumped
- # Basic check for common parent. Far too complicated and fragile
- tr = repo.currenttransaction()
- assert tr is not None
- bmupdate = _bookmarksupdater(repo, bumped.node(), tr)
- if not list(repo.set('parents(%d) and parents(%d)', bumped, prec)):
- # Need to rebase the changeset at the right place
- repo.ui.status(
- _('rebasing to destination parent: %s\n') % prec.p1())
- try:
- tmpid = relocate(repo, bumped, prec.p1())
- if tmpid is not None:
- tmpctx = repo[tmpid]
- obsolete.createmarkers(repo, [(bumped, (tmpctx,))])
- except MergeFailure:
- repo.vfs.write('graftstate', bumped.hex() + '\n')
- repo.ui.write_err(_('evolution failed!\n'))
- msg = _("fix conflict and run 'hg evolve --continue'\n")
- repo.ui.write_err(msg)
- raise
- # Create the new commit context
- repo.ui.status(_('computing new diff\n'))
- files = set()
- copied = copies.pathcopies(prec, bumped)
- precmanifest = prec.manifest().copy()
- # 3.3.2 needs a list.
- # future 3.4 don't detect the size change during iteration
- # this is fishy
- for key, val in list(bumped.manifest().iteritems()):
- precvalue = precmanifest.get(key, None)
- if precvalue is not None:
- del precmanifest[key]
- if precvalue != val:
- files.add(key)
- files.update(precmanifest) # add missing files
- # commit it
- if files: # something to commit!
- def filectxfn(repo, ctx, path):
- if path in bumped:
- fctx = bumped[path]
- flags = fctx.flags()
- mctx = compat.memfilectx(repo, ctx, fctx, flags, copied, path)
- return mctx
- return None
- text = '%s update to %s:\n\n' % (TROUBLES['PHASEDIVERGENT'], prec)
- text += bumped.description()
-
- new = context.memctx(repo,
- parents=[prec.node(), node.nullid],
- text=text,
- files=files,
- filectxfn=filectxfn,
- user=bumped.user(),
- date=bumped.date(),
- extra=bumped.extra())
-
- newid = repo.commitctx(new)
- if newid is None:
- obsolete.createmarkers(repo, [(tmpctx, ())])
- newid = prec.node()
- else:
- phases.retractboundary(repo, tr, bumped.phase(), [newid])
- obsolete.createmarkers(repo, [(tmpctx, (repo[newid],))],
- flag=obsolete.bumpedfix)
- bmupdate(newid)
- repo.ui.status(_('committed as %s\n') % node.short(newid))
- # reroute the working copy parent to the new changeset
- with repo.dirstate.parentchange():
- repo.dirstate.setparents(newid, node.nullid)
- return (True, newid)
-
-def _solvedivergent(ui, repo, divergent, dryrun=False, confirm=False,
- progresscb=None):
- """tries to solve content-divergence of a changeset
-
- returns a tuple (bool, newnode) where,
- bool: a boolean value indicating whether the instability was solved
- newnode: if bool is True, then the newnode of the resultant commit
- formed. newnode can be node, when resolution led to no new
- commit. If bool is False, this is ''.
- """
- repo = repo.unfiltered()
- divergent = repo[divergent.rev()]
- base, others = divergentdata(divergent)
- if len(others) > 1:
- othersstr = "[%s]" % (','.join([str(i) for i in others]))
- msg = _("skipping %d:%s with a changeset that got split"
- " into multiple ones:\n"
- "|[%s]\n"
- "| This is not handled by automatic evolution yet\n"
- "| You have to fallback to manual handling with commands "
- "such as:\n"
- "| - hg touch -D\n"
- "| - hg prune\n"
- "| \n"
- "| You should contact your local evolution Guru for help.\n"
- ) % (divergent, TROUBLES['CONTENTDIVERGENT'], othersstr)
- ui.write_err(msg)
- return (False, '')
- other = others[0]
- if len(other.parents()) > 1:
- msg = _("skipping %s: %s changeset can't be "
- "a merge (yet)\n") % (divergent, TROUBLES['CONTENTDIVERGENT'])
- ui.write_err(msg)
- hint = _("You have to fallback to solving this by hand...\n"
- "| This probably means redoing the merge and using \n"
- "| `hg prune` to kill older version.\n")
- ui.write_err(hint)
- return (False, '')
- if other.p1() not in divergent.parents():
- msg = _("skipping %s: have a different parent than %s "
- "(not handled yet)\n") % (divergent, other)
- hint = _("| %(d)s, %(o)s are not based on the same changeset.\n"
- "| With the current state of its implementation, \n"
- "| evolve does not work in that case.\n"
- "| rebase one of them next to the other and run \n"
- "| this command again.\n"
- "| - either: hg rebase --dest 'p1(%(d)s)' -r %(o)s\n"
- "| - or: hg rebase --dest 'p1(%(o)s)' -r %(d)s\n"
- ) % {'d': divergent, 'o': other}
- ui.write_err(msg)
- ui.write_err(hint)
- return (False, '')
-
- displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate})
- if not ui.quiet or confirm:
- ui.write(_('merge:'))
- displayer.show(divergent)
- ui.write(_('with: '))
- displayer.show(other)
- ui.write(_('base: '))
- displayer.show(base)
- if confirm and ui.prompt(_('perform evolve? [Ny]'), 'n') != 'y':
- raise error.Abort(_('evolve aborted by user'))
- if dryrun:
- ui.write(('hg update -c %s &&\n' % divergent))
- ui.write(('hg merge %s &&\n' % other))
- ui.write(('hg commit -m "auto merge resolving conflict between '
- '%s and %s"&&\n' % (divergent, other)))
- ui.write(('hg up -C %s &&\n' % base))
- ui.write(('hg revert --all --rev tip &&\n'))
- ui.write(('hg commit -m "`hg log -r %s --template={desc}`";\n'
- % divergent))
- return (False, '')
- if divergent not in repo[None].parents():
- repo.ui.status(_('updating to "local" conflict\n'))
- hg.update(repo, divergent.rev())
- repo.ui.note(_('merging %s changeset\n') % TROUBLES['CONTENTDIVERGENT'])
- if progresscb:
- progresscb()
- stats = merge.update(repo,
- other.node(),
- branchmerge=True,
- force=False,
- ancestor=base.node(),
- mergeancestor=True)
- hg._showstats(repo, stats)
- if stats[3]:
- repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
- "or 'hg update -C .' to abort\n"))
- if stats[3] > 0:
- raise error.Abort('merge conflict between several amendments '
- '(this is not automated yet)',
- hint="""/!\ You can try:
-/!\ * manual merge + resolve => new cset X
-/!\ * hg up to the parent of the amended changeset (which are named W and Z)
-/!\ * hg revert --all -r X
-/!\ * hg ci -m "same message as the amended changeset" => new cset Y
-/!\ * hg prune -n Y W Z
-""")
- if progresscb:
- progresscb()
- emtpycommitallowed = repo.ui.backupconfig('ui', 'allowemptycommit')
- tr = repo.currenttransaction()
- assert tr is not None
- try:
- repo.ui.setconfig('ui', 'allowemptycommit', True, 'evolve')
- with repo.dirstate.parentchange():
- repo.dirstate.setparents(divergent.node(), node.nullid)
- oldlen = len(repo)
- cmdrewrite.amend(ui, repo, message='', logfile='')
- if oldlen == len(repo):
- new = divergent
- # no changes
- else:
- new = repo['.']
- obsolete.createmarkers(repo, [(other, (new,))])
- phases.retractboundary(repo, tr, other.phase(), [new.node()])
- return (True, new.node())
- finally:
- repo.ui.restoreconfig(emtpycommitallowed)
-
def divergentdata(ctx):
"""return base, other part of a conflict
--- a/hgext3rd/evolve/evolvecmd.py Fri Jan 19 14:42:46 2018 +0530
+++ b/hgext3rd/evolve/evolvecmd.py Fri Jan 19 15:04:12 2018 +0530
@@ -9,10 +9,33 @@
"""logic related to hg evolve command"""
from mercurial import (
+ cmdutil,
+ context,
+ copies,
+ error,
+ hg,
lock as lockmod,
+ merge,
+ node,
+ obsolete,
+ phases,
)
-from . import _solveunstable, _solvebumped, _solvedivergent
+from mercurial.i18n import _
+
+from . import (
+ cmdrewrite,
+ compat,
+ rewriteutil,
+ state,
+ utility,
+)
+
+TROUBLES = compat.TROUBLES
+shorttemplate = utility.shorttemplate
+_bookmarksupdater = rewriteutil.bookmarksupdater
+
+from . import relocate, divergentdata, MergeFailure
def _solveone(ui, repo, ctx, dryrun, confirm, progresscb, category):
"""Resolve the troubles affecting one revision
@@ -41,3 +64,326 @@
return result
finally:
lockmod.release(tr, lock, wlock)
+
+def _solveunstable(ui, repo, orig, dryrun=False, confirm=False,
+ progresscb=None):
+ """ Tries to stabilize the changeset orig which is orphan.
+
+ returns a tuple (bool, newnode) where,
+ bool: a boolean value indicating whether the instability was solved
+ newnode: if bool is True, then the newnode of the resultant commit
+ formed. newnode can be node, when resolution led to no new
+ commit. If bool is False, this is ''.
+ """
+ pctx = orig.p1()
+ keepbranch = orig.p1().branch() != orig.branch()
+ if len(orig.parents()) == 2:
+ if not pctx.obsolete():
+ pctx = orig.p2() # second parent is obsolete ?
+ keepbranch = orig.p2().branch() != orig.branch()
+ elif orig.p2().obsolete():
+ hint = _("Redo the merge (%s) and use `hg prune <old> "
+ "--succ <new>` to obsolete the old one") % orig.hex()[:12]
+ ui.warn(_("warning: no support for evolving merge changesets "
+ "with two obsolete parents yet\n") +
+ _("(%s)\n") % hint)
+ return (False, '')
+
+ if not pctx.obsolete():
+ ui.warn(_("cannot solve instability of %s, skipping\n") % orig)
+ return (False, '')
+ obs = pctx
+ newer = compat.successorssets(repo, obs.node())
+ # search of a parent which is not killed
+ while not newer or newer == [()]:
+ ui.debug("stabilize target %s is plain dead,"
+ " trying to stabilize on its parent\n" %
+ obs)
+ obs = obs.parents()[0]
+ newer = compat.successorssets(repo, obs.node())
+ if len(newer) > 1:
+ msg = _("skipping %s: divergent rewriting. can't choose "
+ "destination\n") % obs
+ ui.write_err(msg)
+ return (False, '')
+ targets = newer[0]
+ assert targets
+ if len(targets) > 1:
+ # split target, figure out which one to pick, are they all in line?
+ targetrevs = [repo[r].rev() for r in targets]
+ roots = repo.revs('roots(%ld)', targetrevs)
+ heads = repo.revs('heads(%ld)', targetrevs)
+ if len(roots) > 1 or len(heads) > 1:
+ msg = "cannot solve split across two branches\n"
+ ui.write_err(msg)
+ return (False, '')
+ target = repo[heads.first()]
+ else:
+ target = targets[0]
+ displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate})
+ target = repo[target]
+ if not ui.quiet or confirm:
+ repo.ui.write(_('move:'))
+ displayer.show(orig)
+ repo.ui.write(_('atop:'))
+ displayer.show(target)
+ if confirm and ui.prompt('perform evolve? [Ny]', 'n') != 'y':
+ raise error.Abort(_('evolve aborted by user'))
+ if progresscb:
+ progresscb()
+ todo = 'hg rebase -r %s -d %s\n' % (orig, target)
+ if dryrun:
+ repo.ui.write(todo)
+ return (False, '')
+ else:
+ repo.ui.note(todo)
+ if progresscb:
+ progresscb()
+ try:
+ newid = relocate(repo, orig, target, pctx, keepbranch)
+ return (True, newid)
+ except MergeFailure:
+ ops = {'current': orig.node()}
+ evolvestate = state.cmdstate(repo, opts=ops)
+ evolvestate.save()
+ repo.ui.write_err(_('evolve failed!\n'))
+ repo.ui.write_err(
+ _("fix conflict and run 'hg evolve --continue'"
+ " or use 'hg update -C .' to abort\n"))
+ raise
+
+def _solvebumped(ui, repo, bumped, dryrun=False, confirm=False,
+ progresscb=None):
+ """Stabilize a bumped changeset
+
+ returns a tuple (bool, newnode) where,
+ bool: a boolean value indicating whether the instability was solved
+ newnode: if bool is True, then the newnode of the resultant commit
+ formed. newnode can be node, when resolution led to no new
+ commit. If bool is False, this is ''.
+ """
+ repo = repo.unfiltered()
+ bumped = repo[bumped.rev()]
+ # For now we deny bumped merge
+ if len(bumped.parents()) > 1:
+ msg = _('skipping %s : we do not handle merge yet\n') % bumped
+ ui.write_err(msg)
+ return (False, '')
+ prec = repo.set('last(allprecursors(%d) and public())', bumped).next()
+ # For now we deny target merge
+ if len(prec.parents()) > 1:
+ msg = _('skipping: %s: public version is a merge, '
+ 'this is not handled yet\n') % prec
+ ui.write_err(msg)
+ return (False, '')
+
+ displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate})
+ if not ui.quiet or confirm:
+ repo.ui.write(_('recreate:'))
+ displayer.show(bumped)
+ repo.ui.write(_('atop:'))
+ displayer.show(prec)
+ 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())
+ repo.ui.write(todo)
+ repo.ui.write(('hg update %s;\n' % prec))
+ repo.ui.write(('hg revert --all --rev %s;\n' % bumped))
+ repo.ui.write(('hg commit --msg "%s update to %s"\n' %
+ (TROUBLES['PHASEDIVERGENT'], bumped)))
+ return (False, '')
+ if progresscb:
+ progresscb()
+ newid = tmpctx = None
+ tmpctx = bumped
+ # Basic check for common parent. Far too complicated and fragile
+ tr = repo.currenttransaction()
+ assert tr is not None
+ bmupdate = _bookmarksupdater(repo, bumped.node(), tr)
+ if not list(repo.set('parents(%d) and parents(%d)', bumped, prec)):
+ # Need to rebase the changeset at the right place
+ repo.ui.status(
+ _('rebasing to destination parent: %s\n') % prec.p1())
+ try:
+ tmpid = relocate(repo, bumped, prec.p1())
+ if tmpid is not None:
+ tmpctx = repo[tmpid]
+ obsolete.createmarkers(repo, [(bumped, (tmpctx,))])
+ except MergeFailure:
+ repo.vfs.write('graftstate', bumped.hex() + '\n')
+ repo.ui.write_err(_('evolution failed!\n'))
+ msg = _("fix conflict and run 'hg evolve --continue'\n")
+ repo.ui.write_err(msg)
+ raise
+ # Create the new commit context
+ repo.ui.status(_('computing new diff\n'))
+ files = set()
+ copied = copies.pathcopies(prec, bumped)
+ precmanifest = prec.manifest().copy()
+ # 3.3.2 needs a list.
+ # future 3.4 don't detect the size change during iteration
+ # this is fishy
+ for key, val in list(bumped.manifest().iteritems()):
+ precvalue = precmanifest.get(key, None)
+ if precvalue is not None:
+ del precmanifest[key]
+ if precvalue != val:
+ files.add(key)
+ files.update(precmanifest) # add missing files
+ # commit it
+ if files: # something to commit!
+ def filectxfn(repo, ctx, path):
+ if path in bumped:
+ fctx = bumped[path]
+ flags = fctx.flags()
+ mctx = compat.memfilectx(repo, ctx, fctx, flags, copied, path)
+ return mctx
+ return None
+ text = '%s update to %s:\n\n' % (TROUBLES['PHASEDIVERGENT'], prec)
+ text += bumped.description()
+
+ new = context.memctx(repo,
+ parents=[prec.node(), node.nullid],
+ text=text,
+ files=files,
+ filectxfn=filectxfn,
+ user=bumped.user(),
+ date=bumped.date(),
+ extra=bumped.extra())
+
+ newid = repo.commitctx(new)
+ if newid is None:
+ obsolete.createmarkers(repo, [(tmpctx, ())])
+ newid = prec.node()
+ else:
+ phases.retractboundary(repo, tr, bumped.phase(), [newid])
+ obsolete.createmarkers(repo, [(tmpctx, (repo[newid],))],
+ flag=obsolete.bumpedfix)
+ bmupdate(newid)
+ repo.ui.status(_('committed as %s\n') % node.short(newid))
+ # reroute the working copy parent to the new changeset
+ with repo.dirstate.parentchange():
+ repo.dirstate.setparents(newid, node.nullid)
+ return (True, newid)
+
+def _solvedivergent(ui, repo, divergent, dryrun=False, confirm=False,
+ progresscb=None):
+ """tries to solve content-divergence of a changeset
+
+ returns a tuple (bool, newnode) where,
+ bool: a boolean value indicating whether the instability was solved
+ newnode: if bool is True, then the newnode of the resultant commit
+ formed. newnode can be node, when resolution led to no new
+ commit. If bool is False, this is ''.
+ """
+ repo = repo.unfiltered()
+ divergent = repo[divergent.rev()]
+ base, others = divergentdata(divergent)
+ if len(others) > 1:
+ othersstr = "[%s]" % (','.join([str(i) for i in others]))
+ msg = _("skipping %d:%s with a changeset that got split"
+ " into multiple ones:\n"
+ "|[%s]\n"
+ "| This is not handled by automatic evolution yet\n"
+ "| You have to fallback to manual handling with commands "
+ "such as:\n"
+ "| - hg touch -D\n"
+ "| - hg prune\n"
+ "| \n"
+ "| You should contact your local evolution Guru for help.\n"
+ ) % (divergent, TROUBLES['CONTENTDIVERGENT'], othersstr)
+ ui.write_err(msg)
+ return (False, '')
+ other = others[0]
+ if len(other.parents()) > 1:
+ msg = _("skipping %s: %s changeset can't be "
+ "a merge (yet)\n") % (divergent, TROUBLES['CONTENTDIVERGENT'])
+ ui.write_err(msg)
+ hint = _("You have to fallback to solving this by hand...\n"
+ "| This probably means redoing the merge and using \n"
+ "| `hg prune` to kill older version.\n")
+ ui.write_err(hint)
+ return (False, '')
+ if other.p1() not in divergent.parents():
+ msg = _("skipping %s: have a different parent than %s "
+ "(not handled yet)\n") % (divergent, other)
+ hint = _("| %(d)s, %(o)s are not based on the same changeset.\n"
+ "| With the current state of its implementation, \n"
+ "| evolve does not work in that case.\n"
+ "| rebase one of them next to the other and run \n"
+ "| this command again.\n"
+ "| - either: hg rebase --dest 'p1(%(d)s)' -r %(o)s\n"
+ "| - or: hg rebase --dest 'p1(%(o)s)' -r %(d)s\n"
+ ) % {'d': divergent, 'o': other}
+ ui.write_err(msg)
+ ui.write_err(hint)
+ return (False, '')
+
+ displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate})
+ if not ui.quiet or confirm:
+ ui.write(_('merge:'))
+ displayer.show(divergent)
+ ui.write(_('with: '))
+ displayer.show(other)
+ ui.write(_('base: '))
+ displayer.show(base)
+ if confirm and ui.prompt(_('perform evolve? [Ny]'), 'n') != 'y':
+ raise error.Abort(_('evolve aborted by user'))
+ if dryrun:
+ ui.write(('hg update -c %s &&\n' % divergent))
+ ui.write(('hg merge %s &&\n' % other))
+ ui.write(('hg commit -m "auto merge resolving conflict between '
+ '%s and %s"&&\n' % (divergent, other)))
+ ui.write(('hg up -C %s &&\n' % base))
+ ui.write(('hg revert --all --rev tip &&\n'))
+ ui.write(('hg commit -m "`hg log -r %s --template={desc}`";\n'
+ % divergent))
+ return (False, '')
+ if divergent not in repo[None].parents():
+ repo.ui.status(_('updating to "local" conflict\n'))
+ hg.update(repo, divergent.rev())
+ repo.ui.note(_('merging %s changeset\n') % TROUBLES['CONTENTDIVERGENT'])
+ if progresscb:
+ progresscb()
+ stats = merge.update(repo,
+ other.node(),
+ branchmerge=True,
+ force=False,
+ ancestor=base.node(),
+ mergeancestor=True)
+ hg._showstats(repo, stats)
+ if stats[3]:
+ repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
+ "or 'hg update -C .' to abort\n"))
+ if stats[3] > 0:
+ raise error.Abort('merge conflict between several amendments '
+ '(this is not automated yet)',
+ hint="""/!\ You can try:
+/!\ * manual merge + resolve => new cset X
+/!\ * hg up to the parent of the amended changeset (which are named W and Z)
+/!\ * hg revert --all -r X
+/!\ * hg ci -m "same message as the amended changeset" => new cset Y
+/!\ * hg prune -n Y W Z
+""")
+ if progresscb:
+ progresscb()
+ emtpycommitallowed = repo.ui.backupconfig('ui', 'allowemptycommit')
+ tr = repo.currenttransaction()
+ assert tr is not None
+ try:
+ repo.ui.setconfig('ui', 'allowemptycommit', True, 'evolve')
+ with repo.dirstate.parentchange():
+ repo.dirstate.setparents(divergent.node(), node.nullid)
+ oldlen = len(repo)
+ cmdrewrite.amend(ui, repo, message='', logfile='')
+ if oldlen == len(repo):
+ new = divergent
+ # no changes
+ else:
+ new = repo['.']
+ obsolete.createmarkers(repo, [(other, (new,))])
+ phases.retractboundary(repo, tr, other.phase(), [new.node()])
+ return (True, new.node())
+ finally:
+ repo.ui.restoreconfig(emtpycommitallowed)