--- a/.hgtags Sat Jan 20 12:38:10 2018 +0100
+++ b/.hgtags Wed Mar 21 16:32:54 2018 +0100
@@ -63,3 +63,4 @@
c4940c22d76b9c6b3c2117a3b490f3c4fd796972 7.0.1
06a3cb59495636df8b567e49a0fd7fd8fd823074 7.1.0
bf6b859807bac23752a26e58876fe3a4a9a2fef8 7.2.0
+6c772398eb4e209914e1074cdac4f3ebf714e437 7.2.1
--- a/CHANGELOG Sat Jan 20 12:38:10 2018 +0100
+++ b/CHANGELOG Wed Mar 21 16:32:54 2018 +0100
@@ -1,6 +1,21 @@
Changelog
=========
+7.3.0 -- 2018-03-21
+---------------------
+
+ * grab: new command to grab a changeset, put in on wdir parent
+ and update to it
+ * resolve: shows how to continue evolve after resolving all conflicts
+ * evolve: `--continue` flag now continue evolving all the remaining revisions
+ * prev and next now prompts user to choose a changeset in case of ambiguity
+ * evolve: a new `--stop` flag which can be used to stop interrupted evolution
+
+ * fold: fix issue related to bookmarks movement (issue5772)
+ * amend: take lock before parsing the commit description (issue5266)
+ * legacy: respect 'server.bundle1' config if any is set
+ * previous: fix behavior on obsolete rev when topic is involved (issue5708)
+
7.2.1 --2018-01-20
-------------------
--- a/debian/changelog Sat Jan 20 12:38:10 2018 +0100
+++ b/debian/changelog Wed Mar 21 16:32:54 2018 +0100
@@ -1,3 +1,9 @@
+mercurial-evolve (7.3.0-1) unstable; urgency=medium
+
+ * New upstream release
+
+ -- Pierre-Yves David <pierre-yves.david@ens-lyon.org> Wed, 21 Mar 2018 15:34:15 +0100
+
mercurial-evolve (7.2.1-1) unstable; urgency=medium
* new upstream release
--- a/docs/index.rst Sat Jan 20 12:38:10 2018 +0100
+++ b/docs/index.rst Wed Mar 21 16:32:54 2018 +0100
@@ -157,6 +157,8 @@
or care about the new features added by evolution won't be negatively impacted
by the new default.
+You can find the `evolution roadmap in the wiki`_.
+
.. # .. _`this query`: https://bz.mercurial-scm.org/buglist.cgi?component=evolution&bug_status=UNCONFIRMED&bug_status=CONFIRMED&bug_status=NEED_EXAMPLE
Resources
@@ -173,3 +175,4 @@
.. _`sharing mutable history`: sharing.html
.. _`concepts`: concepts.html
.. _`MQ migration guide`: from-mq.html
+.. _`evolution roadmap in the wiki`: https://www.mercurial-scm.org/wiki/CEDRoadMap
--- a/hgext3rd/evolve/__init__.py Sat Jan 20 12:38:10 2018 +0100
+++ b/hgext3rd/evolve/__init__.py Wed Mar 21 16:32:54 2018 +0100
@@ -252,11 +252,7 @@
evolution=all
""".strip()
-
-import os
import sys
-import re
-import collections
import struct
try:
@@ -287,23 +283,18 @@
cmdutil,
commands,
context,
- copies,
dirstate,
error,
extensions,
help,
hg,
lock as lockmod,
- merge,
node,
- obsolete,
patch,
- phases,
revset,
scmutil,
)
-from mercurial.commands import mergetoolopts
from mercurial.i18n import _
from mercurial.node import nullid
@@ -312,7 +303,8 @@
compat,
debugcmd,
cmdrewrite,
- evolvestate,
+ state,
+ evolvecmd,
exthelper,
metadata,
obscache,
@@ -330,8 +322,6 @@
minimumhgversion = metadata.minimumhgversion
buglink = metadata.buglink
-sha1re = re.compile(r'\b[0-9a-f]{6,40}\b')
-
# Flags for enabling optional parts of evolve
commandopt = 'allnewcommands'
@@ -345,6 +335,7 @@
'evolve.date': 'cyan',
'evolve.current_rev': 'bold',
'evolve.verb': '',
+ 'evolve.operation': 'bold'
}
_pack = struct.pack
@@ -353,7 +344,6 @@
aliases, entry = cmdutil.findcmd('commit', commands.table)
commitopts3 = cmdrewrite.commitopts3
interactiveopt = cmdrewrite.interactiveopt
-_bookmarksupdater = rewriteutil.bookmarksupdater
rewrite = rewriteutil.rewrite
# This extension contains the following code
@@ -365,6 +355,7 @@
eh = exthelper.exthelper()
eh.merge(debugcmd.eh)
+eh.merge(evolvecmd.eh)
eh.merge(obsexchange.eh)
eh.merge(checkheads.eh)
eh.merge(safeguard.eh)
@@ -424,7 +415,9 @@
evolveopts = ['all']
repo.ui.setconfig('experimental', 'evolution', evolveopts, 'evolve')
if obsolete.isenabled(repo, 'exchange'):
- repo.ui.setconfig('server', 'bundle1', False)
+ # if no config explicitly set, disable bundle1
+ if not isinstance(repo.ui.config('server', 'bundle1'), str):
+ repo.ui.setconfig('server', 'bundle1', False)
class trdescrepo(repo.__class__):
@@ -520,18 +513,6 @@
ui.setconfig('alias', 'odiff',
"diff --hidden --rev 'limit(precursors(.),1)' --rev .",
'evolve')
- if ui.config('alias', 'grab') is None:
- if os.name == 'nt':
- hgexe = ('"%s"' % util.hgexecutable())
- ui.setconfig('alias', 'grab', "! " + hgexe
- + " rebase --dest . --rev $@ && "
- + hgexe + " up tip",
- 'evolve')
- else:
- ui.setconfig('alias', 'grab',
- "! $HG rebase --dest . --rev $@ && $HG up tip",
- 'evolve')
-
### Troubled revset symbol
@@ -710,7 +691,8 @@
ui.warn(msg % shortnode)
# Check that evolve is activated for performance reasons
- if ui.quiet or not obsolete.isenabled(repo, commandopt):
+ evolvecommandenabled = any('evolve' in e for e in cmdtable)
+ if ui.quiet or not evolvecommandenabled:
return
# Show a warning for helping the user to solve the issue
@@ -853,8 +835,8 @@
raise
def summaryhook(ui, repo):
- state = evolvestate.evolvestate(repo)
- if state:
+ evolvestate = state.cmdstate(repo)
+ if evolvestate:
# i18n: column positioning for "hg summary"
ui.status(_('evolve: (evolve --continue)\n'))
@@ -888,79 +870,6 @@
### Old Evolve extension content ###
#####################################################################
-# XXX need clean up and proper sorting in other section
-
-### changeset rewriting logic
-#############################
-
-class MergeFailure(error.Abort):
- pass
-
-def relocate(repo, orig, dest, pctx=None, keepbranch=False):
- """rewrite <rev> on dest"""
- if orig.rev() == dest.rev():
- raise error.Abort(_('tried to relocate a node on top of itself'),
- hint=_("This shouldn't happen. If you still "
- "need to move changesets, please do so "
- "manually with nothing to rebase - working "
- "directory parent is also destination"))
-
- if pctx is None:
- if len(orig.parents()) == 2:
- raise error.Abort(_("tried to relocate a merge commit without "
- "specifying which parent should be moved"),
- hint=_("Specify the parent by passing in pctx"))
- pctx = orig.p1()
-
- commitmsg = orig.description()
-
- cache = {}
- sha1s = re.findall(sha1re, commitmsg)
- unfi = repo.unfiltered()
- for sha1 in sha1s:
- ctx = None
- try:
- ctx = unfi[sha1]
- except error.RepoLookupError:
- continue
-
- if not ctx.obsolete():
- continue
-
- successors = compat.successorssets(repo, ctx.node(), cache)
-
- # We can't make any assumptions about how to update the hash if the
- # cset in question was split or diverged.
- if len(successors) == 1 and len(successors[0]) == 1:
- newsha1 = node.hex(successors[0][0])
- commitmsg = commitmsg.replace(sha1, newsha1[:len(sha1)])
- else:
- repo.ui.note(_('The stale commit message reference to %s could '
- 'not be updated\n') % sha1)
-
- tr = repo.currenttransaction()
- assert tr is not None
- try:
- r = _evolvemerge(repo, orig, dest, pctx, keepbranch)
- if r[-1]: # some conflict
- raise error.Abort(_('unresolved merge conflicts '
- '(see hg help resolve)'))
- nodenew = _relocatecommit(repo, orig, commitmsg)
- except error.Abort as exc:
- with repo.dirstate.parentchange():
- repo.setparents(repo['.'].node(), nullid)
- repo.dirstate.write(tr)
- # fix up dirstate for copies and renames
- compat.duplicatecopies(repo, repo[None], dest.rev(), orig.p1().rev())
-
- class LocalMergeFailure(MergeFailure, exc.__class__):
- pass
- exc.__class__ = LocalMergeFailure
- tr.close() # to keep changes in this transaction (e.g. dirstate)
- raise
- _finalizerelocate(repo, orig, dest, nodenew, tr)
- return nodenew
-
### new command
#############################
@@ -1038,1009 +947,6 @@
_deprecatealias('gup', 'next')
_deprecatealias('gdown', 'previous')
-def _solveone(ui, repo, ctx, dryrun, confirm, progresscb, category):
- """Resolve the troubles affecting one revision"""
- wlock = lock = tr = None
- try:
- wlock = repo.wlock()
- lock = repo.lock()
- tr = repo.transaction("evolve")
- if 'orphan' == category:
- result = _solveunstable(ui, repo, ctx, dryrun, confirm, progresscb)
- elif 'phasedivergent' == category:
- result = _solvebumped(ui, repo, ctx, dryrun, confirm, progresscb)
- elif 'contentdivergent' == category:
- result = _solvedivergent(ui, repo, ctx, dryrun, confirm,
- progresscb)
- else:
- assert False, "unknown trouble category: %s" % (category)
- tr.close()
- return result
- finally:
- lockmod.release(tr, lock, wlock)
-
-def _handlenotrouble(ui, repo, allopt, revopt, anyopt, targetcat):
- """Used by the evolve function to display an error message when
- no troubles can be resolved"""
- troublecategories = ['phasedivergent', 'contentdivergent', 'orphan']
- unselectedcategories = [c for c in troublecategories if c != targetcat]
- msg = None
- hint = None
-
- troubled = {
- "orphan": repo.revs("orphan()"),
- "contentdivergent": repo.revs("contentdivergent()"),
- "phasedivergent": repo.revs("phasedivergent()"),
- "all": repo.revs("troubled()"),
- }
-
- hintmap = {
- 'phasedivergent': _("do you want to use --phase-divergent"),
- 'phasedivergent+contentdivergent': _("do you want to use "
- "--phase-divergent or"
- " --content-divergent"),
- 'phasedivergent+orphan': _("do you want to use --phase-divergent"
- " or --orphan"),
- 'contentdivergent': _("do you want to use --content-divergent"),
- 'contentdivergent+orphan': _("do you want to use --content-divergent"
- " or --orphan"),
- 'orphan': _("do you want to use --orphan"),
- 'any+phasedivergent': _("do you want to use --any (or --rev) and"
- " --phase-divergent"),
- 'any+phasedivergent+contentdivergent': _("do you want to use --any"
- " (or --rev) and"
- " --phase-divergent or"
- " --content-divergent"),
- 'any+phasedivergent+orphan': _("do you want to use --any (or --rev)"
- " and --phase-divergent or --orphan"),
- 'any+contentdivergent': _("do you want to use --any (or --rev) and"
- " --content-divergent"),
- 'any+contentdivergent+orphan': _("do you want to use --any (or --rev)"
- " and --content-divergent or "
- "--orphan"),
- 'any+orphan': _("do you want to use --any (or --rev)"
- "and --orphan"),
- }
-
- if revopt:
- revs = scmutil.revrange(repo, revopt)
- if not revs:
- msg = _("set of specified revisions is empty")
- else:
- msg = _("no %s changesets in specified revisions") % targetcat
- othertroubles = []
- for cat in unselectedcategories:
- if revs & troubled[cat]:
- othertroubles.append(cat)
- if othertroubles:
- hint = hintmap['+'.join(othertroubles)]
-
- elif anyopt:
- msg = _("no %s changesets to evolve") % targetcat
- othertroubles = []
- for cat in unselectedcategories:
- if troubled[cat]:
- othertroubles.append(cat)
- if othertroubles:
- hint = hintmap['+'.join(othertroubles)]
-
- else:
- # evolve without any option = relative to the current wdir
- if targetcat == 'orphan':
- msg = _("nothing to evolve on current working copy parent")
- else:
- msg = _("current working copy parent is not %s") % targetcat
-
- p1 = repo['.'].rev()
- othertroubles = []
- for cat in unselectedcategories:
- if p1 in troubled[cat]:
- othertroubles.append(cat)
- if othertroubles:
- hint = hintmap['+'.join(othertroubles)]
- else:
- length = len(troubled[targetcat])
- if length:
- hint = _("%d other %s in the repository, do you want --any "
- "or --rev") % (length, targetcat)
- else:
- othertroubles = []
- for cat in unselectedcategories:
- if troubled[cat]:
- othertroubles.append(cat)
- if othertroubles:
- hint = hintmap['any+' + ('+'.join(othertroubles))]
- else:
- msg = _("no troubled changesets")
-
- assert msg is not None
- ui.write_err("%s\n" % msg)
- if hint:
- ui.write_err("(%s)\n" % hint)
- return 2
- else:
- return 1
-
-def _cleanup(ui, repo, startnode, showprogress):
- if showprogress:
- ui.progress(_('evolve'), None)
- if repo['.'] != startnode:
- ui.status(_('working directory is now at %s\n') % repo['.'])
-
-class MultipleSuccessorsError(RuntimeError):
- """Exception raised by _singlesuccessor when multiple successor sets exists
-
- The object contains the list of successorssets in its 'successorssets'
- attribute to call to easily recover.
- """
-
- def __init__(self, successorssets):
- self.successorssets = successorssets
-
-def _singlesuccessor(repo, p):
- """returns p (as rev) if not obsolete or its unique latest successors
-
- fail if there are no such successor"""
-
- if not p.obsolete():
- return p.rev()
- obs = repo[p]
- ui = repo.ui
- newer = compat.successorssets(repo, obs.node())
- # search of a parent which is not killed
- while not 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 or len(newer[0]) > 1:
- raise MultipleSuccessorsError(newer)
-
- return repo[newer[0][0]].rev()
-
-def builddependencies(repo, revs):
- """returns dependency graphs giving an order to solve instability of revs
- (see _orderrevs for more information on usage)"""
-
- # For each troubled revision we keep track of what instability if any should
- # be resolved in order to resolve it. Example:
- # dependencies = {3: [6], 6:[]}
- # Means that: 6 has no dependency, 3 depends on 6 to be solved
- dependencies = {}
- # rdependencies is the inverted dict of dependencies
- rdependencies = collections.defaultdict(set)
-
- for r in revs:
- dependencies[r] = set()
- for p in repo[r].parents():
- try:
- succ = _singlesuccessor(repo, p)
- except MultipleSuccessorsError as exc:
- dependencies[r] = exc.successorssets
- continue
- if succ in revs:
- dependencies[r].add(succ)
- rdependencies[succ].add(r)
- return dependencies, rdependencies
-
-def _dedupedivergents(repo, revs):
- """Dedupe the divergents revs in revs to get one from each group with the
- lowest revision numbers
- """
- repo = repo.unfiltered()
- res = set()
- # To not reevaluate divergents of the same group once one is encountered
- discarded = set()
- for rev in revs:
- if rev in discarded:
- continue
- divergent = repo[rev]
- base, others = divergentdata(divergent)
- othersrevs = [o.rev() for o in others]
- res.add(min([divergent.rev()] + othersrevs))
- discarded.update(othersrevs)
- return res
-
-instabilities_map = {
- 'contentdivergent': "content-divergent",
- 'phasedivergent': "phase-divergent"
-}
-
-def _selectrevs(repo, allopt, revopt, anyopt, targetcat):
- """select troubles in repo matching according to given options"""
- revs = set()
- if allopt or revopt:
- revs = repo.revs("%s()" % targetcat)
- if revopt:
- revs = scmutil.revrange(repo, revopt) & revs
- elif not anyopt:
- topic = getattr(repo, 'currenttopic', '')
- if topic:
- revs = repo.revs('topic(%s)', topic) & revs
- elif targetcat == 'orphan':
- revs = _aspiringdescendant(repo,
- repo.revs('(.::) - obsolete()::'))
- revs = set(revs)
- if targetcat == 'contentdivergent':
- # Pick one divergent per group of divergents
- revs = _dedupedivergents(repo, revs)
- elif anyopt:
- revs = repo.revs('first(%s())' % (targetcat))
- elif targetcat == 'orphan':
- revs = set(_aspiringchildren(repo, repo.revs('(.::) - obsolete()::')))
- if 1 < len(revs):
- msg = "multiple evolve candidates"
- hint = (_("select one of %s with --rev")
- % ', '.join([str(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()])
- return revs
-
-
-def _orderrevs(repo, revs):
- """Compute an ordering to solve instability for the given revs
-
- revs is a list of unstable revisions.
-
- Returns the same revisions ordered to solve their instability from the
- bottom to the top of the stack that the stabilization process will produce
- eventually.
-
- This ensures the minimal number of stabilizations, as we can stabilize each
- revision on its final stabilized destination.
- """
- # Step 1: Build the dependency graph
- dependencies, rdependencies = builddependencies(repo, revs)
- # Step 2: Build the ordering
- # Remove the revisions with no dependency(A) and add them to the ordering.
- # Removing these revisions leads to new revisions with no dependency (the
- # one depending on A) that we can remove from the dependency graph and add
- # to the ordering. We progress in a similar fashion until the ordering is
- # built
- solvablerevs = collections.deque([r for r in sorted(dependencies.keys())
- if not dependencies[r]])
- ordering = []
- while solvablerevs:
- rev = solvablerevs.popleft()
- for dependent in rdependencies[rev]:
- dependencies[dependent].remove(rev)
- if not dependencies[dependent]:
- solvablerevs.append(dependent)
- del dependencies[rev]
- ordering.append(rev)
-
- ordering.extend(sorted(dependencies))
- return ordering
-
-def divergentsets(repo, ctx):
- """Compute sets of commits divergent with a given one"""
- cache = {}
- base = {}
- for n in compat.allprecursors(repo.obsstore, [ctx.node()]):
- if n == ctx.node():
- # a node can't be a base for divergence with itself
- continue
- nsuccsets = compat.successorssets(repo, n, cache)
- for nsuccset in nsuccsets:
- if ctx.node() in nsuccset:
- # we are only interested in *other* successor sets
- continue
- if tuple(nsuccset) in base:
- # we already know the latest base for this divergency
- continue
- base[tuple(nsuccset)] = n
- divergence = []
- for divset, b in base.iteritems():
- divergence.append({
- 'divergentnodes': divset,
- 'commonprecursor': b
- })
-
- return divergence
-
-def _preparelistctxs(items, condition):
- return [item.hex() for item in items if condition(item)]
-
-def _formatctx(fm, ctx):
- fm.data(node=ctx.hex())
- fm.data(desc=ctx.description())
- fm.data(date=ctx.date())
- fm.data(user=ctx.user())
-
-def listtroubles(ui, repo, troublecategories, **opts):
- """Print all the troubles for the repo (or given revset)"""
- troublecategories = troublecategories or ['contentdivergent', 'orphan', 'phasedivergent']
- showunstable = 'orphan' in troublecategories
- showbumped = 'phasedivergent' in troublecategories
- showdivergent = 'contentdivergent' in troublecategories
-
- revs = repo.revs('+'.join("%s()" % t for t in troublecategories))
- if opts.get('rev'):
- revs = scmutil.revrange(repo, opts.get('rev'))
-
- fm = ui.formatter('evolvelist', opts)
- for rev in revs:
- ctx = repo[rev]
- unpars = _preparelistctxs(ctx.parents(), lambda p: p.orphan())
- obspars = _preparelistctxs(ctx.parents(), lambda p: p.obsolete())
- imprecs = _preparelistctxs(repo.set("allprecursors(%n)", ctx.node()),
- lambda p: not p.mutable())
- dsets = divergentsets(repo, ctx)
-
- fm.startitem()
- # plain formatter section
- hashlen, desclen = 12, 60
- desc = ctx.description()
- if desc:
- desc = desc.splitlines()[0]
- desc = (desc[:desclen] + '...') if len(desc) > desclen else desc
- fm.plain('%s: ' % ctx.hex()[:hashlen])
- fm.plain('%s\n' % desc)
- fm.data(node=ctx.hex(), rev=ctx.rev(), desc=desc, phase=ctx.phasestr())
-
- for unpar in unpars if showunstable else []:
- fm.plain(' %s: %s (%s parent)\n' % (TROUBLES['ORPHAN'],
- unpar[:hashlen],
- TROUBLES['ORPHAN']))
- for obspar in obspars if showunstable else []:
- fm.plain(' %s: %s (obsolete parent)\n' % (TROUBLES['ORPHAN'],
- obspar[:hashlen]))
- for imprec in imprecs if showbumped else []:
- fm.plain(' %s: %s (immutable precursor)\n' %
- (TROUBLES['PHASEDIVERGENT'], imprec[:hashlen]))
-
- if dsets and showdivergent:
- for dset in dsets:
- fm.plain(' %s: ' % TROUBLES['CONTENTDIVERGENT'])
- first = True
- for n in dset['divergentnodes']:
- t = "%s (%s)" if first else " %s (%s)"
- first = False
- fm.plain(t % (node.hex(n)[:hashlen], repo[n].phasestr()))
- comprec = node.hex(dset['commonprecursor'])[:hashlen]
- fm.plain(" (precursor %s)\n" % comprec)
- fm.plain("\n")
-
- # templater-friendly section
- _formatctx(fm, ctx)
- troubles = []
- for unpar in unpars:
- troubles.append({'troubletype': TROUBLES['ORPHAN'],
- 'sourcenode': unpar, 'sourcetype': 'orphanparent'})
- for obspar in obspars:
- troubles.append({'troubletype': TROUBLES['ORPHAN'],
- 'sourcenode': obspar,
- 'sourcetype': 'obsoleteparent'})
- for imprec in imprecs:
- troubles.append({'troubletype': TROUBLES['PHASEDIVERGENT'],
- 'sourcenode': imprec,
- 'sourcetype': 'immutableprecursor'})
- for dset in dsets:
- divnodes = [{'node': node.hex(n),
- 'phase': repo[n].phasestr(),
- } for n in dset['divergentnodes']]
- troubles.append({'troubletype': TROUBLES['CONTENTDIVERGENT'],
- 'commonprecursor': node.hex(dset['commonprecursor']),
- 'divergentnodes': divnodes})
- fm.data(troubles=troubles)
-
- fm.end()
-
-@eh.command(
- '^evolve|stabilize|solve',
- [('n', 'dry-run', False,
- _('do not perform actions, just print what would be done')),
- ('', 'confirm', False,
- _('ask for confirmation before performing the action')),
- ('A', 'any', False,
- _('also consider troubled changesets unrelated to current working '
- 'directory')),
- ('r', 'rev', [], _('solves troubles of these revisions')),
- ('', 'bumped', False, _('solves only bumped changesets')),
- ('', 'phase-divergent', False, _('solves only phase-divergent changesets')),
- ('', 'divergent', False, _('solves only divergent changesets')),
- ('', 'content-divergent', False, _('solves only content-divergent changesets')),
- ('', 'unstable', False, _('solves only unstable changesets')),
- ('', 'orphan', False, _('solves only orphan changesets (default)')),
- ('a', 'all', False, _('evolve all troubled changesets related to the '
- 'current working directory and its descendants')),
- ('c', 'continue', False, _('continue an interrupted evolution')),
- ('l', 'list', False, 'provide details on troubled changesets in the repo'),
- ] + mergetoolopts,
- _('[OPTIONS]...')
-)
-def evolve(ui, repo, **opts):
- """solve troubled changesets in your repository
-
- Modifying history can lead to various types of troubled changesets:
- unstable, bumped, or divergent. The evolve command resolves your troubles
- by executing one of the following actions:
-
- - update working copy to a successor
- - rebase an unstable changeset
- - extract the desired changes from a bumped changeset
- - fuse divergent changesets back together
-
- If you pass no arguments, evolve works in automatic mode: it will execute a
- single action to reduce instability related to your working copy. There are
- two cases for this action. First, if the parent of your working copy is
- obsolete, evolve updates to the parent's successor. Second, if the working
- copy parent is not obsolete but has obsolete predecessors, then evolve
- determines if there is an unstable changeset that can be rebased onto the
- working copy parent in order to reduce instability.
- If so, evolve rebases that changeset. If not, evolve refuses to guess your
- intention, and gives a hint about what you might want to do next.
-
- Any time evolve creates a changeset, it updates the working copy to the new
- changeset. (Currently, every successful evolve operation involves an update
- as well; this may change in future.)
-
- Automatic mode only handles common use cases. For example, it avoids taking
- action in the case of ambiguity, and it ignores unstable changesets that
- are not related to your working copy.
- It also refuses to solve bumped or divergent changesets unless you
- explicitly request such behavior (see below).
-
- Eliminating all instability around your working copy may require multiple
- invocations of :hg:`evolve`. Alternately, use ``--all`` to recursively
- select and evolve all unstable changesets that can be rebased onto the
- working copy parent.
- This is more powerful than successive invocations, since ``--all`` handles
- ambiguous cases (e.g. unstable changesets with multiple children) by
- evolving all branches.
-
- When your repository cannot be handled by automatic mode, you might need to
- use ``--rev`` to specify a changeset to evolve. For example, if you have
- an unstable changeset that is not related to the working copy parent,
- you could use ``--rev`` to evolve it. Or, if some changeset has multiple
- unstable children, evolve in automatic mode refuses to guess which one to
- evolve; you have to use ``--rev`` in that case.
-
- Alternately, ``--any`` makes evolve search for the next evolvable changeset
- regardless of whether it is related to the working copy parent.
-
- You can supply multiple revisions to evolve multiple troubled changesets
- in a single invocation. In revset terms, ``--any`` is equivalent to ``--rev
- first(unstable())``. ``--rev`` and ``--all`` are mutually exclusive, as are
- ``--rev`` and ``--any``.
-
- ``hg evolve --any --all`` is useful for cleaning up instability across all
- branches, letting evolve figure out the appropriate order and destination.
-
- When you have troubled changesets that are not unstable, :hg:`evolve`
- refuses to consider them unless you specify the category of trouble you
- wish to resolve, with ``--bumped`` or ``--divergent``. These options are
- currently mutually exclusive with each other and with ``--unstable``
- (the default). You can combine ``--bumped`` or ``--divergent`` with
- ``--rev``, ``--all``, or ``--any``.
-
- You can also use the evolve command to list the troubles affecting your
- repository by using the --list flag. You can choose to display only some
- categories of troubles with the --unstable, --divergent or --bumped flags.
- """
-
- opts = _checkevolveopts(repo, opts)
- # Options
- contopt = opts['continue']
- anyopt = opts['any']
- allopt = opts['all']
- startnode = repo['.']
- dryrunopt = opts['dry_run']
- confirmopt = opts['confirm']
- revopt = opts['rev']
-
- troublecategories = ['phase_divergent', 'content_divergent', 'orphan']
- specifiedcategories = [t.replace('_', '')
- for t in troublecategories
- if opts[t]]
- if opts['list']:
- compat.startpager(ui, 'evolve')
- listtroubles(ui, repo, specifiedcategories, **opts)
- return
-
- targetcat = 'orphan'
- 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():
- displayer = cmdutil.show_changeset(ui, repo,
- {'template': shorttemplate})
- # no args and parent is obsolete, update to successors
- try:
- ctx = repo[_singlesuccessor(repo, repo['.'])]
- except MultipleSuccessorsError as exc:
- repo.ui.write_err('parent is obsolete with multiple successors:\n')
- for ln in exc.successorssets:
- for n in ln:
- displayer.show(repo[n])
- return 2
-
- ui.status(_('update:'))
- if not ui.quiet:
- displayer.show(ctx)
-
- if dryrunopt:
- return 0
- res = hg.update(repo, ctx.rev())
- if ctx != startnode:
- ui.status(_('working directory is now at %s\n') % ctx)
- return res
-
- ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'evolve')
- troubled = set(repo.revs('troubled()'))
-
- # Progress handling
- seen = 1
- count = allopt and len(troubled) or 1
- showprogress = allopt
-
- def progresscb():
- if revopt or allopt:
- ui.progress(_('evolve'), seen, unit=_('changesets'), total=count)
-
- # Continuation handling
- if contopt:
- state = evolvestate.evolvestate(repo)
- if not state:
- raise error.Abort('no evolve to continue')
- state.load()
- orig = repo[state['current']]
- with repo.wlock(), repo.lock():
- ctx = orig
- source = ctx.extra().get('source')
- extra = {}
- if source:
- extra['source'] = source
- extra['intermediate-source'] = ctx.hex()
- else:
- extra['source'] = ctx.hex()
- user = ctx.user()
- date = ctx.date()
- message = ctx.description()
- ui.status(_('evolving %d:%s "%s"\n') % (ctx.rev(), ctx,
- message.split('\n', 1)[0]))
- targetphase = max(ctx.phase(), phases.draft)
- overrides = {('phases', 'new-commit'): targetphase}
-
- with repo.ui.configoverride(overrides, 'evolve-continue'):
- node = repo.commit(text=message, user=user,
- date=date, extra=extra)
-
- obsolete.createmarkers(repo, [(ctx, (repo[node],))])
- state.delete()
- return
-
- cmdutil.bailifchanged(repo)
-
- revs = _selectrevs(repo, allopt, revopt, anyopt, targetcat)
-
- if not revs:
- return _handlenotrouble(ui, repo, allopt, revopt, anyopt, targetcat)
-
- # For the progress bar to show
- count = len(revs)
- # Order the revisions
- if targetcat == 'orphan':
- revs = _orderrevs(repo, revs)
- for rev in revs:
- progresscb()
- _solveone(ui, repo, repo[rev], dryrunopt, confirmopt,
- progresscb, targetcat)
- seen += 1
- progresscb()
- _cleanup(ui, repo, startnode, showprogress)
-
-def _checkevolveopts(repo, opts):
- """ check the options passed to `hg evolve` and warn for deprecation warning
- if any """
-
- if opts['continue']:
- if opts['any']:
- raise error.Abort('cannot specify both "--any" and "--continue"')
- if opts['all']:
- raise error.Abort('cannot specify both "--all" and "--continue"')
-
- if opts['rev']:
- if opts['any']:
- raise error.Abort('cannot specify both "--rev" and "--any"')
- if opts['all']:
- raise error.Abort('cannot specify both "--rev" and "--all"')
-
- # Backward compatibility
- if opts['unstable']:
- msg = ("'evolve --unstable' is deprecated, "
- "use 'evolve --orphan'")
- repo.ui.deprecwarn(msg, '4.4')
-
- opts['orphan'] = opts['divergent']
-
- if opts['divergent']:
- msg = ("'evolve --divergent' is deprecated, "
- "use 'evolve --content-divergent'")
- repo.ui.deprecwarn(msg, '4.4')
-
- opts['content_divergent'] = opts['divergent']
-
- if opts['bumped']:
- msg = ("'evolve --bumped' is deprecated, "
- "use 'evolve --phase-divergent'")
- repo.ui.deprecwarn(msg, '4.4')
-
- opts['phase_divergent'] = opts['bumped']
-
- return opts
-
-def _possibledestination(repo, rev):
- """return all changesets that may be a new parent for REV"""
- tonode = repo.changelog.node
- parents = repo.changelog.parentrevs
- torev = repo.changelog.rev
- dest = set()
- tovisit = list(parents(rev))
- while tovisit:
- r = tovisit.pop()
- succsets = compat.successorssets(repo, tonode(r))
- if not succsets:
- tovisit.extend(parents(r))
- else:
- # We should probably pick only one destination from split
- # (case where '1 < len(ss)'), This could be the currently tipmost
- # but logic is less clear when result of the split are now on
- # multiple branches.
- for ss in succsets:
- for n in ss:
- dest.add(torev(n))
- return dest
-
-def _aspiringchildren(repo, revs):
- """Return a list of changectx which can be stabilized on top of pctx or
- one of its descendants. Empty list if none can be found."""
- target = set(revs)
- result = []
- for r in repo.revs('orphan() - %ld', revs):
- dest = _possibledestination(repo, r)
- if target & dest:
- result.append(r)
- return result
-
-def _aspiringdescendant(repo, revs):
- """Return a list of changectx which can be stabilized on top of pctx or
- one of its descendants recursively. Empty list if none can be found."""
- target = set(revs)
- result = set(target)
- paths = collections.defaultdict(set)
- for r in repo.revs('orphan() - %ld', revs):
- for d in _possibledestination(repo, r):
- paths[d].add(r)
-
- result = set(target)
- tovisit = list(revs)
- while tovisit:
- base = tovisit.pop()
- for unstable in paths[base]:
- if unstable not in result:
- tovisit.append(unstable)
- result.add(unstable)
- return sorted(result - target)
-
-def _solveunstable(ui, repo, orig, dryrun=False, confirm=False,
- progresscb=None):
- """Stabilize an unstable changeset"""
- 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 2
- 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 2
- 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)
- else:
- repo.ui.note(todo)
- if progresscb:
- progresscb()
- try:
- relocate(repo, orig, target, pctx, keepbranch)
- except MergeFailure:
- ops = {'current': orig.node()}
- state = evolvestate.evolvestate(repo, opts=ops)
- state.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"""
- 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 2
- 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 2
-
- 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 0
- 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)
-
-def _solvedivergent(ui, repo, divergent, dryrun=False, confirm=False,
- progresscb=None):
- 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 2
- 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 2
- 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 2
-
- 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
- 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()])
- finally:
- repo.ui.restoreconfig(emtpycommitallowed)
-
-def divergentdata(ctx):
- """return base, other part of a conflict
-
- This only return the first one.
-
- XXX this woobly function won't survive XXX
- """
- repo = ctx._repo.unfiltered()
- for base in repo.set('reverse(allprecursors(%d))', ctx):
- newer = compat.successorssets(ctx._repo, base.node())
- # drop filter and solution including the original ctx
- newer = [n for n in newer if n and ctx.node() not in n]
- if newer:
- return base, tuple(ctx._repo[o] for o in newer[0])
- raise error.Abort("base of divergent changeset %s not found" % ctx,
- hint='this case is not yet handled')
-
def _gettopic(ctx):
"""handle topic fetching with or without the extension"""
return getattr(ctx, 'topic', lambda: '')()
@@ -2054,10 +960,10 @@
def _prevupdate(repo, displayer, target, bookmark, dryrun):
if dryrun:
- repo.ui.write(('hg update %s;\n' % target.rev()))
+ repo.ui.write(_('hg update %s;\n') % target)
if bookmark is not None:
- repo.ui.write(('hg bookmark %s -r %s;\n'
- % (bookmark, target.rev())))
+ repo.ui.write(_('hg bookmark %s -r %s;\n')
+ % (bookmark, target))
else:
ret = hg.update(repo, target.rev())
if not ret:
@@ -2086,7 +992,7 @@
# we do not filter in the 1 case to allow prev to t0
if currenttopic and topic and _gettopicidx(p1) != 1:
- parents = [repo[_singlesuccessor(repo, ctx)] if ctx.mutable() else ctx
+ parents = [repo[utility._singlesuccessor(repo, ctx)] if ctx.mutable() else ctx
for ctx in parents]
parents = [ctx for ctx in parents if ctx.topic() == currenttopic]
@@ -2102,16 +1008,22 @@
if movebookmark:
bookmark = repo._activebookmark
else:
- for p in parents:
- displayer.show(p)
- repo.ui.warn(_('multiple parents, explicitly update to one\n'))
+ header = _("multiple parents, choose one to update:")
+ prevs = [p.rev() for p in parents]
+ choosedrev = utility.revselectionprompt(repo.ui, repo, prevs, header)
+ if choosedrev is None:
+ for p in parents:
+ displayer.show(p)
+ repo.ui.warn(_('multiple parents, explicitly update to one\n'))
+ else:
+ target = repo[choosedrev]
return target, bookmark
@eh.command(
'^previous',
[('B', 'move-bookmark', False,
_('move active bookmark after update')),
- ('', 'merge', False, _('bring uncommitted change along')),
+ ('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'))],
@@ -2128,7 +1040,7 @@
wkctx = repo[None]
wparents = wkctx.parents()
if len(wparents) != 1:
- raise error.Abort('merge in progress')
+ raise error.Abort(_('merge in progress'))
if not opts['merge']:
try:
cmdutil.bailifchanged(repo)
@@ -2136,7 +1048,8 @@
exc.hint = _('do you want --merge?')
raise
- displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate})
+ displayer = compat.changesetdisplayer(ui, repo,
+ {'template': shorttemplate})
topic = not opts.get("no_topic", False)
target, bookmark = _findprevtarget(repo, displayer,
@@ -2160,7 +1073,7 @@
'^next',
[('B', 'move-bookmark', False,
_('move active bookmark after update')),
- ('', 'merge', False, _('bring uncommitted change along')),
+ ('m', 'merge', False, _('bring uncommitted change along')),
('', 'evolve', False, _('evolve the next changeset if necessary')),
('', 'no-topic', False, _('ignore topic and move topologically')),
('n', 'dry-run', False,
@@ -2181,7 +1094,7 @@
wkctx = repo[None]
wparents = wkctx.parents()
if len(wparents) != 1:
- raise error.Abort('merge in progress')
+ raise error.Abort(_('merge in progress'))
if not opts['merge']:
try:
cmdutil.bailifchanged(repo)
@@ -2196,41 +1109,25 @@
filtered = [ctx for ctx in children if ctx.topic() != topic]
# XXX N-square membership on children
children = [ctx for ctx in children if ctx not in filtered]
- displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate})
+ displayer = compat.changesetdisplayer(ui, repo,
+ {'template': shorttemplate})
if len(children) == 1:
c = children[0]
- bm = repo._activebookmark
- shouldmove = opts.get('move_bookmark') and bm is not None
- if dryrunopt:
- ui.write(('hg update %s;\n' % c.rev()))
- if shouldmove:
- ui.write(('hg bookmark %s -r %s;\n' % (bm, c.rev())))
+ return _updatetonext(ui, repo, c, displayer, opts)
+ elif children:
+ cheader = _("ambiguous next changeset, choose one to update:")
+ crevs = [c.rev() for c in children]
+ choosedrev = utility.revselectionprompt(ui, repo, crevs, cheader)
+ if choosedrev is None:
+ ui.warn(_("ambiguous next changeset:\n"))
+ for c in children:
+ displayer.show(c)
+ ui.warn(_("explicitly update to one of them\n"))
+ return 1
else:
- ret = hg.update(repo, c.rev())
- if not ret:
- lock = tr = None
- try:
- lock = repo.lock()
- tr = repo.transaction('next')
- if shouldmove:
- bmchanges = [(bm, c.node())]
- compat.bookmarkapplychanges(repo, tr, bmchanges)
- else:
- bookmarksmod.deactivate(repo)
- tr.close()
- finally:
- lockmod.release(tr, lock)
- if not ui.quiet:
- displayer.show(c)
- result = 0
- elif children:
- ui.warn(_("ambiguous next changeset:\n"))
- for c in children:
- displayer.show(c)
- ui.warn(_('explicitly update to one of them\n'))
- result = 1
+ return _updatetonext(ui, repo, repo[choosedrev], displayer, opts)
else:
- aspchildren = _aspiringchildren(repo, [repo['.'].rev()])
+ aspchildren = evolvecmd._aspiringchildren(repo, [repo['.'].rev()])
if topic:
filtered.extend(repo[c] for c in children
if repo[c].topic() != topic)
@@ -2246,26 +1143,67 @@
msg = _('(%i unstable changesets to be evolved here, '
'do you want --evolve?)\n')
ui.warn(msg % len(aspchildren))
- result = 1
+ return 1
elif 1 < len(aspchildren):
- ui.warn(_("ambiguous next (unstable) changeset:\n"))
- for c in aspchildren:
- displayer.show(repo[c])
- ui.warn(_("(run 'hg evolve --rev REV' on one of them)\n"))
- return 1
+ cheader = _("ambiguous next (unstable) changeset, choose one to"
+ " evolve and update:")
+ choosedrev = utility.revselectionprompt(ui, repo,
+ aspchildren, cheader)
+ if choosedrev is None:
+ ui.warn(_("ambiguous next (unstable) changeset:\n"))
+ for c in aspchildren:
+ displayer.show(repo[c])
+ ui.warn(_("(run 'hg evolve --rev REV' on one of them)\n"))
+ return 1
+ else:
+ return _nextevolve(ui, repo, repo[choosedrev], opts)
else:
- cmdutil.bailifchanged(repo)
- result = _solveone(ui, repo, repo[aspchildren[0]], dryrunopt,
- False, lambda: None, category='orphan')
- if not result:
- ui.status(_('working directory now at %s\n')
- % ui.label(str(repo['.']), 'evolve.node'))
- return result
- return 1
- return result
+ return _nextevolve(ui, repo, aspchildren[0], opts)
finally:
lockmod.release(wlock)
+def _nextevolve(ui, repo, aspchildren, opts):
+ """logic for hg next command to evolve and update to an aspiring children"""
+
+ cmdutil.bailifchanged(repo)
+ evolvestate = state.cmdstate(repo, opts={'command': 'next'})
+ result = evolvecmd._solveone(ui, repo, repo[aspchildren],
+ evolvestate, opts.get('dry_run'), False,
+ lambda: None, category='orphan')
+ # making sure a next commit is formed
+ if result[0] and result[1]:
+ ui.status(_('working directory now at %s\n')
+ % ui.label(str(repo['.']), 'evolve.node'))
+ return 0
+
+def _updatetonext(ui, repo, children, 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)
+ if shouldmove:
+ ui.write(_('hg bookmark %s -r %s;\n') % (bm, children))
+ else:
+ ret = hg.update(repo, children)
+ if not ret:
+ lock = tr = None
+ try:
+ lock = repo.lock()
+ tr = repo.transaction('next')
+ if shouldmove:
+ bmchanges = [(bm, children.node())]
+ compat.bookmarkapplychanges(repo, tr, bmchanges)
+ else:
+ bookmarksmod.deactivate(repo)
+ tr.close()
+ finally:
+ lockmod.release(tr, lock)
+ if not ui.quiet:
+ displayer.show(children)
+ return 0
+
@eh.wrapcommand('commit')
def commitwrapper(orig, ui, repo, *arg, **kwargs):
tr = None
@@ -2287,7 +1225,7 @@
oldbookmarks.extend(repo.nodebookmarks(old.node()))
markers.append((old, (new,)))
if markers:
- obsolete.createmarkers(repo, markers)
+ compat.createmarkers(repo, markers, operation="amend")
bmchanges = []
for book in oldbookmarks:
bmchanges.append((book, new.node()))
@@ -2432,47 +1370,6 @@
_helploader))
help.helptable.sort()
-def _relocatecommit(repo, orig, commitmsg):
- if commitmsg is None:
- commitmsg = orig.description()
- extra = dict(orig.extra())
- if 'branch' in extra:
- del extra['branch']
- extra['rebase_source'] = orig.hex()
-
- backup = repo.ui.backupconfig('phases', 'new-commit')
- try:
- targetphase = max(orig.phase(), phases.draft)
- repo.ui.setconfig('phases', 'new-commit', targetphase, 'evolve')
- # Commit might fail if unresolved files exist
- nodenew = repo.commit(text=commitmsg, user=orig.user(),
- date=orig.date(), extra=extra)
- finally:
- repo.ui.restoreconfig(backup)
- return nodenew
-
-def _finalizerelocate(repo, orig, dest, nodenew, tr):
- destbookmarks = repo.nodebookmarks(dest.node())
- nodesrc = orig.node()
- destphase = repo[nodesrc].phase()
- oldbookmarks = repo.nodebookmarks(nodesrc)
- bmchanges = []
-
- if nodenew is not None:
- phases.retractboundary(repo, tr, destphase, [nodenew])
- obsolete.createmarkers(repo, [(repo[nodesrc], (repo[nodenew],))])
- for book in oldbookmarks:
- bmchanges.append((book, nodenew))
- else:
- obsolete.createmarkers(repo, [(repo[nodesrc], ())])
- # Behave like rebase, move bookmarks to dest
- for book in oldbookmarks:
- bmchanges.append((book, dest.node()))
- for book in destbookmarks: # restore bookmark that rebase move
- bmchanges.append((book, dest.node()))
- if bmchanges:
- compat.bookmarkapplychanges(repo, tr, bmchanges)
-
evolvestateversion = 0
@eh.uisetup
@@ -2481,32 +1378,13 @@
_("use 'hg evolve --continue' or 'hg update -C .' to abort"))
cmdutil.unfinishedstates.append(data)
+ afterresolved = ('evolvestate', _('hg evolve --continue'))
+ grabresolved = ('grabstate', _('hg grab --continue'))
+ cmdutil.afterresolvedstates.append(afterresolved)
+ cmdutil.afterresolvedstates.append(grabresolved)
+
@eh.wrapfunction(hg, 'clean')
def clean(orig, repo, *args, **kwargs):
ret = orig(repo, *args, **kwargs)
util.unlinkpath(repo.vfs.join('evolvestate'), ignoremissing=True)
return ret
-
-def _evolvemerge(repo, orig, dest, pctx, keepbranch):
- """Used by the evolve function to merge dest on top of pctx.
- return the same tuple as merge.graft"""
- if repo['.'].rev() != dest.rev():
- merge.update(repo,
- dest,
- branchmerge=False,
- force=True)
- if repo._activebookmark:
- repo.ui.status(_("(leaving bookmark %s)\n") % repo._activebookmark)
- bookmarksmod.deactivate(repo)
- if keepbranch:
- repo.dirstate.setbranch(orig.branch())
- if util.safehasattr(repo, 'currenttopic'):
- # uurrgs
- # there no other topic setter yet
- if not orig.topic() and repo.vfs.exists('topic'):
- repo.vfs.unlink('topic')
- else:
- with repo.vfs.open('topic', 'w') as f:
- f.write(orig.topic())
-
- return merge.graft(repo, orig, pctx, ['destination', 'evolving'], True)
--- a/hgext3rd/evolve/cmdrewrite.py Sat Jan 20 12:38:10 2018 +0100
+++ b/hgext3rd/evolve/cmdrewrite.py Wed Mar 21 16:32:54 2018 +0100
@@ -22,6 +22,7 @@
error,
hg,
lock as lockmod,
+ merge,
node,
obsolete,
patch,
@@ -34,6 +35,7 @@
from . import (
compat,
+ state,
exthelper,
rewriteutil,
utility,
@@ -72,7 +74,7 @@
"""
# N.B. this is extremely similar to setupheaderopts() in mq.py
if not opts.get('date') and opts.get('current_date'):
- opts['date'] = '%d %d' % util.makedate()
+ opts['date'] = '%d %d' % compat.makedate()
if not opts.get('user') and opts.get('current_user'):
opts['user'] = ui.username()
@@ -126,13 +128,13 @@
edit = opts.pop('edit', False)
log = opts.get('logfile')
opts['amend'] = True
- if not (edit or opts['message'] or log):
- opts['message'] = repo['.'].description()
_resolveoptions(ui, opts)
_alias, commitcmd = cmdutil.findcmd('commit', commands.table)
try:
wlock = repo.wlock()
lock = repo.lock()
+ if not (edit or opts['message'] or log):
+ opts['message'] = repo['.'].description()
rewriteutil.precheck(repo, [repo['.'].rev()], action='amend')
return commitcmd[0](ui, repo, *pats, **opts)
finally:
@@ -387,7 +389,8 @@
if opts.get('note'):
metadata['note'] = opts['note']
- obsolete.createmarkers(repo, [(old, (repo[newid],))], metadata=metadata)
+ compat.createmarkers(repo, [(old, (repo[newid],))], metadata=metadata,
+ operation="uncommit")
phases.retractboundary(repo, tr, oldphase, [newid])
if opts.get('revert'):
hg.updaterepo(repo, newid, True)
@@ -425,7 +428,7 @@
fp.seek(0)
newnode = _patchtocommit(ui, repo, old, fp)
# creating obs marker temp -> ()
- obsolete.createmarkers(repo, [(repo[tempnode], ())])
+ compat.createmarkers(repo, [(repo[tempnode], ())], operation="uncommit")
return newnode
def _createtempcommit(ui, repo, old, match):
@@ -609,8 +612,14 @@
root.p2().node()],
commitopts=commitopts)
phases.retractboundary(repo, tr, targetphase, [newid])
- obsolete.createmarkers(repo, [(ctx, (repo[newid],))
- for ctx in allctx], metadata=metadata)
+ compat.createmarkers(repo, [(ctx, (repo[newid],))
+ for ctx in allctx], metadata=metadata,
+ operation="fold")
+ # move bookmarks from old nodes to the new one
+ # XXX: we should make rewriteutil.rewrite() handle such cases
+ for ctx in allctx:
+ bmupdater = rewriteutil.bookmarksupdater(repo, ctx.node(), tr)
+ bmupdater(newid)
tr.close()
finally:
tr.release()
@@ -736,9 +745,9 @@
metadata['note'] = opts['note']
phases.retractboundary(repo, tr, targetphase, [newid])
- obsolete.createmarkers(repo, [(ctx, (repo[newid],))
- for ctx in allctx],
- metadata=metadata)
+ compat.createmarkers(repo, [(ctx, (repo[newid],))
+ for ctx in allctx],
+ metadata=metadata, operation="metaedit")
else:
ui.status(_("nothing changed\n"))
tr.close()
@@ -764,7 +773,7 @@
date = opts.get('date')
user = opts.get('user')
if date:
- metadata['date'] = '%i %i' % util.parsedate(date)
+ metadata['date'] = '%i %i' % compat.parsedate(date)
if user:
metadata['user'] = user
return metadata
@@ -930,12 +939,14 @@
metadata['note'] = opts['note']
# create markers
- obsolete.createmarkers(repo, relations, metadata=metadata)
+ compat.createmarkers(repo, relations, metadata=metadata,
+ operation="prune")
# informs that changeset have been pruned
ui.status(_('%i changesets pruned\n') % len(precs))
- for ctx in repo.unfiltered().set('bookmark() and %ld', precs):
+ precrevs = (precursor.rev() for precursor in precs)
+ for ctx in repo.unfiltered().set('bookmark() and %ld', precrevs):
# used to be:
#
# ldest = list(repo.set('max((::%d) - obsolete())', ctx))
@@ -986,11 +997,11 @@
try:
wlock = repo.wlock()
lock = repo.lock()
- rev = scmutil.revsingle(repo, revarg[0])
+ ctx = scmutil.revsingle(repo, revarg[0])
+ rev = ctx.rev()
cmdutil.bailifchanged(repo)
rewriteutil.precheck(repo, [rev], action='split')
tr = repo.transaction('split')
- ctx = repo[rev]
if len(ctx.parents()) > 1:
raise error.Abort(_("cannot split merge commits"))
@@ -1042,8 +1053,8 @@
metadata = {}
if opts.get('note'):
metadata['note'] = opts['note']
- obsolete.createmarkers(repo, [(repo[rev], newcommits)],
- metadata=metadata)
+ compat.createmarkers(repo, [(repo[rev], newcommits)],
+ metadata=metadata, operation="split")
tr.close()
finally:
# Restore the old branch
@@ -1081,7 +1092,7 @@
if not duplicate:
rewriteutil.precheck(repo, revs, touch)
tmpl = utility.shorttemplate
- displayer = cmdutil.show_changeset(ui, repo, {'template': tmpl})
+ displayer = compat.changesetdisplayer(ui, repo, {'template': tmpl})
wlock = lock = tr = None
try:
wlock = repo.wlock()
@@ -1134,8 +1145,8 @@
metadata = {}
if opts.get('note'):
metadata['note'] = opts['note']
- obsolete.createmarkers(repo, [(ctx, (repo[new],))],
- metadata=metadata)
+ compat.createmarkers(repo, [(ctx, (repo[new],))],
+ metadata=metadata, operation="touch")
phases.retractboundary(repo, tr, ctx.phase(), [new])
if ctx in repo[None].parents():
with repo.dirstate.parentchange():
@@ -1143,3 +1154,94 @@
tr.close()
finally:
lockmod.release(tr, lock, wlock)
+
+@eh.command(
+ 'grab',
+ [('r', 'rev', '', 'revision to grab'),
+ ('c', 'continue', False, 'continue interrupted grab'),
+ ('a', 'abort', False, 'abort interrupted grab'),
+ ],
+ _('[-r] rev'))
+def grab(ui, repo, *revs, **opts):
+ """grabs a commit, move it on the top of working directory parent and
+ updates to it."""
+
+ cont = opts.get('continue')
+ abort = opts.get('abort')
+
+ if cont and abort:
+ raise error.Abort(_("cannot specify both --continue and --abort"))
+
+ revs = list(revs)
+ if opts.get('rev'):
+ revs.append(opts['rev'])
+
+ with repo.wlock(), repo.lock(), repo.transaction('grab'):
+ grabstate = state.cmdstate(repo, path='grabstate')
+ pctx = repo['.']
+
+ if not cont and not abort:
+ cmdutil.bailifchanged(repo)
+ revs = scmutil.revrange(repo, revs)
+ if len(revs) > 1:
+ raise error.Abort(_("specify just one revision"))
+ elif not revs:
+ raise error.Abort(_("empty revision set"))
+
+ origctx = repo[revs.first()]
+
+ if origctx in pctx.ancestors() or origctx.node() == pctx.node():
+ raise error.Abort(_("cannot grab an ancestor revision"))
+
+ rewriteutil.precheck(repo, [origctx.rev()], 'grab')
+
+ ui.status(_('grabbing %d:%s "%s"\n') %
+ (origctx.rev(), origctx,
+ origctx.description().split("\n", 1)[0]))
+ stats = merge.graft(repo, origctx, origctx.p1(), ['local',
+ 'destination'])
+ if stats[3]:
+ grabstate.addopts({'orignode': origctx.node(),
+ 'oldpctx': pctx.node()})
+ grabstate.save()
+ raise error.InterventionRequired(_("unresolved merge conflicts"
+ " (see hg help resolve)"))
+
+ elif abort:
+ if not grabstate:
+ raise error.Abort(_("no interrupted grab state exists"))
+ grabstate.load()
+ pctxnode = grabstate['oldpctx']
+ ui.status(_("aborting grab, updating to %s\n") %
+ node.hex(pctxnode)[:12])
+ hg.updaterepo(repo, pctxnode, True)
+ return 0
+
+ else:
+ if revs:
+ raise error.Abort(_("cannot specify both --continue and "
+ "revision"))
+ if not grabstate:
+ raise error.Abort(_("no interrupted grab state exists"))
+
+ grabstate.load()
+ orignode = grabstate['orignode']
+ origctx = repo[orignode]
+
+ overrides = {('phases', 'new-commit'): origctx.phase()}
+ with repo.ui.configoverride(overrides, 'grab'):
+ newnode = repo.commit(text=origctx.description(),
+ user=origctx.user(),
+ date=origctx.date(), extra=origctx.extra())
+
+ if grabstate:
+ grabstate.delete()
+ newctx = repo[newnode] if newnode else pctx
+ compat.createmarkers(repo, [(origctx, (newctx,))], operation="grab")
+
+ if newnode is None:
+ ui.warn(_("note: grab of %d:%s created no changes to commit\n") %
+ (origctx.rev(), origctx))
+ return 0
+
+ return 0
--- a/hgext3rd/evolve/compat.py Sat Jan 20 12:38:10 2018 +0100
+++ b/hgext3rd/evolve/compat.py Wed Mar 21 16:32:54 2018 +0100
@@ -6,14 +6,21 @@
Compatibility module
"""
+import inspect
+
+import functools
+
from mercurial import (
copies,
context,
hg,
+ mdiff,
obsolete,
revset,
util,
+ wireproto,
)
+from mercurial.hgweb import hgweb_mod
# hg < 4.2 compat
try:
@@ -28,12 +35,35 @@
except ImportError:
obsutil = None
+# hg < 4.6 compat (c8e2d6ed1f9e)
+try:
+ from mercurial import logcmdutil
+ changesetdisplayer = logcmdutil.changesetdisplayer
+ changesetprinter = logcmdutil.changesetprinter
+ displaygraph = logcmdutil.displaygraph
+except (AttributeError, ImportError):
+ from mercurial import cmdutil
+ changesetdisplayer = cmdutil.show_changeset
+ changesetprinter = cmdutil.changeset_printer
+ displaygraph = cmdutil.displaygraph
+
from . import (
exthelper,
)
eh = exthelper.exthelper()
+# Wrap obsolete.creatmarkers and make it accept but ignore "operation" argument
+# for hg < 4.3
+createmarkers = obsolete.createmarkers
+originalcreatemarkers = createmarkers
+while isinstance(originalcreatemarkers, functools.partial):
+ originalcreatemarkers = originalcreatemarkers.func
+if originalcreatemarkers.__code__.co_argcount < 6:
+ def createmarkers(repo, relations, flag=0, date=None, metadata=None,
+ operation=None):
+ return obsolete.createmarkers(repo, relations, flag, date, metadata)
+
if not util.safehasattr(hg, '_copycache'):
# exact copy of relevantmarkers as in Mercurial-176d1a0ce385
# this fixes relevant markers computation for version < hg-4.3
@@ -226,3 +256,40 @@
cachevfs = vfsmod.vfs(repo.vfs.join('cache'))
cachevfs.createmode = repo.store.createmode
return cachevfs
+
+def strdiff(a, b, fn1, fn2):
+ """ A version of mdiff.unidiff for comparing two strings
+ """
+ args = [a, '', b, '', fn1, fn2]
+
+ # hg < 4.6 compat 8b6dd3922f70
+ argspec = inspect.getargspec(mdiff.unidiff)
+
+ if 'binary' in argspec.args:
+ args.append(False)
+
+ return mdiff.unidiff(*args)
+
+# date related
+
+try:
+ import mercurial.utils.dateutil
+ makedate = mercurial.utils.dateutil.makedate
+ parsedate = mercurial.utils.dateutil.parsedate
+except ImportError as e:
+ import mercurial.util
+ makedate = mercurial.util.makedate
+ parsedate = mercurial.util.parsedate
+
+def wireprotocommand(exthelper, name, args='', permission='pull'):
+ if 3 <= len(wireproto.wireprotocommand.func_defaults):
+ return wireproto.wireprotocommand(name, args, permission=permission)
+
+ else:
+ # <= hg-4.5 permission must be registered in dictionnary
+ def decorator(func):
+ @eh.extsetup
+ def install(ui):
+ hgweb_mod.perms[name] = permission
+ wireproto.commands[name] = (func, args)
+ return decorator
--- a/hgext3rd/evolve/depthcache.py Sat Jan 20 12:38:10 2018 +0100
+++ b/hgext3rd/evolve/depthcache.py Wed Mar 21 16:32:54 2018 +0100
@@ -87,11 +87,11 @@
if util.safehasattr(repo, 'updatecaches'):
@localrepo.unfilteredmethod
- def updatecaches(self, tr=None):
+ def updatecaches(self, tr=None, **kwargs):
if utility.shouldwarmcache(self, tr):
self.depthcache.update(self)
self.depthcache.save(self)
- super(depthcacherepo, self).updatecaches(tr)
+ super(depthcacherepo, self).updatecaches(tr, **kwargs)
else:
def transaction(self, *args, **kwargs):
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext3rd/evolve/evolvecmd.py Wed Mar 21 16:32:54 2018 +0100
@@ -0,0 +1,1297 @@
+# Copyright 2011 Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
+# Logilab SA <contact@logilab.fr>
+# Pierre-Yves David <pierre-yves.david@ens-lyon.org>
+# Patrick Mezard <patrick@mezard.eu>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+"""logic related to hg evolve command"""
+
+import collections
+import re
+
+from mercurial import (
+ bookmarks as bookmarksmod,
+ cmdutil,
+ commands,
+ context,
+ copies,
+ error,
+ hg,
+ lock as lockmod,
+ merge,
+ node,
+ obsolete,
+ phases,
+ scmutil,
+ util,
+)
+
+from mercurial.i18n import _
+
+from . import (
+ cmdrewrite,
+ compat,
+ exthelper,
+ rewriteutil,
+ state,
+ utility,
+)
+
+TROUBLES = compat.TROUBLES
+shorttemplate = utility.shorttemplate
+_bookmarksupdater = rewriteutil.bookmarksupdater
+sha1re = re.compile(r'\b[0-9a-f]{6,40}\b')
+
+eh = exthelper.exthelper()
+_bookmarksupdater = rewriteutil.bookmarksupdater
+mergetoolopts = commands.mergetoolopts
+
+def _solveone(ui, repo, ctx, evolvestate, dryrun, confirm,
+ progresscb, category):
+ """Resolve the troubles affecting one revision
+
+ 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 ''.
+ """
+ wlock = lock = tr = None
+ try:
+ wlock = repo.wlock()
+ lock = repo.lock()
+ tr = repo.transaction("evolve")
+ if 'orphan' == category:
+ result = _solveunstable(ui, repo, ctx, evolvestate,
+ dryrun, confirm, progresscb)
+ elif 'phasedivergent' == category:
+ result = _solvebumped(ui, repo, ctx, evolvestate,
+ dryrun, confirm, progresscb)
+ elif 'contentdivergent' == category:
+ result = _solvedivergent(ui, repo, ctx, evolvestate,
+ dryrun, confirm, progresscb)
+ else:
+ assert False, "unknown trouble category: %s" % (category)
+ tr.close()
+ return result
+ finally:
+ lockmod.release(tr, lock, wlock)
+
+def _solveunstable(ui, repo, orig, evolvestate, 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:
+ p1obs = orig.p1().obsolete()
+ p2obs = orig.p2().obsolete()
+ if not p1obs and p2obs:
+ pctx = orig.p2() # second parent is obsolete ?
+ keepbranch = orig.p2().branch() != orig.branch()
+ elif not p2obs and p1obs:
+ pass
+ else:
+ # store that we are resolving an orphan merge with both parents
+ # obsolete and proceed with first parent
+ evolvestate['orphanmerge'] = True
+ # we should process the second parent first, so that in case of
+ # no-conflicts the first parent is processed later and preserved as
+ # first parent
+ pctx = orig.p2()
+ keepbranch = orig.p2().branch() != orig.branch()
+
+ 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:
+ cheader = _("ancestor '%s' split over multiple topological"
+ " branches.\nchoose an evolve destination:") % orig
+ selectedrev = utility.revselectionprompt(ui, repo, list(heads),
+ cheader)
+ if selectedrev is None:
+ msg = _("could not solve instability, "
+ "ambiguous destination: "
+ "parent split across two branches\n")
+ ui.write_err(msg)
+ return (False, '')
+ target = repo[selectedrev]
+ else:
+ target = repo[heads.first()]
+ else:
+ target = targets[0]
+ displayer = compat.changesetdisplayer(ui, repo, {'template': shorttemplate})
+ target = repo[target]
+ if not ui.quiet or confirm:
+ repo.ui.write(_('move:'), label='evolve.operation')
+ 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.addopts(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, evolvestate, 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.rev()).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 = compat.changesetdisplayer(ui, repo, {'template': shorttemplate})
+ if not ui.quiet or confirm:
+ repo.ui.write(_('recreate:'), label='evolve.operation')
+ 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.rev(), prec.rev())):
+ # 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]
+ compat.createmarkers(repo, [(bumped, (tmpctx,))],
+ operation='evolve')
+ 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:
+ compat.createmarkers(repo, [(tmpctx, ())], operation='evolve')
+ newid = prec.node()
+ else:
+ phases.retractboundary(repo, tr, bumped.phase(), [newid])
+ compat.createmarkers(repo, [(tmpctx, (repo[newid],))],
+ flag=obsolete.bumpedfix, operation='evolve')
+ 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, evolvestate, 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 = compat.changesetdisplayer(ui, repo, {'template': shorttemplate})
+ if not ui.quiet or confirm:
+ ui.write(_('merge:'), label='evolve.operation')
+ 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['.']
+ compat.createmarkers(repo, [(other, (new,))], operation='evolve')
+ phases.retractboundary(repo, tr, other.phase(), [new.node()])
+ return (True, new.node())
+ finally:
+ repo.ui.restoreconfig(emtpycommitallowed)
+
+class MergeFailure(error.Abort):
+ pass
+
+def _orderrevs(repo, revs):
+ """Compute an ordering to solve instability for the given revs
+
+ revs is a list of unstable revisions.
+
+ Returns the same revisions ordered to solve their instability from the
+ bottom to the top of the stack that the stabilization process will produce
+ eventually.
+
+ This ensures the minimal number of stabilizations, as we can stabilize each
+ revision on its final stabilized destination.
+ """
+ # Step 1: Build the dependency graph
+ dependencies, rdependencies = utility.builddependencies(repo, revs)
+ # Step 2: Build the ordering
+ # Remove the revisions with no dependency(A) and add them to the ordering.
+ # Removing these revisions leads to new revisions with no dependency (the
+ # one depending on A) that we can remove from the dependency graph and add
+ # to the ordering. We progress in a similar fashion until the ordering is
+ # built
+ solvablerevs = collections.deque([r for r in sorted(dependencies.keys())
+ if not dependencies[r]])
+ ordering = []
+ while solvablerevs:
+ rev = solvablerevs.popleft()
+ for dependent in rdependencies[rev]:
+ dependencies[dependent].remove(rev)
+ if not dependencies[dependent]:
+ solvablerevs.append(dependent)
+ del dependencies[rev]
+ ordering.append(rev)
+
+ ordering.extend(sorted(dependencies))
+ return ordering
+
+def relocate(repo, orig, dest, pctx=None, keepbranch=False):
+ """rewrites the orig rev on dest rev
+
+ returns the node of new commit which is formed
+ """
+ if orig.rev() == dest.rev():
+ raise error.Abort(_('tried to relocate a node on top of itself'),
+ hint=_("This shouldn't happen. If you still "
+ "need to move changesets, please do so "
+ "manually with nothing to rebase - working "
+ "directory parent is also destination"))
+
+ if pctx is None:
+ if len(orig.parents()) == 2:
+ raise error.Abort(_("tried to relocate a merge commit without "
+ "specifying which parent should be moved"),
+ hint=_("Specify the parent by passing in pctx"))
+ pctx = orig.p1()
+
+ commitmsg = orig.description()
+
+ cache = {}
+ sha1s = re.findall(sha1re, commitmsg)
+ unfi = repo.unfiltered()
+ for sha1 in sha1s:
+ ctx = None
+ try:
+ ctx = unfi[sha1]
+ except error.RepoLookupError:
+ continue
+
+ if not ctx.obsolete():
+ continue
+
+ successors = compat.successorssets(repo, ctx.node(), cache)
+
+ # We can't make any assumptions about how to update the hash if the
+ # cset in question was split or diverged.
+ if len(successors) == 1 and len(successors[0]) == 1:
+ newsha1 = node.hex(successors[0][0])
+ commitmsg = commitmsg.replace(sha1, newsha1[:len(sha1)])
+ else:
+ repo.ui.note(_('The stale commit message reference to %s could '
+ 'not be updated\n') % sha1)
+
+ tr = repo.currenttransaction()
+ assert tr is not None
+ try:
+ r = _evolvemerge(repo, orig, dest, pctx, keepbranch)
+ if r[-1]: # some conflict
+ raise error.Abort(_('unresolved merge conflicts '
+ '(see hg help resolve)'))
+ nodenew = _relocatecommit(repo, orig, commitmsg)
+ except error.Abort as exc:
+ with repo.dirstate.parentchange():
+ repo.setparents(repo['.'].node(), node.nullid)
+ repo.dirstate.write(tr)
+ # fix up dirstate for copies and renames
+ compat.duplicatecopies(repo, repo[None], dest.rev(), orig.p1().rev())
+
+ class LocalMergeFailure(MergeFailure, exc.__class__):
+ pass
+ exc.__class__ = LocalMergeFailure
+ tr.close() # to keep changes in this transaction (e.g. dirstate)
+ raise
+ _finalizerelocate(repo, orig, dest, nodenew, tr)
+ return nodenew
+
+def _relocatecommit(repo, orig, commitmsg):
+ if commitmsg is None:
+ commitmsg = orig.description()
+ extra = dict(orig.extra())
+ if 'branch' in extra:
+ del extra['branch']
+ extra['rebase_source'] = orig.hex()
+
+ backup = repo.ui.backupconfig('phases', 'new-commit')
+ try:
+ targetphase = max(orig.phase(), phases.draft)
+ repo.ui.setconfig('phases', 'new-commit', targetphase, 'evolve')
+ # Commit might fail if unresolved files exist
+ nodenew = repo.commit(text=commitmsg, user=orig.user(),
+ date=orig.date(), extra=extra)
+ finally:
+ repo.ui.restoreconfig(backup)
+ return nodenew
+
+def _finalizerelocate(repo, orig, dest, nodenew, tr):
+ destbookmarks = repo.nodebookmarks(dest.node())
+ nodesrc = orig.node()
+ oldbookmarks = repo.nodebookmarks(nodesrc)
+ bmchanges = []
+
+ if nodenew is not None:
+ compat.createmarkers(repo, [(repo[nodesrc], (repo[nodenew],))],
+ operation='evolve')
+ for book in oldbookmarks:
+ bmchanges.append((book, nodenew))
+ else:
+ compat.createmarkers(repo, [(repo[nodesrc], ())], operation='evolve')
+ # Behave like rebase, move bookmarks to dest
+ for book in oldbookmarks:
+ bmchanges.append((book, dest.node()))
+ for book in destbookmarks: # restore bookmark that rebase move
+ bmchanges.append((book, dest.node()))
+ if bmchanges:
+ compat.bookmarkapplychanges(repo, tr, bmchanges)
+
+def _evolvemerge(repo, orig, dest, pctx, keepbranch):
+ """Used by the evolve function to merge dest on top of pctx.
+ return the same tuple as merge.graft"""
+ if repo['.'].rev() != dest.rev():
+ merge.update(repo,
+ dest,
+ branchmerge=False,
+ force=True)
+ if repo._activebookmark:
+ repo.ui.status(_("(leaving bookmark %s)\n") % repo._activebookmark)
+ bookmarksmod.deactivate(repo)
+ if keepbranch:
+ repo.dirstate.setbranch(orig.branch())
+ if util.safehasattr(repo, 'currenttopic'):
+ # uurrgs
+ # there no other topic setter yet
+ if not orig.topic() and repo.vfs.exists('topic'):
+ repo.vfs.unlink('topic')
+ else:
+ with repo.vfs.open('topic', 'w') as f:
+ f.write(orig.topic())
+
+ return merge.graft(repo, orig, pctx, ['destination', 'evolving'], True)
+
+instabilities_map = {
+ 'contentdivergent': "content-divergent",
+ 'phasedivergent': "phase-divergent"
+}
+
+def _selectrevs(repo, allopt, revopt, anyopt, targetcat):
+ """select troubles in repo matching according to given options"""
+ revs = set()
+ if allopt or revopt:
+ revs = repo.revs("%s()" % targetcat)
+ if revopt:
+ revs = scmutil.revrange(repo, revopt) & revs
+ elif not anyopt:
+ topic = getattr(repo, 'currenttopic', '')
+ if topic:
+ revs = repo.revs('topic(%s)', topic) & revs
+ elif targetcat == 'orphan':
+ revs = _aspiringdescendant(repo,
+ repo.revs('(.::) - obsolete()::'))
+ revs = set(revs)
+ if targetcat == 'contentdivergent':
+ # Pick one divergent per group of divergents
+ revs = _dedupedivergents(repo, revs)
+ elif anyopt:
+ revs = repo.revs('first(%s())' % (targetcat))
+ elif targetcat == 'orphan':
+ revs = set(_aspiringchildren(repo, repo.revs('(.::) - obsolete()::')))
+ if 1 < len(revs):
+ msg = "multiple evolve candidates"
+ hint = (_("select one of %s with --rev")
+ % ', '.join([str(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()])
+ return revs
+
+def _dedupedivergents(repo, revs):
+ """Dedupe the divergents revs in revs to get one from each group with the
+ lowest revision numbers
+ """
+ repo = repo.unfiltered()
+ res = set()
+ # To not reevaluate divergents of the same group once one is encountered
+ discarded = set()
+ for rev in revs:
+ if rev in discarded:
+ continue
+ divergent = repo[rev]
+ base, others = divergentdata(divergent)
+ othersrevs = [o.rev() for o in others]
+ res.add(min([divergent.rev()] + othersrevs))
+ discarded.update(othersrevs)
+ return res
+
+def divergentdata(ctx):
+ """return base, other part of a conflict
+
+ This only return the first one.
+
+ XXX this woobly function won't survive XXX
+ """
+ repo = ctx._repo.unfiltered()
+ for base in repo.set('reverse(allprecursors(%d))', ctx.rev()):
+ newer = compat.successorssets(ctx._repo, base.node())
+ # drop filter and solution including the original ctx
+ newer = [n for n in newer if n and ctx.node() not in n]
+ if newer:
+ return base, tuple(ctx._repo[o] for o in newer[0])
+ raise error.Abort(_("base of divergent changeset %s not found") % ctx,
+ hint=_('this case is not yet handled'))
+
+def _aspiringdescendant(repo, revs):
+ """Return a list of changectx which can be stabilized on top of pctx or
+ one of its descendants recursively. Empty list if none can be found."""
+ target = set(revs)
+ result = set(target)
+ paths = collections.defaultdict(set)
+ for r in repo.revs('orphan() - %ld', revs):
+ for d in _possibledestination(repo, r):
+ paths[d].add(r)
+
+ result = set(target)
+ tovisit = list(revs)
+ while tovisit:
+ base = tovisit.pop()
+ for unstable in paths[base]:
+ if unstable not in result:
+ tovisit.append(unstable)
+ result.add(unstable)
+ return sorted(result - target)
+
+def _aspiringchildren(repo, revs):
+ """Return a list of changectx which can be stabilized on top of pctx or
+ one of its descendants. Empty list if none can be found."""
+ target = set(revs)
+ result = []
+ for r in repo.revs('orphan() - %ld', revs):
+ dest = _possibledestination(repo, r)
+ if target & dest:
+ result.append(r)
+ return result
+
+def _possibledestination(repo, rev):
+ """return all changesets that may be a new parent for REV"""
+ tonode = repo.changelog.node
+ parents = repo.changelog.parentrevs
+ torev = repo.changelog.rev
+ dest = set()
+ tovisit = list(parents(rev))
+ while tovisit:
+ r = tovisit.pop()
+ succsets = compat.successorssets(repo, tonode(r))
+ if not succsets:
+ tovisit.extend(parents(r))
+ else:
+ # We should probably pick only one destination from split
+ # (case where '1 < len(ss)'), This could be the currently tipmost
+ # but logic is less clear when result of the split are now on
+ # multiple branches.
+ for ss in succsets:
+ for n in ss:
+ dest.add(torev(n))
+ return dest
+
+def _handlenotrouble(ui, repo, allopt, revopt, anyopt, targetcat):
+ """Used by the evolve function to display an error message when
+ no troubles can be resolved"""
+ troublecategories = ['phasedivergent', 'contentdivergent', 'orphan']
+ unselectedcategories = [c for c in troublecategories if c != targetcat]
+ msg = None
+ hint = None
+
+ troubled = {
+ "orphan": repo.revs("orphan()"),
+ "contentdivergent": repo.revs("contentdivergent()"),
+ "phasedivergent": repo.revs("phasedivergent()"),
+ "all": repo.revs("troubled()"),
+ }
+
+ hintmap = {
+ 'phasedivergent': _("do you want to use --phase-divergent"),
+ 'phasedivergent+contentdivergent': _("do you want to use "
+ "--phase-divergent or"
+ " --content-divergent"),
+ 'phasedivergent+orphan': _("do you want to use --phase-divergent"
+ " or --orphan"),
+ 'contentdivergent': _("do you want to use --content-divergent"),
+ 'contentdivergent+orphan': _("do you want to use --content-divergent"
+ " or --orphan"),
+ 'orphan': _("do you want to use --orphan"),
+ 'any+phasedivergent': _("do you want to use --any (or --rev) and"
+ " --phase-divergent"),
+ 'any+phasedivergent+contentdivergent': _("do you want to use --any"
+ " (or --rev) and"
+ " --phase-divergent or"
+ " --content-divergent"),
+ 'any+phasedivergent+orphan': _("do you want to use --any (or --rev)"
+ " and --phase-divergent or --orphan"),
+ 'any+contentdivergent': _("do you want to use --any (or --rev) and"
+ " --content-divergent"),
+ 'any+contentdivergent+orphan': _("do you want to use --any (or --rev)"
+ " and --content-divergent or "
+ "--orphan"),
+ 'any+orphan': _("do you want to use --any (or --rev)"
+ "and --orphan"),
+ }
+
+ if revopt:
+ revs = scmutil.revrange(repo, revopt)
+ if not revs:
+ msg = _("set of specified revisions is empty")
+ else:
+ msg = _("no %s changesets in specified revisions") % targetcat
+ othertroubles = []
+ for cat in unselectedcategories:
+ if revs & troubled[cat]:
+ othertroubles.append(cat)
+ if othertroubles:
+ hint = hintmap['+'.join(othertroubles)]
+
+ elif anyopt:
+ msg = _("no %s changesets to evolve") % targetcat
+ othertroubles = []
+ for cat in unselectedcategories:
+ if troubled[cat]:
+ othertroubles.append(cat)
+ if othertroubles:
+ hint = hintmap['+'.join(othertroubles)]
+
+ else:
+ # evolve without any option = relative to the current wdir
+ if targetcat == 'orphan':
+ msg = _("nothing to evolve on current working copy parent")
+ else:
+ msg = _("current working copy parent is not %s") % targetcat
+
+ p1 = repo['.'].rev()
+ othertroubles = []
+ for cat in unselectedcategories:
+ if p1 in troubled[cat]:
+ othertroubles.append(cat)
+ if othertroubles:
+ hint = hintmap['+'.join(othertroubles)]
+ else:
+ length = len(troubled[targetcat])
+ if length:
+ hint = _("%d other %s in the repository, do you want --any "
+ "or --rev") % (length, targetcat)
+ else:
+ othertroubles = []
+ for cat in unselectedcategories:
+ if troubled[cat]:
+ othertroubles.append(cat)
+ if othertroubles:
+ hint = hintmap['any+' + ('+'.join(othertroubles))]
+ else:
+ msg = _("no troubled changesets")
+
+ assert msg is not None
+ ui.write_err("%s\n" % msg)
+ if hint:
+ ui.write_err("(%s)\n" % hint)
+ return 2
+ else:
+ return 1
+
+def _preparelistctxs(items, condition):
+ return [item.hex() for item in items if condition(item)]
+
+def _formatctx(fm, ctx):
+ fm.data(node=ctx.hex())
+ fm.data(desc=ctx.description())
+ fm.data(date=ctx.date())
+ fm.data(user=ctx.user())
+
+def listtroubles(ui, repo, troublecategories, **opts):
+ """Print all the troubles for the repo (or given revset)"""
+ troublecategories = troublecategories or ['contentdivergent', 'orphan', 'phasedivergent']
+ showunstable = 'orphan' in troublecategories
+ showbumped = 'phasedivergent' in troublecategories
+ showdivergent = 'contentdivergent' in troublecategories
+
+ revs = repo.revs('+'.join("%s()" % t for t in troublecategories))
+ if opts.get('rev'):
+ revs = scmutil.revrange(repo, opts.get('rev'))
+
+ fm = ui.formatter('evolvelist', opts)
+ for rev in revs:
+ ctx = repo[rev]
+ unpars = _preparelistctxs(ctx.parents(), lambda p: p.orphan())
+ obspars = _preparelistctxs(ctx.parents(), lambda p: p.obsolete())
+ imprecs = _preparelistctxs(repo.set("allprecursors(%n)", ctx.node()),
+ lambda p: not p.mutable())
+ dsets = divergentsets(repo, ctx)
+
+ fm.startitem()
+ # plain formatter section
+ hashlen, desclen = 12, 60
+ desc = ctx.description()
+ if desc:
+ desc = desc.splitlines()[0]
+ desc = (desc[:desclen] + '...') if len(desc) > desclen else desc
+ fm.plain('%s: ' % ctx.hex()[:hashlen])
+ fm.plain('%s\n' % desc)
+ fm.data(node=ctx.hex(), rev=ctx.rev(), desc=desc, phase=ctx.phasestr())
+
+ for unpar in unpars if showunstable else []:
+ fm.plain(' %s: %s (%s parent)\n' % (TROUBLES['ORPHAN'],
+ unpar[:hashlen],
+ TROUBLES['ORPHAN']))
+ for obspar in obspars if showunstable else []:
+ fm.plain(' %s: %s (obsolete parent)\n' % (TROUBLES['ORPHAN'],
+ obspar[:hashlen]))
+ for imprec in imprecs if showbumped else []:
+ fm.plain(' %s: %s (immutable precursor)\n' %
+ (TROUBLES['PHASEDIVERGENT'], imprec[:hashlen]))
+
+ if dsets and showdivergent:
+ for dset in dsets:
+ fm.plain(' %s: ' % TROUBLES['CONTENTDIVERGENT'])
+ first = True
+ for n in dset['divergentnodes']:
+ t = "%s (%s)" if first else " %s (%s)"
+ first = False
+ fm.plain(t % (node.hex(n)[:hashlen], repo[n].phasestr()))
+ comprec = node.hex(dset['commonprecursor'])[:hashlen]
+ fm.plain(" (precursor %s)\n" % comprec)
+ fm.plain("\n")
+
+ # templater-friendly section
+ _formatctx(fm, ctx)
+ troubles = []
+ for unpar in unpars:
+ troubles.append({'troubletype': TROUBLES['ORPHAN'],
+ 'sourcenode': unpar, 'sourcetype': 'orphanparent'})
+ for obspar in obspars:
+ troubles.append({'troubletype': TROUBLES['ORPHAN'],
+ 'sourcenode': obspar,
+ 'sourcetype': 'obsoleteparent'})
+ for imprec in imprecs:
+ troubles.append({'troubletype': TROUBLES['PHASEDIVERGENT'],
+ 'sourcenode': imprec,
+ 'sourcetype': 'immutableprecursor'})
+ for dset in dsets:
+ divnodes = [{'node': node.hex(n),
+ 'phase': repo[n].phasestr(),
+ } for n in dset['divergentnodes']]
+ troubles.append({'troubletype': TROUBLES['CONTENTDIVERGENT'],
+ 'commonprecursor': node.hex(dset['commonprecursor']),
+ 'divergentnodes': divnodes})
+ fm.data(troubles=troubles)
+
+ fm.end()
+
+def _checkevolveopts(repo, opts):
+ """ check the options passed to `hg evolve` and warn for deprecation warning
+ if any """
+
+ if opts['continue']:
+ if opts['any']:
+ raise error.Abort(_('cannot specify both "--any" and "--continue"'))
+ if opts['all']:
+ raise error.Abort(_('cannot specify both "--all" and "--continue"'))
+ if opts['rev']:
+ raise error.Abort(_('cannot specify both "--rev" and "--continue"'))
+ if opts['stop']:
+ raise error.Abort(_('cannot specify both "--stop" and'
+ ' "--continue"'))
+
+ if opts['stop']:
+ if opts['any']:
+ raise error.Abort(_('cannot specify both "--any" and "--stop"'))
+ if opts['all']:
+ raise error.Abort(_('cannot specify both "--all" and "--stop"'))
+ if opts['rev']:
+ raise error.Abort(_('cannot specify both "--rev" and "--stop"'))
+
+ if opts['rev']:
+ if opts['any']:
+ raise error.Abort(_('cannot specify both "--rev" and "--any"'))
+ if opts['all']:
+ raise error.Abort(_('cannot specify both "--rev" and "--all"'))
+
+ # Backward compatibility
+ if opts['unstable']:
+ msg = ("'evolve --unstable' is deprecated, "
+ "use 'evolve --orphan'")
+ repo.ui.deprecwarn(msg, '4.4')
+
+ opts['orphan'] = opts['divergent']
+
+ if opts['divergent']:
+ msg = ("'evolve --divergent' is deprecated, "
+ "use 'evolve --content-divergent'")
+ repo.ui.deprecwarn(msg, '4.4')
+
+ opts['content_divergent'] = opts['divergent']
+
+ if opts['bumped']:
+ msg = ("'evolve --bumped' is deprecated, "
+ "use 'evolve --phase-divergent'")
+ repo.ui.deprecwarn(msg, '4.4')
+
+ opts['phase_divergent'] = opts['bumped']
+
+ return opts
+
+def _cleanup(ui, repo, startnode, showprogress):
+ if showprogress:
+ ui.progress(_('evolve'), None)
+ if repo['.'] != startnode:
+ ui.status(_('working directory is now at %s\n') % repo['.'])
+
+def divergentsets(repo, ctx):
+ """Compute sets of commits divergent with a given one"""
+ cache = {}
+ base = {}
+ for n in compat.allprecursors(repo.obsstore, [ctx.node()]):
+ if n == ctx.node():
+ # a node can't be a base for divergence with itself
+ continue
+ nsuccsets = compat.successorssets(repo, n, cache)
+ for nsuccset in nsuccsets:
+ if ctx.node() in nsuccset:
+ # we are only interested in *other* successor sets
+ continue
+ if tuple(nsuccset) in base:
+ # we already know the latest base for this divergency
+ continue
+ base[tuple(nsuccset)] = n
+ divergence = []
+ for divset, b in base.iteritems():
+ divergence.append({
+ 'divergentnodes': divset,
+ 'commonprecursor': b
+ })
+
+ return divergence
+
+@eh.command(
+ '^evolve|stabilize|solve',
+ [('n', 'dry-run', False,
+ _('do not perform actions, just print what would be done')),
+ ('', 'confirm', False,
+ _('ask for confirmation before performing the action')),
+ ('A', 'any', False,
+ _('also consider troubled changesets unrelated to current working '
+ 'directory')),
+ ('r', 'rev', [], _('solves troubles of these revisions')),
+ ('', 'bumped', False, _('solves only bumped changesets')),
+ ('', 'phase-divergent', False, _('solves only phase-divergent changesets')),
+ ('', 'divergent', False, _('solves only divergent changesets')),
+ ('', 'content-divergent', False, _('solves only content-divergent changesets')),
+ ('', 'unstable', False, _('solves only unstable changesets')),
+ ('', 'orphan', False, _('solves only orphan changesets (default)')),
+ ('a', 'all', False, _('evolve all troubled changesets related to the '
+ 'current working directory and its descendants')),
+ ('c', 'continue', False, _('continue an interrupted evolution')),
+ ('', 'stop', False, _('stop the interrupted evolution')),
+ ('l', 'list', False, 'provide details on troubled changesets in the repo'),
+ ] + mergetoolopts,
+ _('[OPTIONS]...')
+)
+def evolve(ui, repo, **opts):
+ """solve troubled changesets in your repository
+
+ Modifying history can lead to various types of troubled changesets:
+ unstable, bumped, or divergent. The evolve command resolves your troubles
+ by executing one of the following actions:
+
+ - update working copy to a successor
+ - rebase an unstable changeset
+ - extract the desired changes from a bumped changeset
+ - fuse divergent changesets back together
+
+ If you pass no arguments, evolve works in automatic mode: it will execute a
+ single action to reduce instability related to your working copy. There are
+ two cases for this action. First, if the parent of your working copy is
+ obsolete, evolve updates to the parent's successor. Second, if the working
+ copy parent is not obsolete but has obsolete predecessors, then evolve
+ determines if there is an unstable changeset that can be rebased onto the
+ working copy parent in order to reduce instability.
+ If so, evolve rebases that changeset. If not, evolve refuses to guess your
+ intention, and gives a hint about what you might want to do next.
+
+ Any time evolve creates a changeset, it updates the working copy to the new
+ changeset. (Currently, every successful evolve operation involves an update
+ as well; this may change in future.)
+
+ Automatic mode only handles common use cases. For example, it avoids taking
+ action in the case of ambiguity, and it ignores unstable changesets that
+ are not related to your working copy.
+ It also refuses to solve bumped or divergent changesets unless you
+ explicitly request such behavior (see below).
+
+ Eliminating all instability around your working copy may require multiple
+ invocations of :hg:`evolve`. Alternately, use ``--all`` to recursively
+ select and evolve all unstable changesets that can be rebased onto the
+ working copy parent.
+ This is more powerful than successive invocations, since ``--all`` handles
+ ambiguous cases (e.g. unstable changesets with multiple children) by
+ evolving all branches.
+
+ When your repository cannot be handled by automatic mode, you might need to
+ use ``--rev`` to specify a changeset to evolve. For example, if you have
+ an unstable changeset that is not related to the working copy parent,
+ you could use ``--rev`` to evolve it. Or, if some changeset has multiple
+ unstable children, evolve in automatic mode refuses to guess which one to
+ evolve; you have to use ``--rev`` in that case.
+
+ Alternately, ``--any`` makes evolve search for the next evolvable changeset
+ regardless of whether it is related to the working copy parent.
+
+ You can supply multiple revisions to evolve multiple troubled changesets
+ in a single invocation. In revset terms, ``--any`` is equivalent to ``--rev
+ first(unstable())``. ``--rev`` and ``--all`` are mutually exclusive, as are
+ ``--rev`` and ``--any``.
+
+ ``hg evolve --any --all`` is useful for cleaning up instability across all
+ branches, letting evolve figure out the appropriate order and destination.
+
+ When you have troubled changesets that are not unstable, :hg:`evolve`
+ refuses to consider them unless you specify the category of trouble you
+ wish to resolve, with ``--bumped`` or ``--divergent``. These options are
+ currently mutually exclusive with each other and with ``--unstable``
+ (the default). You can combine ``--bumped`` or ``--divergent`` with
+ ``--rev``, ``--all``, or ``--any``.
+
+ You can also use the evolve command to list the troubles affecting your
+ repository by using the --list flag. You can choose to display only some
+ categories of troubles with the --unstable, --divergent or --bumped flags.
+ """
+
+ opts = _checkevolveopts(repo, opts)
+ # Options
+ contopt = opts['continue']
+ anyopt = opts['any']
+ allopt = opts['all']
+ startnode = repo['.']
+ dryrunopt = opts['dry_run']
+ confirmopt = opts['confirm']
+ revopt = opts['rev']
+ stopopt = opts['stop']
+
+ troublecategories = ['phase_divergent', 'content_divergent', 'orphan']
+ specifiedcategories = [t.replace('_', '')
+ for t in troublecategories
+ if opts[t]]
+ if opts['list']:
+ compat.startpager(ui, 'evolve')
+ listtroubles(ui, repo, specifiedcategories, **opts)
+ return
+
+ targetcat = 'orphan'
+ 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():
+ displayer = compat.changesetdisplayer(ui, repo,
+ {'template': shorttemplate})
+ # no args and parent is obsolete, update to successors
+ try:
+ ctx = repo[utility._singlesuccessor(repo, repo['.'])]
+ except utility.MultipleSuccessorsError as exc:
+ repo.ui.write_err(_('parent is obsolete with multiple'
+ ' successors:\n'))
+ for ln in exc.successorssets:
+ for n in ln:
+ displayer.show(repo[n])
+ return 2
+
+ ui.status(_('update:'))
+ if not ui.quiet:
+ displayer.show(ctx)
+
+ if dryrunopt:
+ return 0
+ res = hg.update(repo, ctx.rev())
+ if ctx != startnode:
+ ui.status(_('working directory is now at %s\n') % ctx)
+ return res
+
+ ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'evolve')
+ troubled = set(repo.revs('troubled()'))
+
+ # Progress handling
+ seen = 1
+ count = allopt and len(troubled) or 1
+ showprogress = allopt
+
+ def progresscb():
+ if revopt or allopt:
+ ui.progress(_('evolve'), seen, unit=_('changesets'), total=count)
+
+ evolvestate = state.cmdstate(repo)
+ # Continuation handling
+ if contopt:
+ if not evolvestate:
+ raise error.Abort(_('no interrupted evolve to continue'))
+ evolvestate.load()
+ continueevolve(ui, repo, evolvestate, progresscb)
+ if evolvestate['command'] != 'evolve':
+ evolvestate.delete()
+ return
+ startnode = repo.unfiltered()[evolvestate['startnode']]
+ evolvestate.delete()
+ elif stopopt:
+ if not evolvestate:
+ raise error.Abort(_('no interrupted evolve to stop'))
+ evolvestate.load()
+ pctx = repo['.']
+ hg.updaterepo(repo, pctx.node(), True)
+ ui.status(_('stopped the interrupted evolve\n'))
+ ui.status(_('working directory is now at %s\n') % pctx)
+ evolvestate.delete()
+ return
+ else:
+ cmdutil.bailifchanged(repo)
+
+ revs = _selectrevs(repo, allopt, revopt, anyopt, targetcat)
+
+ if not revs:
+ return _handlenotrouble(ui, repo, allopt, revopt, anyopt, targetcat)
+
+ # For the progress bar to show
+ count = len(revs)
+ # Order the revisions
+ if targetcat == 'orphan':
+ revs = _orderrevs(repo, revs)
+
+ # cbor does not know how to serialize sets, using list for skippedrevs
+ stateopts = {'category': targetcat, 'replacements': {}, 'revs': revs,
+ 'confirm': confirmopt, 'startnode': startnode.node(),
+ 'skippedrevs': [], 'command': 'evolve', 'orphanmerge': False}
+ evolvestate.addopts(stateopts)
+ for rev in revs:
+ curctx = repo[rev]
+ progresscb()
+ ret = _solveone(ui, repo, curctx, evolvestate, dryrunopt, confirmopt,
+ progresscb, targetcat)
+ seen += 1
+ if ret[0]:
+ evolvestate['replacements'][curctx.node()] = [ret[1]]
+ else:
+ evolvestate['skippedrevs'].append(curctx.node())
+
+ if evolvestate['orphanmerge']:
+ # we were processing an orphan merge with both parents obsolete,
+ # stabilized for second parent, re-stabilize for the first parent
+ ret = _solveone(ui, repo, repo[ret[1]], evolvestate, dryrunopt,
+ confirmopt, progresscb, targetcat)
+ if ret[0]:
+ evolvestate['replacements'][curctx.node()] = [ret[1]]
+ else:
+ evolvestate['skippedrevs'].append(curctx.node())
+
+ evolvestate['orphanmerge'] = False
+
+ progresscb()
+ _cleanup(ui, repo, startnode, showprogress)
+
+def continueevolve(ui, repo, evolvestate, progresscb):
+ """logic for handling of `hg evolve --continue`"""
+ orig = repo[evolvestate['current']]
+ with repo.wlock(), repo.lock():
+ ctx = orig
+ source = ctx.extra().get('source')
+ extra = {}
+ if source:
+ extra['source'] = source
+ extra['intermediate-source'] = ctx.hex()
+ else:
+ extra['source'] = ctx.hex()
+ user = ctx.user()
+ date = ctx.date()
+ message = ctx.description()
+ ui.status(_('evolving %d:%s "%s"\n') % (ctx.rev(), ctx,
+ message.split('\n', 1)[0]))
+ targetphase = max(ctx.phase(), phases.draft)
+ overrides = {('phases', 'new-commit'): targetphase}
+
+ ctxparents = orig.parents()
+ if len(ctxparents) == 2:
+ currentp1 = repo.dirstate.parents()[0]
+ p1obs = ctxparents[0].obsolete()
+ p2obs = ctxparents[1].obsolete()
+ # asumming that the parent of current wdir is successor of one
+ # of p1 or p2 of the original changeset
+ if p1obs and not p2obs:
+ # p1 is obsolete and p2 is not obsolete, current working
+ # directory parent should be successor of p1, so we should
+ # set dirstate parents to (succ of p1, p2)
+ with repo.dirstate.parentchange():
+ repo.dirstate.setparents(currentp1,
+ ctxparents[1].node())
+ elif p2obs and not p1obs:
+ # p2 is obsolete and p1 is not obsolete, current working
+ # directory parent should be successor of p2, so we should
+ # set dirstate parents to (succ of p2, p1)
+ with repo.dirstate.parentchange():
+ repo.dirstate.setparents(ctxparents[0].node(),
+ currentp1)
+
+ else:
+ # both the parents were obsoleted, if orphanmerge is set, we
+ # are processing the second parent first (to keep parent order)
+ if evolvestate.get('orphanmerge'):
+ with repo.dirstate.parentchange():
+ repo.dirstate.setparents(ctxparents[0].node(),
+ currentp1)
+ pass
+
+ with repo.ui.configoverride(overrides, 'evolve-continue'):
+ node = repo.commit(text=message, user=user,
+ date=date, extra=extra)
+
+ # resolving conflicts can lead to empty wdir and node can be None in
+ # those cases
+ newctx = repo[node] if node is not None else repo['.']
+ compat.createmarkers(repo, [(ctx, (newctx,))], operation='evolve')
+
+ # make sure we are continuing evolve and not `hg next --evolve`
+ if evolvestate['command'] == 'evolve':
+ evolvestate['replacements'][ctx.node()] = node
+ category = evolvestate['category']
+ confirm = evolvestate['confirm']
+ unfi = repo.unfiltered()
+ if evolvestate['orphanmerge']:
+ # processing a merge changeset with both parents obsoleted,
+ # stabilized on second parent, insert in front of list to
+ # re-process to stabilize on first parent
+ evolvestate['revs'].insert(0, repo[node].rev())
+ evolvestate['orphanmerge'] = False
+ for rev in evolvestate['revs']:
+ # XXX: prevent this lookup by storing nodes instead of revnums
+ curctx = unfi[rev]
+ if (curctx.node() not in evolvestate['replacements'] and
+ curctx.node() not in evolvestate['skippedrevs']):
+ newnode = _solveone(ui, repo, curctx, evolvestate, False,
+ confirm, progresscb, category)
+ if newnode[0]:
+ evolvestate['replacements'][curctx.node()] = newnode[1]
+ else:
+ evolvestate['skippedrevs'].append(curctx.node())
+ return
--- a/hgext3rd/evolve/evolvestate.py Sat Jan 20 12:38:10 2018 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +0,0 @@
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2 or any later version.
-
-"""
-This file contains class to wrap the state for hg evolve command and other
-related logic.
-
-All the data related to the command state is stored as dictionary in the object.
-The class has methods using which the data can be stored to disk in
-.hg/evolvestate file.
-
-We store the data on disk in cbor, for which we use cbor library to serialize
-and deserialize data.
-"""
-
-from __future__ import absolute_import
-
-from .thirdparty import cbor
-
-from mercurial import (
- util,
-)
-
-class evolvestate():
- """a wrapper class to store the state of `hg evolve` command
-
- All the data for the state is stored in the form of key-value pairs in a
- dictionary.
-
- The class object can write all the data to .hg/evolvestate file and also can
- populate the object data reading that file
- """
-
- def __init__(self, repo, path='evolvestate', opts={}):
- self._repo = repo
- self.path = path
- self.opts = opts
-
- def __nonzero__(self):
- return self.exists()
-
- def __getitem__(self, key):
- return self.opts[key]
-
- def load(self):
- """load the existing evolvestate file into the class object"""
- op = self._read()
- self.opts.update(op)
-
- def addopts(self, opts):
- """add more key-value pairs to the data stored by the object"""
- self.opts.update(opts)
-
- def save(self):
- """write all the evolvestate data stored in .hg/evolvestate file
-
- we use third-party library cbor to serialize data to write in the file.
- """
- with self._repo.vfs(self.path, 'wb', atomictemp=True) as fp:
- cbor.dump(self.opts, fp)
-
- def _read(self):
- """reads the evolvestate file and returns a dictionary which contain
- data in the same format as it was before storing"""
- with self._repo.vfs(self.path, 'rb') as fp:
- return cbor.load(fp)
-
- def delete(self):
- """drop the evolvestate file if exists"""
- util.unlinkpath(self._repo.vfs.join(self.path), ignoremissing=True)
-
- def exists(self):
- """check whether the evolvestate file exists or not"""
- return self._repo.vfs.exists(self.path)
--- a/hgext3rd/evolve/exthelper.py Sat Jan 20 12:38:10 2018 +0100
+++ b/hgext3rd/evolve/exthelper.py Wed Mar 21 16:32:54 2018 +0100
@@ -128,8 +128,11 @@
revset.loadpredicate(ui, 'evolve', revsetpredicate)
templatekeyword = registrar.templatekeyword()
- for name, kw in self._templatekws:
- templatekeyword(name)(kw)
+ for name, kw, requires in self._templatekws:
+ if requires is not None:
+ templatekeyword(name, requires=requires)(kw)
+ else:
+ templatekeyword(name)(kw)
templatekw.loadkeyword(ui, 'evolve', templatekeyword)
for ext, command, wrapper, opts in self._extcommandwrappers:
@@ -215,7 +218,7 @@
return symbol
return dec
- def templatekw(self, keywordname):
+ def templatekw(self, keywordname, requires=None):
"""Decorated function is a template keyword
The name of the keyword must be given as the decorator argument.
@@ -228,7 +231,7 @@
return 'babar'
"""
def dec(keyword):
- self._templatekws.append((keywordname, keyword))
+ self._templatekws.append((keywordname, keyword, requires))
return keyword
return dec
--- a/hgext3rd/evolve/firstmergecache.py Sat Jan 20 12:38:10 2018 +0100
+++ b/hgext3rd/evolve/firstmergecache.py Wed Mar 21 16:32:54 2018 +0100
@@ -49,11 +49,11 @@
if util.safehasattr(repo, 'updatecaches'):
@localrepo.unfilteredmethod
- def updatecaches(self, tr=None):
+ def updatecaches(self, tr=None, **kwargs):
if utility.shouldwarmcache(self, tr):
self.firstmergecache.update(self)
self.firstmergecache.save(self)
- super(firstmergecacherepo, self).updatecaches(tr)
+ super(firstmergecacherepo, self).updatecaches(tr, **kwargs)
else:
def transaction(self, *args, **kwargs):
--- a/hgext3rd/evolve/legacy.py Sat Jan 20 12:38:10 2018 +0100
+++ b/hgext3rd/evolve/legacy.py Wed Mar 21 16:32:54 2018 +0100
@@ -30,6 +30,12 @@
from mercurial import registrar
from mercurial import util
+try:
+ from mercurial.utils.dateutil import makedate
+except ImportError as e:
+ # compat with hg < 4.6
+ from mercurial.util import makedate
+
if util.safehasattr(registrar, 'command'):
commandfunc = registrar.command
else: # compat with hg < 4.3
@@ -105,7 +111,7 @@
prec = bin(objhex)
sucs = (suc == nullid) and [] or [suc]
meta = {
- 'date': '%i %i' % util.makedate(),
+ 'date': '%i %i' % makedate(),
'user': ui.username(),
}
try:
--- a/hgext3rd/evolve/metadata.py Sat Jan 20 12:38:10 2018 +0100
+++ b/hgext3rd/evolve/metadata.py Wed Mar 21 16:32:54 2018 +0100
@@ -5,7 +5,7 @@
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
-__version__ = '7.2.1'
-testedwith = '4.1.3 4.2.3 4.3.2 4.4.2'
+__version__ = '7.3.0'
+testedwith = '4.1.3 4.2.3 4.3.2 4.4.2 4.5.2'
minimumhgversion = '4.1'
buglink = 'https://bz.mercurial-scm.org/'
--- a/hgext3rd/evolve/obscache.py Sat Jan 20 12:38:10 2018 +0100
+++ b/hgext3rd/evolve/obscache.py Wed Mar 21 16:32:54 2018 +0100
@@ -225,6 +225,8 @@
self.clear(reset=True)
starttime = timer()
+ revs = list(revs)
+ obsmarkers = list(obsmarkers)
self._updatefrom(repo, revs, obsmarkers)
duration = timer() - starttime
repo.ui.log('evoext-cache', 'updated %s in %.4f seconds (%sr, %so)\n',
@@ -523,8 +525,8 @@
if util.safehasattr(repo, 'updatecaches'):
@localrepo.unfilteredmethod
- def updatecaches(self, tr=None):
- super(obscacherepo, self).updatecaches(tr)
+ def updatecaches(self, tr=None, **kwargs):
+ super(obscacherepo, self).updatecaches(tr, **kwargs)
self.obsstore.obscache.update(repo)
self.obsstore.obscache.save(repo)
--- a/hgext3rd/evolve/obsdiscovery.py Sat Jan 20 12:38:10 2018 +0100
+++ b/hgext3rd/evolve/obsdiscovery.py Wed Mar 21 16:32:54 2018 +0100
@@ -44,10 +44,10 @@
util,
wireproto,
)
-from mercurial.hgweb import hgweb_mod
from mercurial.i18n import _
from . import (
+ compat,
exthelper,
obscache,
utility,
@@ -95,7 +95,8 @@
common = set()
undecided = set(probeset)
totalnb = len(undecided)
- ui.progress(_("comparing with other"), 0, total=totalnb)
+ ui.progress(_("comparing with other"), 0, total=totalnb,
+ unit=_("changesets"))
_takefullsample = setdiscovery._takefullsample
if remote.capable('_evoext_obshash_1'):
getremotehash = remote.evoext_obshash1
@@ -114,7 +115,7 @@
roundtrips += 1
ui.progress(_("comparing with other"), totalnb - len(undecided),
- total=totalnb)
+ total=totalnb, unit=_("changesets"))
ui.debug("query %i; still undecided: %i, sample size is: %i\n"
% (roundtrips, len(undecided), len(sample)))
# indices between sample and externalized version must match
@@ -175,7 +176,8 @@
local.obsstore.rangeobshashcache.update(local)
querycount = 0
- ui.progress(_("comparing obsmarker with other"), querycount)
+ ui.progress(_("comparing obsmarker with other"), querycount,
+ unit=_("queries"))
overflow = []
while sample or overflow:
if overflow:
@@ -230,7 +232,8 @@
addentry(new)
assert nbsample == nbreplies
querycount += 1
- ui.progress(_("comparing obsmarker with other"), querycount)
+ ui.progress(_("comparing obsmarker with other"), querycount,
+ unit=_("queries"))
ui.progress(_("comparing obsmarker with other"), None)
local.obsstore.rangeobshashcache.save(local)
duration = timer() - starttime
@@ -597,11 +600,11 @@
if util.safehasattr(repo, 'updatecaches'):
@localrepo.unfilteredmethod
- def updatecaches(self, tr=None):
+ def updatecaches(self, tr=None, **kwargs):
if utility.shouldwarmcache(self, tr):
self.obsstore.rangeobshashcache.update(self)
self.obsstore.rangeobshashcache.save(self)
- super(obshashrepo, self).updatecaches(tr)
+ super(obshashrepo, self).updatecaches(tr, **kwargs)
else:
def transaction(self, *args, **kwargs):
@@ -675,6 +678,7 @@
except ValueError:
self._abort(error.ResponseError(_("unexpected response:"), d))
+@compat.wireprotocommand(eh, 'evoext_obshashrange_v1', 'ranges')
def srv_obshashrange_v1(repo, proto, ranges):
ranges = wireproto.decodelist(ranges)
ranges = [_decrange(r) for r in ranges]
@@ -698,17 +702,26 @@
caps = orig(repo, proto)
enabled = _useobshashrange(repo)
if obsolete.isenabled(repo, obsolete.exchangeopt) and enabled:
+
+ # Compat hg 4.6+ (2f7290555c96)
+ bytesresponse = False
+ if util.safehasattr(caps, 'data'):
+ bytesresponse = True
+ caps = caps.data
+
caps = caps.split()
- caps.append('_evoext_obshashrange_v1')
+ caps.append(b'_evoext_obshashrange_v1')
caps.sort()
- caps = ' '.join(caps)
+ caps = b' '.join(caps)
+
+ # Compat hg 4.6+ (2f7290555c96)
+ if bytesresponse:
+ from mercurial import wireprototypes
+ caps = wireprototypes.bytesresponse(caps)
return caps
@eh.extsetup
def obshashrange_extsetup(ui):
- hgweb_mod.perms['evoext_obshashrange_v1'] = 'pull'
-
- wireproto.commands['evoext_obshashrange_v1'] = (srv_obshashrange_v1, 'ranges')
###
extensions.wrapfunction(wireproto, 'capabilities', _obshashrange_capabilities)
# wrap command content
@@ -759,7 +772,8 @@
cache = []
unfi = repo.unfiltered()
markercache = {}
- repo.ui.progress(_("preparing locally"), 0, total=len(unfi))
+ repo.ui.progress(_("preparing locally"), 0, total=len(unfi),
+ unit=_("changesets"))
for i in unfi:
ctx = unfi[i]
entry = 0
@@ -789,7 +803,8 @@
cache.append((ctx.node(), sha.digest()))
else:
cache.append((ctx.node(), node.nullid))
- repo.ui.progress(_("preparing locally"), i, total=len(unfi))
+ repo.ui.progress(_("preparing locally"), i, total=len(unfi),
+ unit=_("changesets"))
repo.ui.progress(_("preparing locally"), None)
return cache
@@ -828,9 +843,11 @@
except ValueError:
self._abort(error.ResponseError(_("unexpected response:"), d))
+@compat.wireprotocommand(eh, 'evoext_obshash', 'nodes')
def srv_obshash(repo, proto, nodes):
return wireproto.encodelist(_obshash(repo, wireproto.decodelist(nodes)))
+@compat.wireprotocommand(eh, 'evoext_obshash1', 'nodes')
def srv_obshash1(repo, proto, nodes):
return wireproto.encodelist(_obshash(repo, wireproto.decodelist(nodes),
version=1))
@@ -840,20 +857,27 @@
caps = orig(repo, proto)
if (obsolete.isenabled(repo, obsolete.exchangeopt)
and repo.ui.configbool('experimental', 'evolution.obsdiscovery', True)):
+
+ # Compat hg 4.6+ (2f7290555c96)
+ bytesresponse = False
+ if util.safehasattr(caps, 'data'):
+ bytesresponse = True
+ caps = caps.data
+
caps = caps.split()
- caps.append('_evoext_obshash_0')
- caps.append('_evoext_obshash_1')
+ caps.append(b'_evoext_obshash_0')
+ caps.append(b'_evoext_obshash_1')
caps.sort()
- caps = ' '.join(caps)
+ caps = b' '.join(caps)
+
+ # Compat hg 4.6+ (2f7290555c96)
+ if bytesresponse:
+ from mercurial import wireprototypes
+ caps = wireprototypes.bytesresponse(caps)
return caps
@eh.extsetup
def obshash_extsetup(ui):
- hgweb_mod.perms['evoext_obshash'] = 'pull'
- hgweb_mod.perms['evoext_obshash1'] = 'pull'
-
- wireproto.commands['evoext_obshash'] = (srv_obshash, 'nodes')
- wireproto.commands['evoext_obshash1'] = (srv_obshash1, 'nodes')
extensions.wrapfunction(wireproto, 'capabilities', _obshash_capabilities)
# wrap command content
oldcap, args = wireproto.commands['capabilities']
--- a/hgext3rd/evolve/obsexchange.py Sat Jan 20 12:38:10 2018 +0100
+++ b/hgext3rd/evolve/obsexchange.py Wed Mar 21 16:32:54 2018 +0100
@@ -27,6 +27,12 @@
wireproto,
)
+try:
+ from mercurial import wireprotoserver
+ wireprotoserver.handlewsgirequest
+except (ImportError, AttributeError):
+ wireprotoserver = None
+
from mercurial.hgweb import common as hgwebcommon
from . import (
@@ -106,10 +112,22 @@
"""wrapper to advertise new capability"""
caps = orig(repo, proto)
if obsolete.isenabled(repo, obsolete.exchangeopt):
+
+ # Compat hg 4.6+ (2f7290555c96)
+ bytesresponse = False
+ if util.safehasattr(caps, 'data'):
+ bytesresponse = True
+ caps = caps.data
+
caps = caps.split()
- caps.append('_evoext_getbundle_obscommon')
+ caps.append(b'_evoext_getbundle_obscommon')
caps.sort()
- caps = ' '.join(caps)
+ caps = b' '.join(caps)
+
+ # Compat hg 4.6+ (2f7290555c96)
+ if bytesresponse:
+ from mercurial import wireprototypes
+ caps = wireprototypes.bytesresponse(caps)
return caps
@eh.extsetup
--- a/hgext3rd/evolve/obshistory.py Sat Jan 20 12:38:10 2018 +0100
+++ b/hgext3rd/evolve/obshistory.py Wed Mar 21 16:32:54 2018 +0100
@@ -10,11 +10,9 @@
import re
from mercurial import (
- cmdutil,
commands,
error,
graphmod,
- mdiff,
patch,
obsolete,
node as nodemod,
@@ -58,7 +56,7 @@
] + commands.formatteropts,
_('hg olog [OPTION]... [REV]'))
def cmdobshistory(ui, repo, *revs, **opts):
- """show the obsolescence history of the specified revisions.
+ """show the obsolescence history of the specified revisions
If no revision range is specified, we display the log for the current
working copy parent.
@@ -97,7 +95,7 @@
revs.reverse()
_debugobshistoryrevs(ui, repo, revs, opts)
-class obsmarker_printer(cmdutil.changeset_printer):
+class obsmarker_printer(compat.changesetprinter):
"""show (available) information about a node
We display the node, description (if available) and various information
@@ -173,7 +171,7 @@
basename = "changeset-description"
succname = "changeset-description"
- d = mdiff.unidiff(basedesc, '', succdesc, '', basename, succname)
+ d = compat.strdiff(basedesc, succdesc, basename, succname)
# mercurial 4.1 and before return the patch directly
if not isinstance(d, tuple):
patch = d
@@ -356,7 +354,7 @@
displayer = obsmarker_printer(ui, repo.unfiltered(), matchfn, opts, buffered=True)
edges = graphmod.asciiedges
walker = _obshistorywalker(repo.unfiltered(), revs, opts.get('all', False))
- cmdutil.displaygraph(ui, repo, walker, displayer, edges)
+ compat.displaygraph(ui, repo, walker, displayer, edges)
def _debugobshistoryrevs(ui, repo, revs, opts):
""" Display the obsolescence history for revset
@@ -404,7 +402,7 @@
label="evolve.node")
fm.plain(' ')
- fm.write('rev', '(%d)', int(ctx),
+ fm.write('rev', '(%d)', ctx.rev(),
label="evolve.rev")
fm.plain(' ')
@@ -473,6 +471,11 @@
fm.write('succnodes', '%s', nodes,
label="evolve.node")
+ operation = metadata.get('operation')
+ if operation:
+ fm.plain(' using ')
+ fm.write('operation', '%s', operation, label="evolve.operation")
+
fm.plain(' by ')
fm.write('user', '%s', metadata['user'],
--- a/hgext3rd/evolve/rewriteutil.py Sat Jan 20 12:38:10 2018 +0100
+++ b/hgext3rd/evolve/rewriteutil.py Wed Mar 21 16:32:54 2018 +0100
@@ -24,6 +24,7 @@
phases,
repair,
revset,
+ util,
)
from mercurial.i18n import _
@@ -60,6 +61,10 @@
msg = _("cannot %s the null revision") % (action)
hint = _("no changeset checked out")
raise error.Abort(msg, hint=hint)
+ if any(util.safehasattr(r, 'rev') for r in revs):
+ msg = "rewriteutil.precheck called with ctx not revs"
+ repo.ui.develwarn(msg)
+ revs = (r.rev() for r in revs)
publicrevs = repo.revs('%ld and public()', revs)
if publicrevs:
summary = _formatrevs(repo, publicrevs)
--- a/hgext3rd/evolve/serveronly.py Sat Jan 20 12:38:10 2018 +0100
+++ b/hgext3rd/evolve/serveronly.py Wed Mar 21 16:32:54 2018 +0100
@@ -58,4 +58,6 @@
evolveopts = 'all'
repo.ui.setconfig('experimental', 'evolution', evolveopts)
if obsolete.isenabled(repo, 'exchange'):
- repo.ui.setconfig('server', 'bundle1', False)
+ # if no config explicitly set, disable bundle1
+ if not isinstance(repo.ui.config('server', 'bundle1'), str):
+ repo.ui.setconfig('server', 'bundle1', False)
--- a/hgext3rd/evolve/stablerangecache.py Sat Jan 20 12:38:10 2018 +0100
+++ b/hgext3rd/evolve/stablerangecache.py Wed Mar 21 16:32:54 2018 +0100
@@ -366,11 +366,11 @@
if util.safehasattr(repo, 'updatecaches'):
@localrepo.unfilteredmethod
- def updatecaches(self, tr=None):
+ def updatecaches(self, tr=None, **kwargs):
if utility.shouldwarmcache(self, tr):
self.stablerange.update(self)
self.stablerange.save(self)
- super(stablerangerepo, self).updatecaches(tr)
+ super(stablerangerepo, self).updatecaches(tr, **kwargs)
else:
def transaction(self, *args, **kwargs):
--- a/hgext3rd/evolve/stablesort.py Sat Jan 20 12:38:10 2018 +0100
+++ b/hgext3rd/evolve/stablesort.py Wed Mar 21 16:32:54 2018 +0100
@@ -14,7 +14,6 @@
from mercurial import (
commands,
- cmdutil,
localrepo,
error,
node as nodemod,
@@ -76,7 +75,7 @@
raise error.Abort('unknown sorting method: "%s"' % method,
hint='pick one of: %s' % valid_method)
- displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
+ displayer = compat.changesetdisplayer(ui, repo, opts, buffered=True)
kwargs = {}
if opts['limit']:
kwargs['limit'] = int(opts['limit'])
@@ -673,11 +672,11 @@
if util.safehasattr(repo, 'updatecaches'):
@localrepo.unfilteredmethod
- def updatecaches(self, tr=None):
+ def updatecaches(self, tr=None, **kwargs):
if utility.shouldwarmcache(self, tr):
self.stablesort.update(self)
self.stablesort.save(self)
- super(stablesortrepo, self).updatecaches(tr)
+ super(stablesortrepo, self).updatecaches(tr, **kwargs)
else:
def transaction(self, *args, **kwargs):
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext3rd/evolve/state.py Wed Mar 21 16:32:54 2018 +0100
@@ -0,0 +1,135 @@
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+"""
+This file contains class to wrap the state for commands and other
+related logic.
+
+All the data related to the command state is stored as dictionary in the object.
+The class has methods using which the data can be stored to disk in a file under
+.hg/ directory.
+
+We store the data on disk in cbor, for which we use cbor library to serialize
+and deserialize data.
+"""
+
+from __future__ import absolute_import
+
+import errno
+import struct
+
+from .thirdparty import cbor
+
+from mercurial import (
+ error,
+ util,
+)
+
+from mercurial.i18n import _
+
+class cmdstate():
+ """a wrapper class to store the state of commands like `evolve`, `grab`
+
+ All the data for the state is stored in the form of key-value pairs in a
+ dictionary.
+
+ The class object can write all the data to a file in .hg/ directory and also
+ can populate the object data reading that file
+ """
+
+ def __init__(self, repo, path='evolvestate', opts={}):
+ self._repo = repo
+ self.path = path
+ self.opts = opts
+
+ def __nonzero__(self):
+ return self.exists()
+
+ def __getitem__(self, key):
+ return self.opts[key]
+
+ def __setitem__(self, key, value):
+ updates = {key: value}
+ self.opts.update(updates)
+
+ def load(self):
+ """load the existing evolvestate file into the class object"""
+ op = self._read()
+ if isinstance(op, dict):
+ self.opts.update(op)
+ elif self.path == 'evolvestate':
+ # it is the old evolvestate file
+ oldop = _oldevolvestateread(self._repo)
+ self.opts.update(oldop)
+
+ def addopts(self, opts):
+ """add more key-value pairs to the data stored by the object"""
+ self.opts.update(opts)
+
+ def save(self):
+ """write all the evolvestate data stored in .hg/evolvestate file
+
+ we use third-party library cbor to serialize data to write in the file.
+ """
+ with self._repo.vfs(self.path, 'wb', atomictemp=True) as fp:
+ cbor.dump(self.opts, fp)
+
+ def _read(self):
+ """reads the evolvestate file and returns a dictionary which contain
+ data in the same format as it was before storing"""
+ with self._repo.vfs(self.path, 'rb') as fp:
+ return cbor.load(fp)
+
+ def delete(self):
+ """drop the evolvestate file if exists"""
+ util.unlinkpath(self._repo.vfs.join(self.path), ignoremissing=True)
+
+ def exists(self):
+ """check whether the evolvestate file exists or not"""
+ return self._repo.vfs.exists(self.path)
+
+def _oldevolvestateread(repo):
+ """function to read the old evolvestate file
+
+ This exists for BC reasons."""
+ try:
+ f = repo.vfs('evolvestate')
+ except IOError as err:
+ if err.errno != errno.ENOENT:
+ raise
+ try:
+ versionblob = f.read(4)
+ if len(versionblob) < 4:
+ repo.ui.debug('ignoring corrupted evolvestate (file contains %i bits)'
+ % len(versionblob))
+ return None
+ version = struct._unpack('>I', versionblob)[0]
+ if version != 0:
+ msg = _('unknown evolvestate version %i') % version
+ raise error.Abort(msg, hint=_('upgrade your evolve'))
+ records = []
+ data = f.read()
+ off = 0
+ end = len(data)
+ while off < end:
+ rtype = data[off]
+ off += 1
+ length = struct._unpack('>I', data[off:(off + 4)])[0]
+ off += 4
+ record = data[off:(off + length)]
+ off += length
+ if rtype == 't':
+ rtype, record = record[0], record[1:]
+ records.append((rtype, record))
+ state = {}
+ for rtype, rdata in records:
+ if rtype == 'C':
+ state['current'] = rdata
+ elif rtype.lower():
+ repo.ui.debug('ignore evolve state record type %s' % rtype)
+ else:
+ raise error.Abort(_('unknown evolvestate field type %r')
+ % rtype, hint=_('upgrade your evolve'))
+ return state
+ finally:
+ f.close()
--- a/hgext3rd/evolve/templatekw.py Sat Jan 20 12:38:10 2018 +0100
+++ b/hgext3rd/evolve/templatekw.py Wed Mar 21 16:32:54 2018 +0100
@@ -9,13 +9,13 @@
"""
from . import (
+ compat,
error,
exthelper,
obshistory
)
from mercurial import (
- cmdutil,
templatekw,
node,
util
@@ -34,21 +34,29 @@
return 'obsolete'
return ''
-@eh.templatekw('troubles')
-def showtroubles(**args):
- """List of strings. Evolution troubles affecting the changeset
- (zero or more of "unstable", "divergent" or "bumped")."""
- ctx = args['ctx']
- try:
- # specify plural= explicitly to trigger TypeError on hg < 4.2
- return templatekw.showlist('trouble', ctx.instabilities(), args,
- plural='troubles')
- except TypeError:
- return templatekw.showlist('trouble', ctx.instabilities(), plural='troubles',
- **args)
+if util.safehasattr(templatekw, 'compatlist'):
+ @eh.templatekw('troubles', requires=set(['ctx', 'templ']))
+ def showtroubles(context, mapping):
+ ctx = context.resource(mapping, 'ctx')
+ return templatekw.compatlist(context, mapping, 'trouble',
+ ctx.instabilities(), plural='troubles')
+else:
+ # older template API in hg < 4.6
+ @eh.templatekw('troubles')
+ def showtroubles(**args):
+ """List of strings. Evolution troubles affecting the changeset
+ (zero or more of "unstable", "divergent" or "bumped")."""
+ ctx = args['ctx']
+ try:
+ # specify plural= explicitly to trigger TypeError on hg < 4.2
+ return templatekw.showlist('trouble', ctx.instabilities(), args,
+ plural='troubles')
+ except TypeError:
+ return templatekw.showlist('trouble', ctx.instabilities(), plural='troubles',
+ **args)
if util.safehasattr(templatekw, 'showpredecessors'):
- eh.templatekw("precursors")(templatekw.showpredecessors)
+ templatekw.keywords["precursors"] = templatekw.showpredecessors
else:
# for version <= hg4.3
def closestprecursors(repo, nodeid):
@@ -97,7 +105,7 @@
return directsuccessorssets(repo, nodeid)
if util.safehasattr(templatekw, 'showsuccessorssets'):
- eh.templatekw("successors")(templatekw.showsuccessorssets)
+ templatekw.keywords["successors"] = templatekw.showsuccessorssets
else:
# for version <= hg4.3
@@ -252,13 +260,33 @@
return "\n".join(lines)
-@eh.templatekw("obsfatedata")
-def showobsfatedata(repo, ctx, **args):
- # Get the needed obsfate data
- values = obsfatedata(repo, ctx)
+
+if util.safehasattr(templatekw, 'compatlist'):
+ @eh.templatekw('obsfatedata', requires=set(['ctx', 'templ']))
+ def showobsfatedata(context, mapping):
+ ctx = context.resource(mapping, 'ctx')
+ repo = ctx.repo()
+ values = obsfatedata(repo, ctx)
- if values is None:
- return templatekw.showlist("obsfatedata", [], args)
+ if values is None:
+ return templatekw.compatlist(context, mapping, "obsfatedata", [])
+ args = mapping.copy()
+ args.pop('ctx')
+ args['templ'] = context.resource(mapping, 'templ')
+ return _showobsfatedata(repo, ctx, values, **args)
+else:
+ # pre hg-4.6
+ @eh.templatekw("obsfatedata")
+ def showobsfatedata(repo, ctx, **args):
+ # Get the needed obsfate data
+ values = obsfatedata(repo, ctx)
+
+ if values is None:
+ return templatekw.showlist("obsfatedata", [], args)
+
+ return _showobsfatedata(repo, ctx, values, **args)
+
+def _showobsfatedata(repo, ctx, values, **args):
# Format each successorset successors list
for raw in values:
@@ -316,10 +344,10 @@
def showobsfate(*args, **kwargs):
return showobsfatedata(*args, **kwargs)
-if util.safehasattr(cmdutil.changeset_printer, '_showobsfate'):
+if util.safehasattr(compat.changesetprinter, '_showobsfate'):
pass # already included by default
-elif util.safehasattr(cmdutil.changeset_printer, '_exthook'):
- @eh.wrapfunction(cmdutil.changeset_printer, '_exthook')
+elif util.safehasattr(compat.changesetprinter, '_exthook'):
+ @eh.wrapfunction(compat.changesetprinter, '_exthook')
def exthook(original, self, ctx):
# Call potential other extensions
original(self, ctx)
--- a/hgext3rd/evolve/utility.py Sat Jan 20 12:38:10 2018 +0100
+++ b/hgext3rd/evolve/utility.py Wed Mar 21 16:32:54 2018 +0100
@@ -5,8 +5,16 @@
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
+import collections
+
+from mercurial.i18n import _
+
from mercurial.node import nullrev
+from . import (
+ compat,
+)
+
shorttemplate = "[{label('evolve.rev', rev)}] {desc|firstline}\n"
def obsexcmsg(ui, message, important=False):
@@ -66,3 +74,107 @@
if maxrevs is not None and maxrevs < len(repo.unfiltered()):
return False
return True
+
+class MultipleSuccessorsError(RuntimeError):
+ """Exception raised by _singlesuccessor when multiple successor sets exists
+
+ The object contains the list of successorssets in its 'successorssets'
+ attribute to call to easily recover.
+ """
+
+ def __init__(self, successorssets):
+ self.successorssets = successorssets
+
+def builddependencies(repo, revs):
+ """returns dependency graphs giving an order to solve instability of revs
+ (see _orderrevs for more information on usage)"""
+
+ # For each troubled revision we keep track of what instability if any should
+ # be resolved in order to resolve it. Example:
+ # dependencies = {3: [6], 6:[]}
+ # Means that: 6 has no dependency, 3 depends on 6 to be solved
+ dependencies = {}
+ # rdependencies is the inverted dict of dependencies
+ rdependencies = collections.defaultdict(set)
+
+ for r in revs:
+ dependencies[r] = set()
+ for p in repo[r].parents():
+ try:
+ succ = _singlesuccessor(repo, p)
+ except MultipleSuccessorsError as exc:
+ dependencies[r] = exc.successorssets
+ continue
+ if succ in revs:
+ dependencies[r].add(succ)
+ rdependencies[succ].add(r)
+ return dependencies, rdependencies
+
+def _singlesuccessor(repo, p):
+ """returns p (as rev) if not obsolete or its unique latest successors
+
+ fail if there are no such successor"""
+
+ if not p.obsolete():
+ return p.rev()
+ obs = repo[p]
+ ui = repo.ui
+ newer = compat.successorssets(repo, obs.node())
+ # search of a parent which is not killed
+ while not 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 or len(newer[0]) > 1:
+ raise MultipleSuccessorsError(newer)
+
+ return repo[newer[0][0]].rev()
+
+def revselectionprompt(ui, repo, revs, customheader=""):
+ """function to prompt user to choose a revision from all the revs and return
+ that revision for further tasks
+
+ revs is a list of rev number of revision from which one revision should be
+ choosed by the user
+ customheader is a text which the caller wants as the header of the prompt
+ which will list revisions to select
+
+ returns value is:
+ rev number of revision choosed: if user choose a revision
+ None: if user entered a wrong input, user quit the prompt,
+ ui.interactive is not set
+ """
+
+ # ui.interactive is not set, fallback to default behavior and avoid showing
+ # the prompt
+ if not ui.configbool('ui', 'interactive'):
+ return None
+
+ promptmsg = customheader + "\n"
+ for idx, rev in enumerate(revs):
+ curctx = repo[rev]
+ revmsg = _("%d: [%s] %s\n" % (idx, curctx,
+ curctx.description().split("\n")[0]))
+ promptmsg += revmsg
+
+ promptmsg += _("q: quit the prompt\n")
+ promptmsg += _("enter the index of the revision you want to select:")
+ idxselected = ui.prompt(promptmsg)
+
+ intidx = None
+ try:
+ intidx = int(idxselected)
+ except ValueError:
+ if idxselected == 'q':
+ return None
+ ui.write_err(_("invalid value '%s' entered for index\n") % idxselected)
+ return None
+
+ if intidx >= len(revs) or intidx < 0:
+ # we can make this error message better
+ ui.write_err(_("invalid value '%d' entered for index\n") % intidx)
+ return None
+
+ return revs[intidx]
--- a/hgext3rd/serverminitopic.py Sat Jan 20 12:38:10 2018 +0100
+++ b/hgext3rd/serverminitopic.py Wed Mar 21 16:32:54 2018 +0100
@@ -68,7 +68,7 @@
def branchinfo(self, rev):
"""return branch name and close flag for rev, using and updating
persistent cache."""
- phase = self._repo._phasecache.phase(self, rev)
+ phase = self._repo._phasecache.phase(self._repo, rev)
if phase:
ctx = self._repo[rev]
return ctx.branch(), ctx.closesbranch()
@@ -163,7 +163,7 @@
valid = super(_topiccache, self).validfor(repo)
if not valid:
return False
- elif not mighttopic(repo) and self.phaseshash is None:
+ elif self.phaseshash is None:
# phasehash at None means this is a branchmap
# coming from a public only set
return True
--- a/hgext3rd/topic/__init__.py Sat Jan 20 12:38:10 2018 +0100
+++ b/hgext3rd/topic/__init__.py Wed Mar 21 16:32:54 2018 +0100
@@ -134,13 +134,14 @@
from . import (
compat,
constants,
+ destination,
+ discovery,
+ evolvebits,
flow,
+ randomname,
revset as topicrevset,
- destination,
stack,
topicmap,
- discovery,
- randomname
)
if util.safehasattr(registrar, 'command'):
@@ -175,9 +176,9 @@
'topic.active': 'green',
}
-__version__ = '0.7.0'
+__version__ = '0.8.0'
-testedwith = '4.1.3 4.2.3 4.3.3 4.4.2'
+testedwith = '4.1.3 4.2.3 4.3.3 4.4.2 4.5.2'
minimumhgversion = '4.1'
buglink = 'https://bz.mercurial-scm.org/'
@@ -240,6 +241,12 @@
revlist = stack.stack(self._repo, topic=topic)
try:
return revlist.index(self.rev())
+ except ValueError:
+ if self.obsolete():
+ succ = evolvebits._singlesuccessor(self._repo, self)
+ if succ not in revlist:
+ return None
+ return revlist.index(succ)
except IndexError:
# Lets move to the last ctx of the current topic
return None
--- a/hgext3rd/topic/compat.py Sat Jan 20 12:38:10 2018 +0100
+++ b/hgext3rd/topic/compat.py Wed Mar 21 16:32:54 2018 +0100
@@ -7,6 +7,8 @@
"""
from __future__ import absolute_import
+import functools
+
from mercurial import (
obsolete,
scmutil,
@@ -27,6 +29,17 @@
if successorssets is None:
successorssets = obsolete.successorssets
+# Wrap obsolete.creatmarkers and make it accept but ignore "operation" argument
+# for hg < 4.3
+createmarkers = obsolete.createmarkers
+originalcreatemarkers = createmarkers
+while isinstance(originalcreatemarkers, functools.partial):
+ originalcreatemarkers = originalcreatemarkers.func
+if originalcreatemarkers.__code__.co_argcount < 6:
+ def createmarkers(repo, relations, flag=0, date=None, metadata=None,
+ operation=None):
+ return obsolete.createmarkers(repo, relations, flag, date, metadata)
+
def startpager(ui, cmd):
"""function to start a pager in case ui.pager() exists"""
try:
@@ -44,4 +57,4 @@
else:
relations = [(repo[o], tuple(repo[n] for n in new))
for (o, new) in replacements.iteritems()]
- obsolete.createmarkers(repo, relations)
+ createmarkers(repo, relations, operation=operation)
--- a/tests/test-discovery-obshashrange.t Sat Jan 20 12:38:10 2018 +0100
+++ b/tests/test-discovery-obshashrange.t Wed Mar 21 16:32:54 2018 +0100
@@ -33,7 +33,8 @@
$ hg blackbox
* @0000000000000000000000000000000000000000 (*)> serve --stdio (glob)
* @0000000000000000000000000000000000000000 (*)> -R server serve --stdio exited 0 after *.?? seconds (glob)
- * @0000000000000000000000000000000000000000 (*)> debugbuilddag .+7 (glob)
+ * @0000000000000000000000000000000000000000 (*)> debugbuilddag .+7 (glob) (no-windows !)
+ * @0000000000000000000000000000000000000000 (*)> debugbuilddag ".+7" (glob) (windows !)
* @0000000000000000000000000000000000000000 (*)> updated served branch cache in *.???? seconds (glob)
* @0000000000000000000000000000000000000000 (*)> wrote served branch cache with 1 labels and 1 nodes (glob)
* @0000000000000000000000000000000000000000 (*)> updated served branch cache in *.???? seconds (glob)
@@ -51,7 +52,8 @@
* @0000000000000000000000000000000000000000 (*)> updated served branch cache in *.???? seconds (glob)
* @0000000000000000000000000000000000000000 (*)> wrote served branch cache with 1 labels and 1 nodes (glob)
* @0000000000000000000000000000000000000000 (*)> updated evo-ext-obscache in *.???? seconds (8r, 0o) (glob)
- * @0000000000000000000000000000000000000000 (*)> debugbuilddag .+7 exited 0 after *.?? seconds (glob)
+ * @0000000000000000000000000000000000000000 (*)> debugbuilddag .+7 exited 0 after *.?? seconds (glob) (no-windows !)
+ * @0000000000000000000000000000000000000000 (*)> debugbuilddag ".+7" exited 0 after *.?? seconds (glob) (windows !)
* @0000000000000000000000000000000000000000 (*)> blackbox (glob)
$ rm .hg/blackbox.log
$ hg log -G
@@ -95,7 +97,7 @@
* @0000000000000000000000000000000000000000 (*)> debugobsolete bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 01241442b3c2bf3211e593b549c655ea65b295e3 (glob)
* @0000000000000000000000000000000000000000 (*)> alias 'debugobsolete' expands to 'debugobsolete -d '0 0'' (glob)
* @0000000000000000000000000000000000000000 (*)> updated evo-ext-obscache in *.???? seconds (0r, 1o) (glob)
- * @0000000000000000000000000000000000000000 (*)> debugobsolete bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 01241442b3c2bf3211e593b549c655ea65b295e3 --config 'experimental.obshashrange.max-revs=1' exited 0 after *.?? seconds (glob)
+ * @0000000000000000000000000000000000000000 (*)> debugobsolete bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 01241442b3c2bf3211e593b549c655ea65b295e3 --config *experimental.obshashrange.max-revs=1* exited 0 after *.?? seconds (glob)
* @0000000000000000000000000000000000000000 (*)> debugobsolete cccccccccccccccccccccccccccccccccccccccc bebd167eb94d257ace0e814aeb98e6972ed2970d (glob)
* @0000000000000000000000000000000000000000 (*)> alias 'debugobsolete' expands to 'debugobsolete -d '0 0'' (glob)
* @0000000000000000000000000000000000000000 (*)> updated evo-ext-obscache in *.???? seconds (0r, 1o) (glob)
@@ -103,7 +105,7 @@
* @0000000000000000000000000000000000000000 (*)> debugobsolete dddddddddddddddddddddddddddddddddddddddd c8d03c1b5e94af74b772900c58259d2e08917735 (glob)
* @0000000000000000000000000000000000000000 (*)> alias 'debugobsolete' expands to 'debugobsolete -d '0 0'' (glob)
* @0000000000000000000000000000000000000000 (*)> updated evo-ext-obscache in *.???? seconds (0r, 1o) (glob)
- * @0000000000000000000000000000000000000000 (*)> debugobsolete dddddddddddddddddddddddddddddddddddddddd c8d03c1b5e94af74b772900c58259d2e08917735 --config 'experimental.obshashrange.warm-cache=0' exited 0 after *.?? seconds (glob)
+ * @0000000000000000000000000000000000000000 (*)> debugobsolete dddddddddddddddddddddddddddddddddddddddd c8d03c1b5e94af74b772900c58259d2e08917735 --config *experimental.obshashrange.warm-cache=0* exited 0 after *.?? seconds (glob)
* @0000000000000000000000000000000000000000 (*)> debugobsolete eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee 4de32a90b66cd083ebf3c00b41277aa7abca51dd (glob)
* @0000000000000000000000000000000000000000 (*)> alias 'debugobsolete' expands to 'debugobsolete -d '0 0'' (glob)
* @0000000000000000000000000000000000000000 (*)> updated evo-ext-obscache in *.???? seconds (0r, 1o) (glob)
@@ -186,10 +188,10 @@
$ hg debugobsolete ffffffffffffffffffffffffffffffffffffffff `getid '.'`
$ hg push -f --debug
pushing to ssh://user@dummy/server
- running python "*/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob)
+ running python "*/dummyssh" *user@dummy* *hg -R server serve --stdio* (glob)
sending hello command
sending between command
- remote: 466
+ remote: * (glob)
remote: capabilities: _evoext_getbundle_obscommon _evoext_obshash_0 _evoext_obshash_1 _evoext_obshashrange_v1 batch * (glob)
remote: 1
preparing listkeys for "phases"
@@ -224,7 +226,7 @@
45f8b879de922f6a6e620ba04205730335b6fc7e
sending unbundle command
bundle2-output-bundle: "HG20", 4 parts total
- bundle2-output-part: "replycaps" 172 bytes payload
+ bundle2-output-part: "replycaps" * bytes payload (glob)
bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
bundle2-output-part: "obsmarkers" streamed payload
@@ -497,18 +499,22 @@
3 2dc09a01254d 3 1 4 26f996446ecb
1 66f7d451a68b 1 1 2 327c7dd73d29
$ hg blackbox
- * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobsolete --rev '::6' (glob)
+ * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobsolete --rev '::6' (glob) (no-windows !)
+ * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobsolete --rev ::6 (glob) (windows !)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> alias 'debugobsolete' expands to 'debugobsolete -d '0 0'' (glob)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> writing .hg/cache/tags2-visible with 0 tags (glob)
- * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobsolete --rev '::6' exited 0 after *.?? seconds (glob)
- * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev 'heads(all())' (glob)
+ * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobsolete --rev '::6' exited 0 after *.?? seconds (glob) (no-windows !)
+ * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobsolete --rev ::6 exited 0 after *.?? seconds (glob) (windows !)
+ * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev 'heads(all())' (glob) (no-windows !)
+ * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev "heads(all())" (glob) (windows !)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> strip detected, evo-ext-stablerange-mergepoint cache reset (glob)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> strip detected, evo-ext-depthcache cache reset (glob)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-depthcache in *.???? seconds (8r) (glob)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-stablerange-mergepoint in *.???? seconds (8r) (glob)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> obshashcache clean - new markers affect 2 changeset and cached ranges (glob)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obshashrange in *.???? seconds (2r, 3o) (glob)
- * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev 'heads(all())' exited 0 after *.?? seconds (glob)
+ * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev 'heads(all())' exited 0 after *.?? seconds (glob) (no-windows !)
+ * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev "heads(all())" exited 0 after *.?? seconds (glob) (windows !)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> pull -r f69452c5b1af6cbaaa56ef50cf94fff5bcc6ca23 (glob)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> strip detected, evo-ext-stablerange-mergepoint cache reset (glob)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> strip detected, evo-ext-depthcache cache reset (glob)
@@ -517,14 +523,16 @@
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> obsdiscovery, 2/7 mismatch - 1 obshashrange queries in *.???? seconds (glob)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obscache in *.???? seconds (0r, 2o) (glob)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> pull -r f69452c5b1af6cbaaa56ef50cf94fff5bcc6ca23 exited 0 after *.?? seconds (glob)
- * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev 'desc("r3")' (glob)
+ * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev 'desc("r3")' (glob) (no-windows !)
+ * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev "desc(\"r3\")" (glob) (windows !)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> strip detected, evo-ext-stablerange-mergepoint cache reset (glob)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> strip detected, evo-ext-depthcache cache reset (glob)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-depthcache in *.???? seconds (8r) (glob)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-stablerange-mergepoint in *.???? seconds (8r) (glob)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> obshashcache clean - new markers affect 2 changeset and cached ranges (glob)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obshashrange in *.???? seconds (0r, 2o) (glob)
- * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev 'desc("r3")' exited 0 after *.?? seconds (glob)
+ * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev 'desc("r3")' exited 0 after *.?? seconds (glob) (no-windows !)
+ * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev "desc(\"r3\")" exited 0 after *.?? seconds (glob) (windows !)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> blackbox (glob)
$ rm .hg/blackbox.log
@@ -583,14 +591,16 @@
$ hg rollback
repository tip rolled back to revision 7 (undo pull)
$ hg blackbox
- * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev 'heads(all())' (glob)
+ * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev 'heads(all())' (glob) (no-windows !)
+ * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev "heads(all())" (glob) (windows !)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> strip detected, evo-ext-stablerange-mergepoint cache reset (glob)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> strip detected, evo-ext-depthcache cache reset (glob)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-depthcache in *.???? seconds (8r) (glob)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-stablerange-mergepoint in *.???? seconds (8r) (glob)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> obshashcache clean - new markers affect 1 changeset and cached ranges (glob)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obshashrange in *.???? seconds (0r, 1o) (glob)
- * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev 'heads(all())' exited 0 after *.?? seconds (glob)
+ * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev 'heads(all())' exited 0 after *.?? seconds (glob) (no-windows !)
+ * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev "heads(all())" exited 0 after *.?? seconds (glob) (windows !)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> pull (glob)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> strip detected, evo-ext-stablerange-mergepoint cache reset (glob)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> strip detected, evo-ext-depthcache cache reset (glob)
@@ -751,14 +761,16 @@
o 0 1ea73414a91b r0
$ hg blackbox
- * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev 'heads(all())' (glob)
+ * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev 'heads(all())' (glob) (no-windows !)
+ * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev "heads(all())" (glob) (windows !)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> writing .hg/cache/tags2-visible with 0 tags (glob)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> strip detected, evo-ext-stablerange-mergepoint cache reset (glob)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> strip detected, evo-ext-depthcache cache reset (glob)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-depthcache in *.???? seconds (9r) (glob)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-stablerange-mergepoint in *.???? seconds (9r) (glob)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obshashrange in *.???? seconds (1r, 1o) (glob)
- * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev 'heads(all())' exited 0 after *.?? seconds (glob)
+ * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev 'heads(all())' exited 0 after *.?? seconds (glob) (no-windows !)
+ * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> debugobshashrange --subranges --rev "heads(all())" exited 0 after *.?? seconds (glob) (windows !)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> log -G (glob)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> log -G exited 0 after *.?? seconds (glob)
* @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> strip -r 'desc("foo")' (glob)
@@ -769,7 +781,8 @@
* @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated base branch cache in *.???? seconds (glob)
* @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> wrote base branch cache with 1 labels and 1 nodes (glob)
* @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> 3 incoming changes - new heads: 4de32a90b66c (glob)
- * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> --config 'extensions.strip=' strip -r 'desc("foo")' exited 0 after *.?? seconds (glob)
+ * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> --config 'extensions.strip=' strip -r 'desc("foo")' exited 0 after *.?? seconds (glob) (no-windows !)
+ * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> --config "extensions.strip=" strip -r "desc(\"foo\")" exited 0 after *.?? seconds (glob) (windows !)
* @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> log -G (glob)
* @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> writing .hg/cache/tags2-visible with 0 tags (glob)
* @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> log -G exited 0 after *.?? seconds (glob)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-evolve-continue.t Wed Mar 21 16:32:54 2018 +0100
@@ -0,0 +1,375 @@
+Testing the continue functionality of `hg evolve`
+
+ $ cat >> $HGRCPATH <<EOF
+ > [ui]
+ > interactive = True
+ > [alias]
+ > glog = log -GT "{rev}:{node|short} {desc}\n ({bookmarks}) {phase}"
+ > [extensions]
+ > rebase =
+ > EOF
+ $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext3rd/evolve/" >> $HGRCPATH
+
+Setting up the repo
+
+ $ hg init repo
+ $ cd repo
+ $ echo ".*\.orig" > .hgignore
+ $ hg add .hgignore
+ $ hg ci -m "added hgignore"
+ $ for ch in a b c d; do echo foo>$ch; hg add $ch; hg ci -qm "added "$ch; done
+
+ $ hg glog
+ @ 4:c41c793e0ef1 added d
+ | () draft
+ o 3:ca1b80f7960a added c
+ | () draft
+ o 2:b1661037fa25 added b
+ | () draft
+ o 1:c7586e2a9264 added a
+ | () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+Simple case of evolve --continue
+
+ $ hg up ca1b80f7960a
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ echo bar > d
+ $ hg add d
+ $ hg amend
+ 1 new unstable changesets
+ $ hg glog
+ @ 6:cb6a2ab625bb added c
+ | () draft
+ | o 4:c41c793e0ef1 added d
+ | | () draft
+ | x 3:ca1b80f7960a added c
+ |/ () draft
+ o 2:b1661037fa25 added b
+ | () draft
+ o 1:c7586e2a9264 added a
+ | () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+ $ hg evolve --all
+ move:[4] added d
+ atop:[6] added c
+ merging d
+ warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
+ evolve failed!
+ fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+ abort: unresolved merge conflicts (see hg help resolve)
+ [255]
+
+ $ echo foo > d
+ $ hg resolve -m
+ (no more unresolved files)
+ continue: hg evolve --continue
+
+ $ hg evolve --continue
+ evolving 4:c41c793e0ef1 "added d"
+ working directory is now at 2a4e03d422e2
+
+ $ hg glog
+ @ 7:2a4e03d422e2 added d
+ | () draft
+ o 6:cb6a2ab625bb added c
+ | () draft
+ o 2:b1661037fa25 added b
+ | () draft
+ o 1:c7586e2a9264 added a
+ | () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+Case when conflicts resolution lead to empty wdir in evolve --continue
+
+ $ echo foo > e
+ $ hg ci -Aqm "added e"
+ $ hg prev
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ [7] added d
+ $ echo bar > e
+ $ hg add e
+ $ hg amend
+ 1 new unstable changesets
+
+ $ hg glog
+ @ 10:00a5c774cc37 added d
+ | () draft
+ | o 8:ad0a59d83efe added e
+ | | () draft
+ | x 7:2a4e03d422e2 added d
+ |/ () draft
+ o 6:cb6a2ab625bb added c
+ | () draft
+ o 2:b1661037fa25 added b
+ | () draft
+ o 1:c7586e2a9264 added a
+ | () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+ $ hg evolve
+ move:[8] added e
+ atop:[10] added d
+ merging e
+ warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
+ evolve failed!
+ fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+ abort: unresolved merge conflicts (see hg help resolve)
+ [255]
+
+ $ echo bar > e
+ $ hg resolve -m
+ (no more unresolved files)
+ continue: hg evolve --continue
+ $ hg diff
+
+XXX: maybe we should add a message here about evolve resulting in no commit
+ $ hg evolve --continue
+ evolving 8:ad0a59d83efe "added e"
+
+ $ hg glog
+ @ 10:00a5c774cc37 added d
+ | () draft
+ o 6:cb6a2ab625bb added c
+ | () draft
+ o 2:b1661037fa25 added b
+ | () draft
+ o 1:c7586e2a9264 added a
+ | () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+Case when there are a lot of revision to continue
+
+ $ hg up c7586e2a9264
+ 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
+ $ echo bar > b
+ $ hg add b
+ $ hg amend
+ 3 new unstable changesets
+
+ $ hg evolve --all
+ move:[2] added b
+ atop:[12] added a
+ merging b
+ warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
+ evolve failed!
+ fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+ abort: unresolved merge conflicts (see hg help resolve)
+ [255]
+
+ $ echo foo > b
+ $ hg resolve -m
+ (no more unresolved files)
+ continue: hg evolve --continue
+ $ hg evolve --continue
+ evolving 2:b1661037fa25 "added b"
+ move:[6] added c
+ atop:[13] added b
+ move:[10] added d
+ atop:[14] added c
+ working directory is now at 6642d2c9176e
+
+ $ hg glog
+ @ 15:6642d2c9176e added d
+ | () draft
+ o 14:95665a2de664 added c
+ | () draft
+ o 13:87f748868183 added b
+ | () draft
+ o 12:53b632d203d8 added a
+ | () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+Conlicts -> resolve -> continue -> conflicts -> resolve -> continue
+Test multiple conflicts in one evolve
+
+ $ for ch in f g h; do echo foo > $ch; hg add $ch; hg ci -m "added "$ch; done;
+
+ $ hg glog
+ @ 18:09becba8f97d added h
+ | () draft
+ o 17:5aa7b2bbd944 added g
+ | () draft
+ o 16:be88f889b6dc added f
+ | () draft
+ o 15:6642d2c9176e added d
+ | () draft
+ o 14:95665a2de664 added c
+ | () draft
+ o 13:87f748868183 added b
+ | () draft
+ o 12:53b632d203d8 added a
+ | () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+ $ hg up 95665a2de664
+ 1 files updated, 0 files merged, 4 files removed, 0 files unresolved
+
+ $ echo bar > f
+ $ echo bar > h
+ $ hg add f h
+ $ hg amend
+ 4 new unstable changesets
+
+ $ hg glog
+ @ 20:645135c5caa4 added c
+ | () draft
+ | o 18:09becba8f97d added h
+ | | () draft
+ | o 17:5aa7b2bbd944 added g
+ | | () draft
+ | o 16:be88f889b6dc added f
+ | | () draft
+ | o 15:6642d2c9176e added d
+ | | () draft
+ | x 14:95665a2de664 added c
+ |/ () draft
+ o 13:87f748868183 added b
+ | () draft
+ o 12:53b632d203d8 added a
+ | () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+ $ hg evolve --all
+ move:[15] added d
+ atop:[20] added c
+ move:[16] added f
+ atop:[21] added d
+ merging f
+ warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
+ evolve failed!
+ fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+ abort: unresolved merge conflicts (see hg help resolve)
+ [255]
+
+ $ echo foo > f
+ $ hg resolve -m
+ (no more unresolved files)
+ continue: hg evolve --continue
+ $ hg evolve --continue
+ evolving 16:be88f889b6dc "added f"
+ move:[17] added g
+ atop:[22] added f
+ move:[18] added h
+ atop:[23] added g
+ merging h
+ warning: conflicts while merging h! (edit, then use 'hg resolve --mark')
+ evolve failed!
+ fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+ abort: unresolved merge conflicts (see hg help resolve)
+ [255]
+
+ $ echo foo > h
+ $ hg resolve -m
+ (no more unresolved files)
+ continue: hg evolve --continue
+ $ hg evolve --continue
+ evolving 18:09becba8f97d "added h"
+ working directory is now at 3ba9d3d1b089
+
+Make sure, confirmopt is respected while continue
+
+ $ hg glog
+ @ 24:3ba9d3d1b089 added h
+ | () draft
+ o 23:981e615b14ca added g
+ | () draft
+ o 22:5794f1a3cbb2 added f
+ | () draft
+ o 21:e47537da02b3 added d
+ | () draft
+ o 20:645135c5caa4 added c
+ | () draft
+ o 13:87f748868183 added b
+ | () draft
+ o 12:53b632d203d8 added a
+ | () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+ $ hg up 5794f1a3cbb2
+ 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ echo bar > g
+ $ hg add g
+ $ hg amend
+ 2 new unstable changesets
+
+ $ hg evolve --all --confirm<<EOF
+ > y
+ > EOF
+ move:[23] added g
+ atop:[26] added f
+ perform evolve? [Ny] y
+ merging g
+ warning: conflicts while merging g! (edit, then use 'hg resolve --mark')
+ evolve failed!
+ fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+ abort: unresolved merge conflicts (see hg help resolve)
+ [255]
+
+ $ echo foo > g
+ $ hg resolve -m
+ (no more unresolved files)
+ continue: hg evolve --continue
+
+XXX: this should have asked for confirmation
+
+ $ hg evolve --continue<<EOF
+ > y
+ > EOF
+ evolving 23:981e615b14ca "added g"
+ move:[24] added h
+ atop:[27] added g
+ perform evolve? [Ny] y
+ working directory is now at af6bd002a48d
+
+ $ hg glog
+ @ 28:af6bd002a48d added h
+ | () draft
+ o 27:d2c94a8f44bd added g
+ | () draft
+ o 26:9849fa96c885 added f
+ | () draft
+ o 21:e47537da02b3 added d
+ | () draft
+ o 20:645135c5caa4 added c
+ | () draft
+ o 13:87f748868183 added b
+ | () draft
+ o 12:53b632d203d8 added a
+ | () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+Testing `evolve --continue` after `hg next --evolve`
+
+ $ hg up .^^
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ echo foobar > g
+ $ hg amend
+ 2 new unstable changesets
+
+ $ hg next --evolve
+ move:[27] added g
+ atop:[30] added f
+ merging g
+ warning: conflicts while merging g! (edit, then use 'hg resolve --mark')
+ evolve failed!
+ fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+ abort: unresolved merge conflicts (see hg help resolve)
+ [255]
+ $ echo foo > g
+ $ hg resolve -m
+ (no more unresolved files)
+ continue: hg evolve --continue
+ $ hg evolve --continue
+ evolving 27:d2c94a8f44bd "added g"
--- a/tests/test-evolve-cycles.t Sat Jan 20 12:38:10 2018 +0100
+++ b/tests/test-evolve-cycles.t Wed Mar 21 16:32:54 2018 +0100
@@ -292,8 +292,8 @@
0
],
"effect": [
- "description",
- "parent",
+ *, (glob)
+ *, (glob)
"content"
],
"succnodes": [
@@ -315,8 +315,8 @@
0
],
"effect": [
- "description",
- "parent",
+ *, (glob)
+ *, (glob)
"content"
],
"succnodes": [
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-evolve-orphan-merge.t Wed Mar 21 16:32:54 2018 +0100
@@ -0,0 +1,606 @@
+** Testing resolution of orphans by `hg evolve` when merges are involved **
+
+ $ cat >> $HGRCPATH <<EOF
+ > [ui]
+ > interactive = True
+ > [alias]
+ > glog = log -GT "{rev}:{node|short} {desc}\n ({bookmarks}) {phase}"
+ > [extensions]
+ > rebase =
+ > EOF
+ $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext3rd/evolve/" >> $HGRCPATH
+
+Repo Setup
+
+ $ hg init repo
+ $ cd repo
+ $ echo ".*\.orig" > .hgignore
+ $ hg add .hgignore
+ $ hg ci -m "added hgignore"
+
+An orphan merge changeset with one of the parent obsoleted
+==========================================================
+
+1) When merging both the parents does not result in conflicts
+-------------------------------------------------------------
+
+ $ echo foo > a
+ $ hg ci -Aqm "added a"
+ $ hg up .^
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ echo foo > b
+ $ hg ci -Aqm "added b"
+ $ hg merge
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ (branch merge, don't forget to commit)
+ $ hg ci -m "merging a and b"
+
+ $ hg glog
+ @ 3:3b2b6f4652ee merging a and b
+ |\ () draft
+ | o 2:d76850646258 added b
+ | | () draft
+ o | 1:c7586e2a9264 added a
+ |/ () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+Testing with obsoleting the second parent
+
+ $ hg up d76850646258
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ echo bar > b
+ $ hg amend
+ 1 new unstable changesets
+
+ $ hg glog
+ @ 5:64370c9805e7 added b
+ | () draft
+ | o 3:3b2b6f4652ee merging a and b
+ | |\ () draft
+ +---x 2:d76850646258 added b
+ | | () draft
+ | o 1:c7586e2a9264 added a
+ |/ () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+ $ hg evolve --all
+ move:[3] merging a and b
+ atop:[5] added b
+ working directory is now at 91fd62122a4b
+
+ $ hg glog
+ @ 6:91fd62122a4b merging a and b
+ |\ () draft
+ | o 5:64370c9805e7 added b
+ | | () draft
+ o | 1:c7586e2a9264 added a
+ |/ () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+ $ hg parents
+ changeset: 6:91fd62122a4b
+ tag: tip
+ parent: 5:64370c9805e7
+ parent: 1:c7586e2a9264
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: merging a and b
+
+
+Testing with obsoleting the first parent
+
+ $ hg up c7586e2a9264
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ echo bar > a
+ $ hg amend
+ 1 new unstable changesets
+
+ $ hg glog
+ @ 8:3d41537b44ca added a
+ | () draft
+ | o 6:91fd62122a4b merging a and b
+ | |\ () draft
+ +---o 5:64370c9805e7 added b
+ | | () draft
+ | x 1:c7586e2a9264 added a
+ |/ () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+ $ hg evolve --all
+ move:[6] merging a and b
+ atop:[8] added a
+ working directory is now at 968d205ba4d8
+
+ $ hg glog
+ @ 9:968d205ba4d8 merging a and b
+ |\ () draft
+ | o 8:3d41537b44ca added a
+ | | () draft
+ o | 5:64370c9805e7 added b
+ |/ () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+ $ hg parents
+ changeset: 9:968d205ba4d8
+ tag: tip
+ parent: 8:3d41537b44ca
+ parent: 5:64370c9805e7
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: merging a and b
+
+2) When merging both the parents resulted in conflicts
+------------------------------------------------------
+
+ $ hg up 8fa14d15e168
+ 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+ $ echo foo > c
+ $ hg ci -Aqm "foo to c"
+ $ hg prev
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ [0] added hgignore
+ $ echo bar > c
+ $ hg ci -Aqm "bar to c"
+
+ $ hg glog
+ @ 11:d0f84b25d4e3 bar to c
+ | () draft
+ | o 10:1c165c673853 foo to c
+ |/ () draft
+ | o 9:968d205ba4d8 merging a and b
+ | |\ () draft
+ +---o 8:3d41537b44ca added a
+ | | () draft
+ | o 5:64370c9805e7 added b
+ |/ () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+Prune old test changesets to have clear graph view
+ $ hg prune -r 64370c9805e7 -r 3d41537b44ca -r 968d205ba4d8
+ 3 changesets pruned
+
+ $ hg glog
+ @ 11:d0f84b25d4e3 bar to c
+ | () draft
+ | o 10:1c165c673853 foo to c
+ |/ () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+ $ hg merge
+ merging c
+ warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
+ 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+ use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+ [1]
+ $ echo foobar > c
+ $ hg resolve -m
+ (no more unresolved files)
+ $ hg ci -m "foobar to c"
+
+ $ hg glog
+ @ 12:fd41d25a3e90 foobar to c
+ |\ () draft
+ | o 11:d0f84b25d4e3 bar to c
+ | | () draft
+ o | 10:1c165c673853 foo to c
+ |/ () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+Testing with first parent obsoleted
+
+ $ hg up 1c165c673853
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ echo FOO > c
+ $ hg amend
+ 1 new unstable changesets
+
+ $ hg glog
+ @ 14:31c317b7bdb1 foo to c
+ | () draft
+ | o 12:fd41d25a3e90 foobar to c
+ | |\ () draft
+ +---o 11:d0f84b25d4e3 bar to c
+ | | () draft
+ | x 10:1c165c673853 foo to c
+ |/ () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+ $ hg evolve --all
+ move:[12] foobar to c
+ atop:[14] foo to c
+ merging c
+ warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
+ evolve failed!
+ fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+ abort: unresolved merge conflicts (see hg help resolve)
+ [255]
+
+ $ echo FOObar > c
+ $ hg resolve -m
+ (no more unresolved files)
+ continue: hg evolve --continue
+ $ hg evolve --continue
+ evolving 12:fd41d25a3e90 "foobar to c"
+ working directory is now at c5405d2da7a1
+
+ $ hg glog
+ @ 15:c5405d2da7a1 foobar to c
+ |\ () draft
+ | o 14:31c317b7bdb1 foo to c
+ | | () draft
+ o | 11:d0f84b25d4e3 bar to c
+ |/ () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+ $ hg parents
+ changeset: 15:c5405d2da7a1
+ tag: tip
+ parent: 11:d0f84b25d4e3
+ parent: 14:31c317b7bdb1
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: foobar to c
+
+Testing a conlficting merge with second parent obsoleted
+
+ $ hg up 31c317b7bdb1
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ echo foo > c
+ $ hg amend
+ 1 new unstable changesets
+
+ $ hg glog
+ @ 17:928097d0b5b5 foo to c
+ | () draft
+ | o 15:c5405d2da7a1 foobar to c
+ | |\ () draft
+ +---x 14:31c317b7bdb1 foo to c
+ | | () draft
+ | o 11:d0f84b25d4e3 bar to c
+ |/ () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+ $ hg evolve --all
+ move:[15] foobar to c
+ atop:[17] foo to c
+ merging c
+ warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
+ evolve failed!
+ fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+ abort: unresolved merge conflicts (see hg help resolve)
+ [255]
+
+ $ echo foobar > c
+ $ hg resolve -m
+ (no more unresolved files)
+ continue: hg evolve --continue
+
+ $ hg evolve --continue
+ evolving 15:c5405d2da7a1 "foobar to c"
+ working directory is now at dc1948a6eeab
+
+ $ hg glog
+ @ 18:dc1948a6eeab foobar to c
+ |\ () draft
+ | o 17:928097d0b5b5 foo to c
+ | | () draft
+ o | 11:d0f84b25d4e3 bar to c
+ |/ () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+3) When stabilizing other changesets resulted in orphan merge changeset
+-----------------------------------------------------------------------
+
+ $ hg prune -r d0f84b25d4e3 -r 928097d0b5b5 -r dc1948a6eeab
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ working directory now at 8fa14d15e168
+ 3 changesets pruned
+
+ $ for ch in l m; do echo foo > $ch; hg ci -Aqm "added "$ch; done;
+ $ hg up 8fa14d15e168
+ 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+ $ for ch in x y; do echo foo > $ch; hg ci -Aqm "added "$ch; done;
+ $ hg glog
+ @ 22:863d11043c67 added y
+ | () draft
+ o 21:3f2247835c1d added x
+ | () draft
+ | o 20:e44dc179e7f5 added m
+ | | () draft
+ | o 19:8634bee7bf1e added l
+ |/ () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+ $ hg merge
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ (branch merge, don't forget to commit)
+ $ hg ci -m "merge commit"
+
+ $ hg up 8634bee7bf1e
+ 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
+ $ echo bar > l
+ $ hg amend
+ 2 new unstable changesets
+
+ $ hg glog
+ @ 25:fccc9de66799 added l
+ | () draft
+ | o 23:190763373d8b merge commit
+ | |\ () draft
+ | | o 22:863d11043c67 added y
+ | | | () draft
+ +---o 21:3f2247835c1d added x
+ | | () draft
+ | o 20:e44dc179e7f5 added m
+ | | () draft
+ | x 19:8634bee7bf1e added l
+ |/ () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+ $ hg evolve --all
+ move:[20] added m
+ atop:[25] added l
+ move:[23] merge commit
+ atop:[26] added m
+ working directory is now at a446ad3e6700
+
+ $ hg glog
+ @ 27:a446ad3e6700 merge commit
+ |\ () draft
+ | o 26:495d2039f8f1 added m
+ | | () draft
+ | o 25:fccc9de66799 added l
+ | | () draft
+ o | 22:863d11043c67 added y
+ | | () draft
+ o | 21:3f2247835c1d added x
+ |/ () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+4) When both the parents of the merge changeset are obsolete with a succ
+------------------------------------------------------------------------
+
+ $ hg prune -r a446ad3e6700 -r 495d2039f8f1 -r 863d11043c67
+ 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
+ working directory now at fccc9de66799
+ 3 changesets pruned
+
+ $ hg glog
+ @ 25:fccc9de66799 added l
+ | () draft
+ | o 21:3f2247835c1d added x
+ |/ () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+ $ hg merge
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ (branch merge, don't forget to commit)
+ $ hg ci -m "merged l and x"
+
+ $ hg up fccc9de66799
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ echo foobar > l
+ $ hg amend
+ 1 new unstable changesets
+ $ hg up 3f2247835c1d
+ 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ echo bar > x
+ $ hg amend
+ $ hg glog
+ @ 32:cdf6547da25f added x
+ | () draft
+ | o 30:3f371171d767 added l
+ |/ () draft
+ | o 28:7b78a9784f3e merged l and x
+ | |\ () draft
+ +---x 25:fccc9de66799 added l
+ | | () draft
+ | x 21:3f2247835c1d added x
+ |/ () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+XXX: We should handle this case too
+ $ hg evolve --all
+ move:[28] merged l and x
+ atop:[32] added x
+ move:[33] merged l and x
+ atop:[30] added l
+ working directory is now at adb665a78e08
+
+ $ hg glog
+ @ 34:adb665a78e08 merged l and x
+ |\ () draft
+ | o 32:cdf6547da25f added x
+ | | () draft
+ o | 30:3f371171d767 added l
+ |/ () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+ $ hg exp
+ # HG changeset patch
+ # User test
+ # Date 0 0
+ # Thu Jan 01 00:00:00 1970 +0000
+ # Node ID adb665a78e08b962cff415301058d782086c0f33
+ # Parent 3f371171d767ef79cf85d156cf46d4035960fcf0
+ # Parent cdf6547da25f1ca5d01102302ad713f444547b48
+ merged l and x
+
+ diff -r 3f371171d767 -r adb665a78e08 x
+ --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+ +++ b/x Thu Jan 01 00:00:00 1970 +0000
+ @@ -0,0 +1,1 @@
+ +bar
+
+ $ hg parents
+ changeset: 34:adb665a78e08
+ tag: tip
+ parent: 30:3f371171d767
+ parent: 32:cdf6547da25f
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: merged l and x
+
+
+5) When one of the merge parent is pruned without a successor
+-------------------------------------------------------------
+
+ $ hg prune -r cdf6547da25f
+ 1 changesets pruned
+ 1 new unstable changesets
+ $ hg glog
+ @ 34:adb665a78e08 merged l and x
+ |\ () draft
+ | x 32:cdf6547da25f added x
+ | | () draft
+ o | 30:3f371171d767 added l
+ |/ () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+ $ hg evolve --rev .
+ move:[34] merged l and x
+ atop:[0] added hgignore
+ working directory is now at fb8fe870ae7d
+
+ $ hg glog
+ @ 35:fb8fe870ae7d merged l and x
+ |\ () draft
+ | o 30:3f371171d767 added l
+ |/ () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+6) When one parent is pruned without successor and the other parent of merge is
+the parent of the pruned commit
+--------------------------------------------------------------------------------
+
+ $ hg glog
+ @ 35:fb8fe870ae7d merged l and x
+ |\ () draft
+ | o 30:3f371171d767 added l
+ |/ () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+ $ hg prune -r 3f371171d767
+ 1 changesets pruned
+ 1 new unstable changesets
+
+ $ hg glog
+ @ 35:fb8fe870ae7d merged l and x
+ |\ () draft
+ | x 30:3f371171d767 added l
+ |/ () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+This is the right thing to do here. When you have a merge changeset, and one
+parent is pruned and parent of that pruned parent is same as another parent of
+the merge changeset, that should lead to merge changeset being a non-merge
+changeset and non-pruned changeset as its only parent
+
+If you look at the above graph, the side part:
+
+\
+ x
+/
+
+is redundant now as the changeset is pruned and we should remove this chain
+while evolving.
+
+This case can occur a lot of times in workflows where people make branches and
+merge them again. After getting their work done, they may want to get rid of
+that branch and they prune all their changeset, which will result in this
+case where merge commit becomes orphan with its ancestors pruned up until a
+point where the other parent of merge is the first non-pruned ancestor.
+
+ $ hg evolve -r .
+ move:[35] merged l and x
+ atop:[0] added hgignore
+ working directory is now at b61ba77b924a
+
+ $ hg glog
+ @ 36:b61ba77b924a merged l and x
+ | () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+7) When one parent is pruned without successor and has no parent
+----------------------------------------------------------------
+
+ $ hg prune -r .
+ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ working directory now at 8fa14d15e168
+ 1 changesets pruned
+ $ hg up null
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+
+ $ echo foo > foo
+ $ hg add foo
+ $ hg ci -m "added foo"
+ created new head
+
+ $ hg merge
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ (branch merge, don't forget to commit)
+ $ hg ci -m "merge commit"
+ $ hg glog
+ @ 38:32beb84b9dbc merge commit
+ |\ () draft
+ | o 37:f3ba8b99bb6f added foo
+ | () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+ $ hg prune -r f3ba8b99bb6f
+ 1 changesets pruned
+ 1 new unstable changesets
+
+ $ hg glog
+ @ 38:32beb84b9dbc merge commit
+ |\ () draft
+ | x 37:f3ba8b99bb6f added foo
+ | () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+The current behavior seems to be the correct behavior in the above case. This is
+also another case which can arise flow merge workflow where people start a
+branch from null changeset and merge it and then prune it or get rid of it.
+
+Also if you look at the above graph, the side part:
+
+\
+ x
+
+becomes redundant as the changeset is pruned without successor and we should
+just remove that chain.
+
+ $ hg evolve -r .
+ move:[38] merge commit
+ atop:[-1]
+ working directory is now at d2a03dd8c951
+
+ $ hg glog
+ @ 39:d2a03dd8c951 merge commit
+ | () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-evolve-orphan-split.t Wed Mar 21 16:32:54 2018 +0100
@@ -0,0 +1,276 @@
+** Testing resolution of orphans by `hg evolve` where an obsolete changeset has
+multiple successors **
+
+ $ cat >> $HGRCPATH <<EOF
+ > [defaults]
+ > amend=-d "0 0"
+ > fold=-d "0 0"
+ > split=-d "0 0"
+ > amend=-d "0 0"
+ > [ui]
+ > interactive = True
+ > [alias]
+ > glog = log -GT "{rev}:{node|short} {desc}\n ({bookmarks}) {phase}"
+ > [extensions]
+ > rebase =
+ > EOF
+ $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext3rd/evolve/" >> $HGRCPATH
+
+Repo Setup
+
+ $ hg init repo
+ $ cd repo
+ $ echo ".*\.orig" > .hgignore
+ $ hg add .hgignore
+ $ hg ci -m "added hgignore"
+
+An orphan changeset with parent got splitted
+--------------------------------------------
+
+ $ for ch in a b c; do echo foo > $ch; done;
+
+ $ hg add a b
+ $ hg ci -m "added a and b"
+ $ hg add c
+ $ hg ci -m "added c"
+
+ $ hg glog
+ @ 2:86e1ebf1ca61 added c
+ | () draft
+ o 1:d0ddb614efbd added a and b
+ | () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+ $ hg split -r 1 <<EOF
+ > y
+ > y
+ > n
+ > y
+ > EOF
+ 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
+ adding a
+ adding b
+ diff --git a/a b/a
+ new file mode 100644
+ examine changes to 'a'? [Ynesfdaq?] y
+
+ @@ -0,0 +1,1 @@
+ +foo
+ record change 1/2 to 'a'? [Ynesfdaq?] y
+
+ diff --git a/b b/b
+ new file mode 100644
+ examine changes to 'b'? [Ynesfdaq?] n
+
+ created new head
+ Done splitting? [yN] y
+
+ $ hg glog
+ @ 4:8b179cffc81c added a and b
+ | () draft
+ o 3:bd3735d4dab0 added a and b
+ | () draft
+ | o 2:86e1ebf1ca61 added c
+ | | () draft
+ | x 1:d0ddb614efbd added a and b
+ |/ () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+ $ hg evolve
+ move:[2] added c
+ atop:[4] added a and b
+ working directory is now at af13f0560b31
+
+ $ hg glog
+ @ 5:af13f0560b31 added c
+ | () draft
+ o 4:8b179cffc81c added a and b
+ | () draft
+ o 3:bd3735d4dab0 added a and b
+ | () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+When the successors does not form a linear chain and are multiple heads
+-----------------------------------------------------------------------
+
+ $ hg fold -r .^^::. --exact -m "added a b c"
+ 3 changesets folded
+ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+ $ hg glog
+ @ 6:f89e4764f2ed added a b c
+ | () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+ $ echo foo > d
+ $ hg ci -Aqm "added d"
+
+ $ hg glog
+ @ 7:d48a30875f01 added d
+ | () draft
+ o 6:f89e4764f2ed added a b c
+ | () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+ $ hg prev
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ [6] added a b c
+ $ hg split -r . <<EOF
+ > y
+ > n
+ > y
+ > y
+ > y
+ > y
+ > y
+ > EOF
+ 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
+ adding a
+ adding b
+ adding c
+ diff --git a/a b/a
+ new file mode 100644
+ examine changes to 'a'? [Ynesfdaq?] y
+
+ @@ -0,0 +1,1 @@
+ +foo
+ record change 1/3 to 'a'? [Ynesfdaq?] n
+
+ diff --git a/b b/b
+ new file mode 100644
+ examine changes to 'b'? [Ynesfdaq?] y
+
+ @@ -0,0 +1,1 @@
+ +foo
+ record change 2/3 to 'b'? [Ynesfdaq?] y
+
+ diff --git a/c b/c
+ new file mode 100644
+ examine changes to 'c'? [Ynesfdaq?] y
+
+ @@ -0,0 +1,1 @@
+ +foo
+ record change 3/3 to 'c'? [Ynesfdaq?] y
+
+ created new head
+ Done splitting? [yN] y
+
+ $ hg glog
+ @ 9:c0fbf8aaf6c4 added a b c
+ | () draft
+ o 8:f2632392aefe added a b c
+ | () draft
+ | o 7:d48a30875f01 added d
+ | | () draft
+ | x 6:f89e4764f2ed added a b c
+ |/ () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+ $ hg rebase -r . -d 8fa14d15e168
+ rebasing 9:c0fbf8aaf6c4 "added a b c" (tip)
+ $ hg glog
+ @ 10:7f87764e5b64 added a b c
+ | () draft
+ | o 8:f2632392aefe added a b c
+ |/ () draft
+ | o 7:d48a30875f01 added d
+ | | () draft
+ | x 6:f89e4764f2ed added a b c
+ |/ () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+ $ hg evolve --dry-run <<EOF
+ > 0
+ > EOF
+ ancestor 'd48a30875f01' split over multiple topological branches.
+ choose an evolve destination:
+ 0: [f2632392aefe] added a b c
+ 1: [7f87764e5b64] added a b c
+ q: quit the prompt
+ enter the index of the revision you want to select: 0
+ move:[7] added d
+ atop:[8] added a b c
+ hg rebase -r d48a30875f01 -d f2632392aefe
+
+ $ hg evolve --dry-run <<EOF
+ > 1
+ > EOF
+ ancestor 'd48a30875f01' split over multiple topological branches.
+ choose an evolve destination:
+ 0: [f2632392aefe] added a b c
+ 1: [7f87764e5b64] added a b c
+ q: quit the prompt
+ enter the index of the revision you want to select: 1
+ move:[7] added d
+ atop:[10] added a b c
+ hg rebase -r d48a30875f01 -d 7f87764e5b64
+
+Testing the interactive prompt with invalid values first
+(this should move its own test file when we use it at multiple places)
+
+ $ hg evolve --all <<EOF
+ > foo
+ > EOF
+ ancestor 'd48a30875f01' split over multiple topological branches.
+ choose an evolve destination:
+ 0: [f2632392aefe] added a b c
+ 1: [7f87764e5b64] added a b c
+ q: quit the prompt
+ enter the index of the revision you want to select: foo
+ invalid value 'foo' entered for index
+ could not solve instability, ambiguous destination: parent split across two branches
+
+ $ hg evolve --all <<EOF
+ > 4
+ > EOF
+ ancestor 'd48a30875f01' split over multiple topological branches.
+ choose an evolve destination:
+ 0: [f2632392aefe] added a b c
+ 1: [7f87764e5b64] added a b c
+ q: quit the prompt
+ enter the index of the revision you want to select: 4
+ invalid value '4' entered for index
+ could not solve instability, ambiguous destination: parent split across two branches
+
+ $ hg evolve --all <<EOF
+ > -1
+ > EOF
+ ancestor 'd48a30875f01' split over multiple topological branches.
+ choose an evolve destination:
+ 0: [f2632392aefe] added a b c
+ 1: [7f87764e5b64] added a b c
+ q: quit the prompt
+ enter the index of the revision you want to select: -1
+ invalid value '-1' entered for index
+ could not solve instability, ambiguous destination: parent split across two branches
+
+ $ hg evolve --all <<EOF
+ > q
+ > EOF
+ ancestor 'd48a30875f01' split over multiple topological branches.
+ choose an evolve destination:
+ 0: [f2632392aefe] added a b c
+ 1: [7f87764e5b64] added a b c
+ q: quit the prompt
+ enter the index of the revision you want to select: q
+ could not solve instability, ambiguous destination: parent split across two branches
+
+Doing the evolve with the interactive prompt
+
+ $ hg evolve --all <<EOF
+ > 1
+ > EOF
+ ancestor 'd48a30875f01' split over multiple topological branches.
+ choose an evolve destination:
+ 0: [f2632392aefe] added a b c
+ 1: [7f87764e5b64] added a b c
+ q: quit the prompt
+ enter the index of the revision you want to select: 1
+ move:[7] added d
+ atop:[10] added a b c
+ working directory is now at 1c6caa7c902a
--- a/tests/test-evolve-phase.t Sat Jan 20 12:38:10 2018 +0100
+++ b/tests/test-evolve-phase.t Wed Mar 21 16:32:54 2018 +0100
@@ -115,8 +115,10 @@
$ echo c2 > a
$ hg resolve -m
(no more unresolved files)
+ continue: hg evolve --continue
$ hg evolve -c
evolving 2:13833940840c "c"
+ working directory is now at 3d2080c198e5
$ hg glog
@ 5 - 3d2080c198e5 c (secret)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-evolve-stop.t Wed Mar 21 16:32:54 2018 +0100
@@ -0,0 +1,382 @@
+Tests for the --stop flag for `hg evolve` command
+=================================================
+
+The `--stop` flag stops the interrupted evolution and delete the state file so
+user can do other things and comeback and do evolution later on
+
+Setup
+=====
+
+ $ cat >> $HGRCPATH <<EOF
+ > [alias]
+ > glog = log -GT "{rev}:{node|short} {desc}\n ({bookmarks}) {phase}"
+ > [extensions]
+ > EOF
+ $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext3rd/evolve/" >> $HGRCPATH
+
+ $ hg init stoprepo
+ $ cd stoprepo
+ $ echo ".*\.orig" > .hgignore
+ $ hg add .hgignore
+ $ hg ci -m "added hgignore"
+ $ for ch in a b c d; do echo foo > $ch; hg add $ch; hg ci -qm "added "$ch; done;
+
+ $ hg glog
+ @ 4:c41c793e0ef1 added d
+ | () draft
+ o 3:ca1b80f7960a added c
+ | () draft
+ o 2:b1661037fa25 added b
+ | () draft
+ o 1:c7586e2a9264 added a
+ | () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+Testing `--stop` when no evolve is interrupted
+==============================================
+
+ $ hg evolve --stop
+ abort: no interrupted evolve to stop
+ [255]
+
+Testing with wrong combinations of flags
+========================================
+
+ $ hg evolve --stop --rev 1
+ abort: cannot specify both "--rev" and "--stop"
+ [255]
+
+ $ hg evolve --stop --continue
+ abort: cannot specify both "--stop" and "--continue"
+ [255]
+
+ $ hg evolve --stop --all
+ abort: cannot specify both "--all" and "--stop"
+ [255]
+
+ $ hg evolve --stop --any
+ abort: cannot specify both "--any" and "--stop"
+ [255]
+
+Testing when only one revision is to evolve
+===========================================
+
+ $ hg prev
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ [3] added c
+ $ echo bar > d
+ $ hg add d
+ $ hg amend
+ 1 new unstable changesets
+ $ hg glog
+ @ 6:cb6a2ab625bb added c
+ | () draft
+ | o 4:c41c793e0ef1 added d
+ | | () draft
+ | x 3:ca1b80f7960a added c
+ |/ () draft
+ o 2:b1661037fa25 added b
+ | () draft
+ o 1:c7586e2a9264 added a
+ | () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+ $ hg evolve
+ move:[4] added d
+ atop:[6] added c
+ merging d
+ warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
+ evolve failed!
+ fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+ abort: unresolved merge conflicts (see hg help resolve)
+ [255]
+
+ $ hg evolve --stop
+ stopped the interrupted evolve
+ working directory is now at cb6a2ab625bb
+
+Checking whether evolvestate file exists or not
+ $ cat .hg/evolvestate
+ cat: .hg/evolvestate: No such file or directory
+ [1]
+
+Checking where we are
+ $ hg id
+ cb6a2ab625bb tip
+
+Checking working dir
+ $ hg status
+Checking for incomplete mergestate
+ $ ls .hg/merge
+ ls: cannot access .?\.hg/merge.?: No such file or directory (re)
+ [2]
+
+Checking graph
+ $ hg glog
+ @ 6:cb6a2ab625bb added c
+ | () draft
+ | o 4:c41c793e0ef1 added d
+ | | () draft
+ | x 3:ca1b80f7960a added c
+ |/ () draft
+ o 2:b1661037fa25 added b
+ | () draft
+ o 1:c7586e2a9264 added a
+ | () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+Testing the stop flag in case conflicts are caused by `hg next --evolve`
+========================================================================
+
+ $ hg next --evolve
+ move:[4] added d
+ atop:[6] added c
+ merging d
+ warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
+ evolve failed!
+ fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+ abort: unresolved merge conflicts (see hg help resolve)
+ [255]
+
+ $ hg diff
+ diff -r cb6a2ab625bb d
+ --- a/d Thu Jan 01 00:00:00 1970 +0000
+ +++ b/d * (glob)
+ @@ -1,1 +1,5 @@
+ +<<<<<<< destination: cb6a2ab625bb - test: added c
+ bar
+ +=======
+ +foo
+ +>>>>>>> evolving: c41c793e0ef1 - test: added d
+
+ $ hg evolve --stop
+ stopped the interrupted evolve
+ working directory is now at cb6a2ab625bb
+
+ $ hg glog
+ @ 6:cb6a2ab625bb added c
+ | () draft
+ | o 4:c41c793e0ef1 added d
+ | | () draft
+ | x 3:ca1b80f7960a added c
+ |/ () draft
+ o 2:b1661037fa25 added b
+ | () draft
+ o 1:c7586e2a9264 added a
+ | () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+ $ hg status
+
+Checking when multiple revs need to be evolved, some revs evolve without
+conflicts
+=========================================================================
+
+Making sure obsmarkers should be on evolved changeset and not rest of them once
+we do `evolve --stop`
+--------------------------------------------------------------------------------
+
+ $ hg evolve
+ move:[4] added d
+ atop:[6] added c
+ merging d
+ warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
+ evolve failed!
+ fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+ abort: unresolved merge conflicts (see hg help resolve)
+ [255]
+ $ echo foo > d
+ $ hg resolve -m
+ (no more unresolved files)
+ continue: hg evolve --continue
+ $ hg evolve --continue
+ evolving 4:c41c793e0ef1 "added d"
+ working directory is now at 2a4e03d422e2
+ $ hg glog
+ @ 7:2a4e03d422e2 added d
+ | () draft
+ o 6:cb6a2ab625bb added c
+ | () draft
+ o 2:b1661037fa25 added b
+ | () draft
+ o 1:c7586e2a9264 added a
+ | () draft
+ o 0:8fa14d15e168 added hgignore
+ () draft
+
+ $ hg up .^^^^
+ 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
+ $ echo bar > c
+ $ hg add c
+ $ hg amend
+ 4 new unstable changesets
+
+ $ hg glog
+ @ 9:21817cd42526 added hgignore
+ () draft
+ o 7:2a4e03d422e2 added d
+ | () draft
+ o 6:cb6a2ab625bb added c
+ | () draft
+ o 2:b1661037fa25 added b
+ | () draft
+ o 1:c7586e2a9264 added a
+ | () draft
+ x 0:8fa14d15e168 added hgignore
+ () draft
+
+ $ hg evolve --all
+ move:[1] added a
+ atop:[9] added hgignore
+ move:[2] added b
+ atop:[10] added a
+ move:[6] added c
+ atop:[11] added b
+ merging c
+ warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
+ evolve failed!
+ fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+ abort: unresolved merge conflicts (see hg help resolve)
+ [255]
+
+ $ hg status
+ M c
+ A d
+
+ $ hg evolve --stop
+ stopped the interrupted evolve
+ working directory is now at aec285328e90
+
+Only changeset which has a successor now are obsoleted
+ $ hg glog
+ @ 11:aec285328e90 added b
+ | () draft
+ o 10:fd00db71edca added a
+ | () draft
+ o 9:21817cd42526 added hgignore
+ () draft
+ o 7:2a4e03d422e2 added d
+ | () draft
+ o 6:cb6a2ab625bb added c
+ | () draft
+ x 2:b1661037fa25 added b
+ | () draft
+ x 1:c7586e2a9264 added a
+ | () draft
+ x 0:8fa14d15e168 added hgignore
+ () draft
+
+Making sure doing evolve again resumes from right place and does the right thing
+
+ $ hg evolve --all
+ move:[6] added c
+ atop:[11] added b
+ merging c
+ warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
+ evolve failed!
+ fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+ abort: unresolved merge conflicts (see hg help resolve)
+ [255]
+
+ $ echo foobar > c
+ $ hg resolve -m
+ (no more unresolved files)
+ continue: hg evolve --continue
+ $ hg evolve --continue
+ evolving 6:cb6a2ab625bb "added c"
+ move:[7] added d
+ atop:[12] added c
+ working directory is now at cd0909a30222
+ $ hg glog
+ @ 13:cd0909a30222 added d
+ | () draft
+ o 12:cb1dd1086ef6 added c
+ | () draft
+ o 11:aec285328e90 added b
+ | () draft
+ o 10:fd00db71edca added a
+ | () draft
+ o 9:21817cd42526 added hgignore
+ () draft
+
+Bookmarks should only be moved of the changesets which have been evolved,
+bookmarks of rest of them should stay where they are are
+-------------------------------------------------------------------------
+
+ $ hg up .^
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg bookmark b1
+ $ hg up .^
+ 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ (leaving bookmark b1)
+ $ hg bookmark b2
+
+ $ hg glog
+ o 13:cd0909a30222 added d
+ | () draft
+ o 12:cb1dd1086ef6 added c
+ | (b1) draft
+ @ 11:aec285328e90 added b
+ | (b2) draft
+ o 10:fd00db71edca added a
+ | () draft
+ o 9:21817cd42526 added hgignore
+ () draft
+
+ $ hg prev
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ [10] added a
+ $ echo tom > c
+ $ hg amend
+ 3 new unstable changesets
+
+ $ hg glog
+ @ 15:a3cc2042492f added a
+ | () draft
+ | o 13:cd0909a30222 added d
+ | | () draft
+ | o 12:cb1dd1086ef6 added c
+ | | (b1) draft
+ | o 11:aec285328e90 added b
+ | | (b2) draft
+ | x 10:fd00db71edca added a
+ |/ () draft
+ o 9:21817cd42526 added hgignore
+ () draft
+
+ $ hg evolve --all
+ move:[11] added b
+ atop:[15] added a
+ move:[12] added c
+ atop:[16] added b
+ merging c
+ warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
+ evolve failed!
+ fix conflict and run 'hg evolve --continue' or use 'hg update -C .' to abort
+ abort: unresolved merge conflicts (see hg help resolve)
+ [255]
+
+ $ hg evolve --stop
+ stopped the interrupted evolve
+ working directory is now at a3f4b95da934
+
+Bookmarks of only the changeset which are evolved is moved
+ $ hg glog
+ @ 16:a3f4b95da934 added b
+ | (b2) draft
+ o 15:a3cc2042492f added a
+ | () draft
+ | o 13:cd0909a30222 added d
+ | | () draft
+ | o 12:cb1dd1086ef6 added c
+ | | (b1) draft
+ | x 11:aec285328e90 added b
+ | | () draft
+ | x 10:fd00db71edca added a
+ |/ () draft
+ o 9:21817cd42526 added hgignore
+ () draft
--- a/tests/test-evolve-topic.t Sat Jan 20 12:38:10 2018 +0100
+++ b/tests/test-evolve-topic.t Wed Mar 21 16:32:54 2018 +0100
@@ -267,3 +267,113 @@
$ hg prev
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
[20] add fff
+
+Testing issue 5708 when we are on obsolete changeset and there is active topic
+------------------------------------------------------------------------------
+
+ $ hg log --graph
+ @ 20 - {bar} 793eb6370b2d add fff (draft)
+ |
+ | o 19 - {bar} 9bf430c106b7 add jjj (draft)
+ | |
+ | o 18 - {bar} d2dc89c57700 add iii (draft)
+ | |
+ | o 17 - {bar} 20bc4d02aa62 add hhh (draft)
+ | |
+ | o 16 - {bar} 16d6f664b17c add ggg (draft)
+ | |
+ | x 15 - {foo} 070c5573d8f9 add fff (draft)
+ |/
+ o 14 - {foo} 42b49017ff90 add eee (draft)
+ |
+ o 11 - {foo} d9cacd156ffc add ddd (draft)
+ |
+ o 2 - {foo} cced9bac76e3 add ccc (draft)
+ |
+ o 1 - {} a4dbed0837ea add bbb (draft)
+ |
+ o 0 - {} 199cc73e9a0b add aaa (draft)
+
+
+ $ hg topic
+ * bar (5 changesets, 4 troubled)
+ foo (3 changesets)
+
+When the current topic, obsoleted changesets topic and successor topic are same
+
+ $ hg up 20bc4d02aa62
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ echo foobar >> hhh
+ $ hg amend
+ $ hg up 20bc4d02aa62
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ working directory parent is obsolete! (20bc4d02aa62)
+ (use 'hg evolve' to update to its successor: d834582d9ee3)
+ $ hg log -Gr 14::
+ o 22 - {bar} d834582d9ee3 add hhh (draft)
+ |
+ | o 20 - {bar} 793eb6370b2d add fff (draft)
+ | |
+ | | o 19 - {bar} 9bf430c106b7 add jjj (draft)
+ | | |
+ | | o 18 - {bar} d2dc89c57700 add iii (draft)
+ | | |
+ +---@ 17 - {bar} 20bc4d02aa62 add hhh (draft)
+ | |
+ o | 16 - {bar} 16d6f664b17c add ggg (draft)
+ | |
+ x | 15 - {foo} 070c5573d8f9 add fff (draft)
+ |/
+ o 14 - {foo} 42b49017ff90 add eee (draft)
+ |
+ ~
+
+ $ hg prev
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ [16] add ggg
+
+When the current topic and successors topic are same, but obsolete cset has
+different topic
+
+ $ hg rebase -s d2dc89c57700 -d d834582d9ee3 --config extensions.rebase=
+ rebasing 18:d2dc89c57700 "add iii"
+ rebasing 19:9bf430c106b7 "add jjj"
+ $ hg log -Gr 42b49017ff90::
+ o 24 - {bar} 7542e76aba2c add jjj (draft)
+ |
+ o 23 - {bar} 7858bd7e9906 add iii (draft)
+ |
+ o 22 - {bar} d834582d9ee3 add hhh (draft)
+ |
+ | o 20 - {bar} 793eb6370b2d add fff (draft)
+ | |
+ @ | 16 - {bar} 16d6f664b17c add ggg (draft)
+ | |
+ x | 15 - {foo} 070c5573d8f9 add fff (draft)
+ |/
+ o 14 - {foo} 42b49017ff90 add eee (draft)
+ |
+ ~
+
+ $ hg up 070c5573d8f9
+ switching to topic foo
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ working directory parent is obsolete! (070c5573d8f9)
+ (use 'hg evolve' to update to its successor: 793eb6370b2d)
+
+ $ hg topic bar
+
+ $ hg prev
+ no parent in topic "bar"
+ (do you want --no-topic)
+ [1]
+
+When current topic and obsolete cset topic are same but successor has different
+one
+
+ $ hg up 070c5573d8f9
+ switching to topic foo
+ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg prev
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ [14] add eee
--- a/tests/test-evolve.t Sat Jan 20 12:38:10 2018 +0100
+++ b/tests/test-evolve.t Wed Mar 21 16:32:54 2018 +0100
@@ -793,10 +793,10 @@
user: 10
marker size:
format v1:
- smallest length: 75
- longer length: 76
- median length: 76
- mean length: 75
+ smallest length: * (glob)
+ longer length: * (glob)
+ median length: * (glob)
+ mean length: * (glob)
format v0:
smallest length: * (glob)
longer length: * (glob)
@@ -1242,6 +1242,20 @@
(use 'hg help' for the full list of commands or 'hg -v' for details)
[255]
+Shows "use 'hg evolve' to..." hints iff the evolve command is enabled
+
+ $ hg --hidden up 14
+ 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ working directory parent is obsolete! (cce26b684bfe)
+ $ cat >> $HGRCPATH <<EOF
+ > [experimental]
+ > evolutioncommands=evolve
+ > EOF
+ $ hg --hidden up 15
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ working directory parent is obsolete! (27247fcb2df6)
+ (use 'hg evolve' to update to its successor: 24e63b319adf)
+
Restore all of the evolution features
$ cat >> $HGRCPATH <<EOF
@@ -1251,7 +1265,7 @@
Check hg evolve --rev on singled out commit
$ hg up 24e63b319adf -C
- 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ mkcommit j1
$ mkcommit j2
$ mkcommit j3
--- a/tests/test-exchange-obsmarkers-case-B5.t Sat Jan 20 12:38:10 2018 +0100
+++ b/tests/test-exchange-obsmarkers-case-B5.t Wed Mar 21 16:32:54 2018 +0100
@@ -158,7 +158,7 @@
(run 'hg update' to get a working copy)
## post pull state
# obstore: main
- 069b05c3876d56f62895e853a501ea58ea85f68d 0 {e5ea8f9c73143125d36658e90ef70c6d2027a5b7} (*) {'ef1': '*', 'user': 'test'} (glob)
+ 069b05c3876d56f62895e853a501ea58ea85f68d 0 {e5ea8f9c73143125d36658e90ef70c6d2027a5b7} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'user': 'test'}
28b51eb45704506b5c603decd6bf7ac5e0f6a52f e5ea8f9c73143125d36658e90ef70c6d2027a5b7 0 (*) {'user': 'test'} (glob)
6e72f0a95b5e01a7504743aa941f69cb1fbef8b0 069b05c3876d56f62895e853a501ea58ea85f68d 0 (*) {'user': 'test'} (glob)
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 6e72f0a95b5e01a7504743aa941f69cb1fbef8b0 0 (*) {'user': 'test'} (glob)
--- a/tests/test-fold.t Sat Jan 20 12:38:10 2018 +0100
+++ b/tests/test-fold.t Wed Mar 21 16:32:54 2018 +0100
@@ -8,7 +8,7 @@
> [extensions]
> evolve=
> [ui]
- > logtemplate = '{rev} - {node|short} {desc|firstline} [{author}] ({phase})\n'
+ > logtemplate = '{rev} - {node|short} {desc|firstline} [{author}] ({phase}) {bookmarks}\n'
> EOF
$ hg init fold-tests
@@ -16,6 +16,7 @@
$ hg debugbuilddag .+3:branchpoint+4*branchpoint+2
$ hg up 'desc("r7")'
0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg bookmark bm1
$ hg log -G
o 10 - a8407f9a3dc1 r10 [debugbuilddag] (draft)
|
@@ -23,7 +24,7 @@
|
o 8 - abf57d94268b r8 [debugbuilddag] (draft)
|
- | @ 7 - 4de32a90b66c r7 [debugbuilddag] (draft)
+ | @ 7 - 4de32a90b66c r7 [debugbuilddag] (draft) bm1
| |
| o 6 - f69452c5b1af r6 [debugbuilddag] (draft)
| |
@@ -87,6 +88,27 @@
3 changesets folded
0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+Checking whether the bookmarks are moved or not
+
+ $ hg log -G
+ @ 11 - 198b5c405d01 r5 [debugbuilddag] (draft) bm1
+ |
+ | o 10 - a8407f9a3dc1 r10 [debugbuilddag] (draft)
+ | |
+ | o 9 - 529dfc5bb875 r9 [debugbuilddag] (draft)
+ | |
+ | o 8 - abf57d94268b r8 [debugbuilddag] (draft)
+ | |
+ o | 4 - bebd167eb94d r4 [debugbuilddag] (draft)
+ |/
+ o 3 - 2dc09a01254d r3 [debugbuilddag] (draft)
+ |
+ o 2 - 01241442b3c2 r2 [debugbuilddag] (draft)
+ |
+ o 1 - 66f7d451a68b r1 [debugbuilddag] (draft)
+ |
+ o 0 - 1ea73414a91b r0 [debugbuilddag] (public)
+
(test inherited from test-evolve.t)
$ hg fold --from 6 # want to run hg fold 6
@@ -95,7 +117,7 @@
[255]
$ hg log -G
- @ 11 - 198b5c405d01 r5 [debugbuilddag] (draft)
+ @ 11 - 198b5c405d01 r5 [debugbuilddag] (draft) bm1
|
| o 10 - a8407f9a3dc1 r10 [debugbuilddag] (draft)
| |
@@ -124,7 +146,7 @@
$ hg log -G
o 12 - b568edbee6e0 r8 [debugbuilddag] (draft)
|
- | @ 11 - 198b5c405d01 r5 [debugbuilddag] (draft)
+ | @ 11 - 198b5c405d01 r5 [debugbuilddag] (draft) bm1
| |
| o 4 - bebd167eb94d r4 [debugbuilddag] (draft)
|/
@@ -144,8 +166,9 @@
$ hg commit '-m r11'
$ hg up '.^'
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ (leaving bookmark bm1)
$ hg log -G
- o 13 - 14d0e0da8e91 r11 [test] (draft)
+ o 13 - 14d0e0da8e91 r11 [test] (draft) bm1
|
| o 12 - b568edbee6e0 r8 [debugbuilddag] (draft)
| |
@@ -190,7 +213,7 @@
2 changesets folded
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg log -G
- @ 14 - 29b470a33594 r5 [Victor Rataxes <victor@rhino.savannah>] (draft)
+ @ 14 - 29b470a33594 r5 [Victor Rataxes <victor@rhino.savannah>] (draft) bm1
|
| o 12 - b568edbee6e0 r8 [debugbuilddag] (draft)
| |
@@ -209,7 +232,7 @@
2 changesets folded
0 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg log -G
- @ 15 - 91880abed0f2 r4 [test] (draft)
+ @ 15 - 91880abed0f2 r4 [test] (draft) bm1
|
| o 12 - b568edbee6e0 r8 [debugbuilddag] (draft)
|/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-grab.t Wed Mar 21 16:32:54 2018 +0100
@@ -0,0 +1,331 @@
+Test for the grab command
+
+ $ cat >> $HGRCPATH <<EOF
+ > [alias]
+ > glog = log -G -T "{rev}:{node|short} {desc}\n"
+ > [extensions]
+ > EOF
+ $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext3rd/evolve/" >> $HGRCPATH
+
+ $ mkcommit() {
+ > echo "$1" > "$1"
+ > hg add "$1"
+ > hg ci -m "add $1"
+ > }
+
+ $ hg init repo
+ $ cd repo
+ $ hg help grab
+ hg grab [-r] rev
+
+ grabs a commit, move it on the top of working directory parent and
+ updates to it.
+
+ options:
+
+ -r --rev VALUE revision to grab
+ -c --continue continue interrupted grab
+ -a --abort abort interrupted grab
+
+ (some details hidden, use --verbose to show complete help)
+
+ $ mkcommit a
+ $ mkcommit b
+ $ mkcommit c
+
+ $ hg glog
+ @ 2:4538525df7e2 add c
+ |
+ o 1:7c3bad9141dc add b
+ |
+ o 0:1f0dee641bb7 add a
+
+
+Grabbing an ancestor
+
+ $ hg grab -r 7c3bad9141dc
+ abort: cannot grab an ancestor revision
+ [255]
+
+Grabbing the working directory parent
+
+ $ hg grab -r .
+ abort: cannot grab an ancestor revision
+ [255]
+
+Specifying multiple revisions to grab
+
+ $ hg grab 1f0dee641bb7 -r 7c3bad9141dc
+ abort: specify just one revision
+ [255]
+
+Specifying no revisions to grab
+
+ $ hg grab
+ abort: empty revision set
+ [255]
+
+Continuing without interrupted grab
+
+ $ hg grab --continue
+ abort: no interrupted grab state exists
+ [255]
+
+Aborting without interrupted grab
+
+ $ hg grab --abort
+ abort: no interrupted grab state exists
+ [255]
+
+Specifying both continue and revs
+
+ $ hg up 1f0dee641bb7
+ 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+ $ hg grab -r 4538525df7e2 --continue
+ abort: cannot specify both --continue and revision
+ [255]
+
+Making new branch heads
+
+ $ mkcommit x
+ created new head
+ $ mkcommit y
+
+ $ hg glog
+ @ 4:d46dc301d92f add y
+ |
+ o 3:8e224524cd09 add x
+ |
+ | o 2:4538525df7e2 add c
+ | |
+ | o 1:7c3bad9141dc add b
+ |/
+ o 0:1f0dee641bb7 add a
+
+Grabbing a revision
+
+ $ hg grab 7c3bad9141dc
+ grabbing 1:7c3bad9141dc "add b"
+ $ hg glog
+ @ 5:7c15c05db6fa add b
+ |
+ o 4:d46dc301d92f add y
+ |
+ o 3:8e224524cd09 add x
+ |
+ | o 2:4538525df7e2 add c
+ | |
+ | x 1:7c3bad9141dc add b
+ |/
+ o 0:1f0dee641bb7 add a
+
+
+When grab does not create any changes
+
+ $ hg graft -r 4538525df7e2
+ grafting 2:4538525df7e2 "add c"
+
+ $ hg glog
+ @ 6:c4636a81ebeb add c
+ |
+ o 5:7c15c05db6fa add b
+ |
+ o 4:d46dc301d92f add y
+ |
+ o 3:8e224524cd09 add x
+ |
+ | o 2:4538525df7e2 add c
+ | |
+ | x 1:7c3bad9141dc add b
+ |/
+ o 0:1f0dee641bb7 add a
+
+ $ hg grab -r 4538525df7e2
+ grabbing 2:4538525df7e2 "add c"
+ note: grab of 2:4538525df7e2 created no changes to commit
+
+ $ hg glog
+ @ 6:c4636a81ebeb add c
+ |
+ o 5:7c15c05db6fa add b
+ |
+ o 4:d46dc301d92f add y
+ |
+ o 3:8e224524cd09 add x
+ |
+ o 0:1f0dee641bb7 add a
+
+interrupted grab
+
+ $ hg up d46dc301d92f
+ 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+ $ echo foo > c
+ $ hg ci -Aqm "foo to c"
+ $ hg grab -r c4636a81ebeb
+ grabbing 6:c4636a81ebeb "add c"
+ merging c
+ warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
+ unresolved merge conflicts (see hg help resolve)
+ [1]
+
+ $ echo foobar > c
+ $ hg resolve --all --mark
+ (no more unresolved files)
+ continue: hg grab --continue
+ $ hg grab --continue
+ $ hg glog
+ @ 8:44e155eb95c7 add c
+ |
+ o 7:2ccc03d1d096 foo to c
+ |
+ | o 5:7c15c05db6fa add b
+ |/
+ o 4:d46dc301d92f add y
+ |
+ o 3:8e224524cd09 add x
+ |
+ o 0:1f0dee641bb7 add a
+
+
+When interrupted grab results in no changes to commit
+
+ $ hg up d46dc301d92f
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ echo bar > c
+ $ hg add c
+ $ hg ci -m "foo to c"
+ created new head
+
+ $ hg up 44e155eb95c7
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+ $ hg grab 4e04628911f6
+ grabbing 9:4e04628911f6 "foo to c"
+ merging c
+ warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
+ unresolved merge conflicts (see hg help resolve)
+ [1]
+ $ echo foobar > c
+ $ hg resolve -m
+ (no more unresolved files)
+ continue: hg grab --continue
+
+ $ hg grab --continue
+ note: grab of 9:4e04628911f6 created no changes to commit
+
+Testing the abort functionality of hg grab
+
+ $ echo foo > b
+ $ hg ci -Aqm "foo to b"
+ $ hg glog -r .^::
+ hg: parse error at 4: not a prefix: end
+ [255]
+
+ $ hg grab -r 7c15c05db6fa
+ grabbing 5:7c15c05db6fa "add b"
+ merging b
+ warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
+ unresolved merge conflicts (see hg help resolve)
+ [1]
+
+ $ hg grab --abort
+ aborting grab, updating to c437988de89f
+
+ $ hg glog
+ @ 10:c437988de89f foo to b
+ |
+ o 8:44e155eb95c7 add c
+ |
+ o 7:2ccc03d1d096 foo to c
+ |
+ | o 5:7c15c05db6fa add b
+ |/
+ o 4:d46dc301d92f add y
+ |
+ o 3:8e224524cd09 add x
+ |
+ o 0:1f0dee641bb7 add a
+
+
+Trying to grab a public changeset
+
+ $ hg phase -r 7c15c05db6fa -p
+
+ $ hg grab -r 7c15c05db6fa
+ abort: cannot grab public changesets: 7c15c05db6fa
+ (see 'hg help phases' for details)
+ [255]
+
+ $ hg glog
+ @ 10:c437988de89f foo to b
+ |
+ o 8:44e155eb95c7 add c
+ |
+ o 7:2ccc03d1d096 foo to c
+ |
+ | o 5:7c15c05db6fa add b
+ |/
+ o 4:d46dc301d92f add y
+ |
+ o 3:8e224524cd09 add x
+ |
+ o 0:1f0dee641bb7 add a
+
+Checking phase preservation while grabbing secret changeset
+
+In case of merge conflicts
+
+ $ hg phase -r 7c15c05db6fa -s -f
+
+ $ hg grab -r 7c15c05db6fa
+ grabbing 5:7c15c05db6fa "add b"
+ merging b
+ warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
+ unresolved merge conflicts (see hg help resolve)
+ [1]
+
+ $ echo bar > b
+ $ hg resolve -m
+ (no more unresolved files)
+ continue: hg grab --continue
+
+ $ hg grab --continue
+ $ hg phase -r .
+ 11: secret
+
+No merge conflicts
+
+ $ hg up d46dc301d92f
+ 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
+ $ echo foo > l
+ $ hg add l
+ $ hg ci -qm "added l" --secret
+
+ $ hg phase -r .
+ 12: secret
+
+ $ hg glog
+ @ 12:508d572e7053 added l
+ |
+ | o 11:10427de9e26e add b
+ | |
+ | o 10:c437988de89f foo to b
+ | |
+ | o 8:44e155eb95c7 add c
+ | |
+ | o 7:2ccc03d1d096 foo to c
+ |/
+ o 4:d46dc301d92f add y
+ |
+ o 3:8e224524cd09 add x
+ |
+ o 0:1f0dee641bb7 add a
+
+ $ hg up 10427de9e26e
+ 3 files updated, 0 files merged, 1 files removed, 0 files unresolved
+
+ $ hg grab -r 508d572e7053
+ grabbing 12:508d572e7053 "added l"
+
+ $ hg phase -r .
+ 13: secret
--- a/tests/test-issue-5720.t Sat Jan 20 12:38:10 2018 +0100
+++ b/tests/test-issue-5720.t Wed Mar 21 16:32:54 2018 +0100
@@ -70,10 +70,12 @@
$ echo c2 > a
$ hg resolve -m
(no more unresolved files)
+ continue: hg evolve --continue
Continue the evolution
$ hg evolve --continue
evolving 2:13833940840c "c"
+ working directory is now at 3d2080c198e5
Tip should stay in secret phase
$ hg log -G -T "{rev}: {phase}"
--- a/tests/test-prev-next.t Sat Jan 20 12:38:10 2018 +0100
+++ b/tests/test-prev-next.t Wed Mar 21 16:32:54 2018 +0100
@@ -1,4 +1,6 @@
$ cat >> $HGRCPATH <<EOF
+ > [ui]
+ > interactive = True
> [extensions]
> EOF
$ echo "evolve=$(echo $(dirname $TESTDIR))/hgext3rd/evolve/" >> $HGRCPATH
@@ -35,8 +37,8 @@
hg next -B should move active bookmark
$ hg next -B --dry-run
- hg update 1;
- hg bookmark mark -r 1;
+ hg update 6e742c9127b3;
+ hg bookmark mark -r 6e742c9127b3;
[1] added b
$ hg next -B
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
@@ -46,7 +48,7 @@
hg prev should unset active bookmark
$ hg prev --dry-run
- hg update 0;
+ hg update a154386e50d1;
[0] added a
$ hg prev
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
@@ -60,7 +62,7 @@
mark 1:6e742c9127b3
* mark2 0:a154386e50d1
$ hg next --dry-run --color=debug
- hg update 1;
+ hg update 6e742c9127b3;
[[evolve.rev|1]] added b
$ hg next
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
@@ -172,7 +174,7 @@
no children
[1]
$ hg prev --dry-run --color=debug
- hg update 1;
+ hg update 6e742c9127b3;
[[evolve.rev|1]] added b
$ hg prev
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
@@ -190,12 +192,11 @@
move:[2] added c
atop:[3] added b (2)
hg rebase -r 4e26ef31f919 -d 9ad178109a19
- working directory now at 9ad178109a19
(add color output for smoke testing)
$ hg next --evolve --color debug
- move:[[evolve.rev|2]] added c
+ [evolve.operation|move:][[evolve.rev|2]] added c
atop:[[evolve.rev|3]] added b (2)
[ ui.status|working directory now at [evolve.node|e3b6d5df389b]]
@@ -211,12 +212,20 @@
$ hg prev
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
[3] added b (2)
- $ hg next
- ambiguous next changeset:
- [4] added c
+ $ hg next <<EOF
+ > 1
+ > EOF
+ ambiguous next changeset, choose one to update:
+ 0: [e3b6d5df389b] added c
+ 1: [9df671ccd2c7] added d
+ q: quit the prompt
+ enter the index of the revision you want to select: 1
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
[5] added d
- explicitly update to one of them
- [1]
+
+ $ hg prev
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ [3] added b (2)
next with ambiguity in aspiring children
@@ -226,24 +235,100 @@
no children
(2 unstable changesets to be evolved here, do you want --evolve?)
[1]
- $ hg next --evolve
- ambiguous next (unstable) changeset:
- [4] added c
- [5] added d
- (run 'hg evolve --rev REV' on one of them)
- [1]
+ $ hg next --evolve <<EOF
+ > 0
+ > EOF
+ ambiguous next (unstable) changeset, choose one to evolve and update:
+ 0: [e3b6d5df389b] added c
+ 1: [9df671ccd2c7] added d
+ q: quit the prompt
+ enter the index of the revision you want to select: 0
+ move:[4] added c
+ atop:[6] added b (3)
+ working directory now at 5ce67c2407b0
+
+ $ hg log -GT "{rev}:{node|short} {desc}\n"
+ @ 7:5ce67c2407b0 added c
+ |
+ o 6:d7f119adc759 added b (3)
+ |
+ | o 5:9df671ccd2c7 added d
+ | |
+ | x 3:9ad178109a19 added b (2)
+ |/
+ o 0:a154386e50d1 added a
+
+
$ hg evolve -r 5
move:[5] added d
atop:[6] added b (3)
working directory is now at 47ea25be8aea
+prev with multiple parents
+
+ $ hg log -GT "{rev}:{node|short} {desc}\n"
+ @ 8:47ea25be8aea added d
+ |
+ | o 7:5ce67c2407b0 added c
+ |/
+ o 6:d7f119adc759 added b (3)
+ |
+ o 0:a154386e50d1 added a
+
+ $ hg merge -r 5ce67c2407b0
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ (branch merge, don't forget to commit)
+ $ hg ci -m "merge commit"
+
+ $ hg prev <<EOF
+ > q
+ > EOF
+ multiple parents, choose one to update:
+ 0: [47ea25be8aea] added d
+ 1: [5ce67c2407b0] added c
+ q: quit the prompt
+ enter the index of the revision you want to select: q
+ [8] added d
+ [7] added c
+ multiple parents, explicitly update to one
+ [1]
+
+ $ hg prev --config ui.interactive=False
+ [8] added d
+ [7] added c
+ multiple parents, explicitly update to one
+ [1]
+
+ $ hg prev <<EOF
+ > 1
+ > EOF
+ multiple parents, choose one to update:
+ 0: [47ea25be8aea] added d
+ 1: [5ce67c2407b0] added c
+ q: quit the prompt
+ enter the index of the revision you want to select: 1
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ [7] added c
+
+ $ hg log -GT "{rev}:{node|short} {desc}\n"
+ o 9:a4b8c25a87d3 merge commit
+ |\
+ | o 8:47ea25be8aea added d
+ | |
+ @ | 7:5ce67c2407b0 added c
+ |/
+ o 6:d7f119adc759 added b (3)
+ |
+ o 0:a154386e50d1 added a
+
+
$ cd ..
prev and next should lock properly against other commands
$ hg init repo
$ cd repo
- $ HGEDITOR=${TESTDIR}/fake-editor.sh
+ $ HGEDITOR="sh ${TESTDIR}/fake-editor.sh"
$ echo hi > foo
$ hg ci -Am 'one'
adding foo
--- a/tests/test-prune.t Sat Jan 20 12:38:10 2018 +0100
+++ b/tests/test-prune.t Wed Mar 21 16:32:54 2018 +0100
@@ -357,10 +357,10 @@
user: 7
marker size:
format v1:
- smallest length: 75
- longer length: 75
- median length: 75
- mean length: 75
+ smallest length: * (glob)
+ longer length: * (glob)
+ median length: * (glob)
+ mean length: * (glob)
format v0:
smallest length: * (glob)
longer length: * (glob)
--- a/tests/test-stabilize-conflict.t Sat Jan 20 12:38:10 2018 +0100
+++ b/tests/test-stabilize-conflict.t Wed Mar 21 16:32:54 2018 +0100
@@ -166,8 +166,10 @@
$ safesed 's/dix/ten/' babar
$ hg resolve --all -m
(no more unresolved files)
+ continue: hg evolve --continue
$ hg evolve --continue
evolving 5:71c18f70c34f "babar count up to fifteen"
+ working directory is now at 1836b91c6c1d
$ hg resolve -l
$ hg log -G
@ changeset: 8:1836b91c6c1d
--- a/tests/test-stabilize-result.t Sat Jan 20 12:38:10 2018 +0100
+++ b/tests/test-stabilize-result.t Wed Mar 21 16:32:54 2018 +0100
@@ -101,8 +101,10 @@
[255]
$ hg resolve -m a
(no more unresolved files)
+ continue: hg evolve --continue
$ hg evolve --continue
evolving 5:3655f0f50885 "newer a"
+ working directory is now at 1cf0aacfd363
Stabilize latecomer with different parent
=========================================
@@ -130,8 +132,7 @@
Get a successors of 8 on it
$ hg grab 1cf0aacfd363
- rebasing 8:1cf0aacfd363 "newer a"
- ? files updated, 0 files merged, 0 files removed, 0 files unresolved (glob)
+ grabbing 8:1cf0aacfd363 "newer a"
Add real change to the successors
@@ -143,7 +144,7 @@
$ hg phase --hidden --public 1cf0aacfd363
1 new bumped changesets
$ glog
- @ 12:(73b15c7566e9|d5c7ef82d003)@default\(draft\) bk:\[\] newer a (re)
+ @ 12:99c21c89bcef@default(draft) bk:[] newer a
|
o 9:7bc2f5967f5e@default(draft) bk:[] add c
|
@@ -159,10 +160,10 @@
$ hg evolve --any --dry-run --phase-divergent
recreate:[12] newer a
atop:[8] newer a
- hg rebase --rev (73b15c7566e9|d5c7ef82d003) --dest 66719795a494; (re)
+ hg rebase --rev 99c21c89bcef --dest 66719795a494;
hg update 1cf0aacfd363;
- hg revert --all --rev (73b15c7566e9|d5c7ef82d003); (re)
- hg commit --msg "bumped update to d5c7ef82d003"
+ hg revert --all --rev 99c21c89bcef;
+ hg commit --msg "bumped update to 99c21c89bcef"
$ hg evolve --any --confirm --phase-divergent
recreate:[12] newer a
atop:[8] newer a
@@ -175,10 +176,10 @@
perform evolve? [Ny] y
rebasing to destination parent: 66719795a494
computing new diff
- committed as c2c1151aa854
- working directory is now at c2c1151aa854
+ committed as e34e87ea7b83
+ working directory is now at e34e87ea7b83
$ glog
- @ 14:c2c1151aa854@default(draft) bk:[] bumped update to 1cf0aacfd363:
+ @ 14:e34e87ea7b83@default(draft) bk:[] bumped update to 1cf0aacfd363:
|
| o 9:7bc2f5967f5e@default(draft) bk:[] add c
| |
--- a/tests/test-topic-change.t Sat Jan 20 12:38:10 2018 +0100
+++ b/tests/test-topic-change.t Wed Mar 21 16:32:54 2018 +0100
@@ -98,7 +98,7 @@
foo (8 changesets)
Changing topics on some revisions (also testing issue 5441)
- $ hg topic -r 12:: bar
+ $ hg topic -r abcedffeae90:: bar
switching to topic bar
changed topic on 4 changes
$ hg glog
--- a/tests/test-tutorial.t Sat Jan 20 12:38:10 2018 +0100
+++ b/tests/test-tutorial.t Wed Mar 21 16:32:54 2018 +0100
@@ -637,11 +637,10 @@
$ hg up 'p1(10b8aeaa8cc8)' # going on "bathroom stuff" parent
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg grab fac207dec9f5 # moving "SPAM SPAM" to the working directory parent
- rebasing 10:fac207dec9f5 "SPAM SPAM" (tip)
+ grabbing 10:fac207dec9f5 "SPAM SPAM"
merging shopping
- ? files updated, 0 files merged, 0 files removed, 0 files unresolved (glob)
$ hg log -G
- @ a224f2a4fb9f (draft): SPAM SPAM
+ @ 57e9caedbcb8 (draft): SPAM SPAM
|
| o 10b8aeaa8cc8 (draft): bathroom stuff
|/
@@ -709,7 +708,7 @@
# User test
# Date 0 0
# Thu Jan 01 00:00:00 1970 +0000
- # Node ID a224f2a4fb9f9f828f608959912229d7b38b26de
+ # Node ID 57e9caedbcb8575a01c128db9d1bcbd624ef2115
# Parent 41aff6a42b7578ec7ec3cb2041633f1ca43cca96
SPAM SPAM
@@ -742,14 +741,13 @@
for simplicity sake we get the bathroom change in line again
$ hg grab 10b8aeaa8cc8
- rebasing 9:10b8aeaa8cc8 "bathroom stuff"
+ grabbing 9:10b8aeaa8cc8 "bathroom stuff"
merging shopping
- ? files updated, 0 files merged, 0 files removed, 0 files unresolved (glob)
$ hg phase --draft .
$ hg log -G
- @ 75954b8cd933 (draft): bathroom stuff
+ @ 4710c0968793 (draft): bathroom stuff
|
- o a224f2a4fb9f (public): SPAM SPAM
+ o 57e9caedbcb8 (public): SPAM SPAM
|
o 41aff6a42b75 (public): adding fruit
|
@@ -914,9 +912,9 @@
1 new obsolescence markers
(run 'hg update' to get a working copy)
$ hg log -G
- o 75954b8cd933 (public): bathroom stuff
+ o 4710c0968793 (public): bathroom stuff
|
- o a224f2a4fb9f (public): SPAM SPAM
+ o 57e9caedbcb8 (public): SPAM SPAM
|
o 41aff6a42b75 (public): adding fruit
|
@@ -934,7 +932,7 @@
$ hg rollback
repository tip rolled back to revision 4 (undo pull)
$ hg log -G
- o a224f2a4fb9f (public): SPAM SPAM
+ o 57e9caedbcb8 (public): SPAM SPAM
|
o 41aff6a42b75 (public): adding fruit
|
@@ -969,9 +967,9 @@
1 new obsolescence markers
(run 'hg update' to get a working copy)
$ hg log -G
- o 75954b8cd933 (draft): bathroom stuff
+ o 4710c0968793 (draft): bathroom stuff
|
- o a224f2a4fb9f (public): SPAM SPAM
+ o 57e9caedbcb8 (public): SPAM SPAM
|
o 41aff6a42b75 (public): adding fruit
|
@@ -987,7 +985,7 @@
Remotely someone add a new changeset on top of the mutable "bathroom" on.
- $ hg up 75954b8cd933 -q
+ $ hg up 4710c0968793 -q
$ cat >> shopping << EOF
> Giraffe
> Rhino
@@ -1000,12 +998,14 @@
$ cd ../local
$ hg up 75954b8cd933 -q
+ abort: unknown revision '75954b8cd933'!
+ [255]
$ sed -i'' -e 's/... More bathroom stuff to come/Bath Robe/' shopping
$ hg commit --amend
$ hg log -G
- @ a44c85f957d3 (draft): bathroom stuff
+ @ 682004e81e71 (draft): bathroom stuff
|
- o a224f2a4fb9f (public): SPAM SPAM
+ o 57e9caedbcb8 (public): SPAM SPAM
|
o 41aff6a42b75 (public): adding fruit
|
@@ -1066,18 +1066,17 @@
(run 'hg heads' to see heads, 'hg merge' to merge)
1 new unstable changesets
-
The new changeset "animal" is based on an old changeset of "bathroom". You can
see both version showing up in the log.
$ hg log -G
- o bf1b0d202029 (draft): animals
+ o e4e4fa805d92 (draft): animals
|
- | @ a44c85f957d3 (draft): bathroom stuff
+ | @ 682004e81e71 (draft): bathroom stuff
| |
- x | 75954b8cd933 (draft): bathroom stuff
+ x | 4710c0968793 (draft): bathroom stuff
|/
- o a224f2a4fb9f (public): SPAM SPAM
+ o 57e9caedbcb8 (public): SPAM SPAM
|
o 41aff6a42b75 (public): adding fruit
|
@@ -1121,7 +1120,7 @@
$ hg push other
pushing to $TESTTMP/other (glob)
searching for changes
- abort: push includes unstable changeset: bf1b0d202029!
+ abort: push includes unstable changeset: e4e4fa805d92!
(use 'hg evolve' to get a stable history or --force to ignore warnings)
[255]
@@ -1134,7 +1133,7 @@
$ hg evolve --dry-run
move:[15] animals
atop:[14] bathroom stuff
- hg rebase -r bf1b0d202029 -d a44c85f957d3
+ hg rebase -r e4e4fa805d92 -d 682004e81e71
Let's do it
@@ -1142,16 +1141,16 @@
move:[15] animals
atop:[14] bathroom stuff
merging shopping
- working directory is now at ee942144f952
+ working directory is now at 2a2b36e14660
The old version of bathroom is hidden again.
$ hg log -G
- @ ee942144f952 (draft): animals
+ @ 2a2b36e14660 (draft): animals
|
- o a44c85f957d3 (draft): bathroom stuff
+ o 682004e81e71 (draft): bathroom stuff
|
- o a224f2a4fb9f (public): SPAM SPAM
+ o 57e9caedbcb8 (public): SPAM SPAM
|
o 41aff6a42b75 (public): adding fruit
|
@@ -1220,13 +1219,13 @@
Now let's see where we are, and update to the successor.
$ hg parents
- bf1b0d202029 (draft): animals
- working directory parent is obsolete! (bf1b0d202029)
- (use 'hg evolve' to update to its successor: ee942144f952)
+ e4e4fa805d92 (draft): animals
+ working directory parent is obsolete! (e4e4fa805d92)
+ (use 'hg evolve' to update to its successor: 2a2b36e14660)
$ hg evolve
update:[8] animals
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- working directory is now at ee942144f952
+ working directory is now at 2a2b36e14660
Relocating unstable change after prune
----------------------------------------------
@@ -1248,13 +1247,13 @@
added 1 changesets with 1 changes to 1 files
(run 'hg update' to get a working copy)
$ hg log -G
- o 99f039c5ec9e (draft): SPAM SPAM SPAM
+ o fc41faf45288 (draft): SPAM SPAM SPAM
|
- @ ee942144f952 (draft): animals
+ @ 2a2b36e14660 (draft): animals
|
- o a44c85f957d3 (draft): bathroom stuff
+ o 682004e81e71 (draft): bathroom stuff
|
- o a224f2a4fb9f (public): SPAM SPAM
+ o 57e9caedbcb8 (public): SPAM SPAM
|
o 41aff6a42b75 (public): adding fruit
|
@@ -1340,9 +1339,9 @@
In the mean time I noticed you can't buy animals in a super market and I prune the animal changeset:
- $ hg prune ee942144f952
+ $ hg prune 2a2b36e14660
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- working directory now at a44c85f957d3
+ working directory now at 682004e81e71
1 changesets pruned
1 new unstable changesets
@@ -1351,13 +1350,13 @@
is neither dead or obsolete. My repository is in an unstable state again.
$ hg log -G
- o 99f039c5ec9e (draft): SPAM SPAM SPAM
+ o fc41faf45288 (draft): SPAM SPAM SPAM
|
- x ee942144f952 (draft): animals
+ x 2a2b36e14660 (draft): animals
|
- @ a44c85f957d3 (draft): bathroom stuff
+ @ 682004e81e71 (draft): bathroom stuff
|
- o a224f2a4fb9f (public): SPAM SPAM
+ o 57e9caedbcb8 (public): SPAM SPAM
|
o 41aff6a42b75 (public): adding fruit
|
@@ -1453,7 +1452,7 @@
#endif
$ hg log -r "orphan()"
- 99f039c5ec9e (draft): SPAM SPAM SPAM
+ fc41faf45288 (draft): SPAM SPAM SPAM
#if docgraph-ext
$ hg docgraph -r "orphan()" --sphinx-directive --rankdir LR #rest-ignore
@@ -1479,14 +1478,14 @@
move:[17] SPAM SPAM SPAM
atop:[14] bathroom stuff
merging shopping
- working directory is now at 40aa40daeefb
+ working directory is now at e6cfcb672150
$ hg log -G
- @ 40aa40daeefb (draft): SPAM SPAM SPAM
+ @ e6cfcb672150 (draft): SPAM SPAM SPAM
|
- o a44c85f957d3 (draft): bathroom stuff
+ o 682004e81e71 (draft): bathroom stuff
|
- o a224f2a4fb9f (public): SPAM SPAM
+ o 57e9caedbcb8 (public): SPAM SPAM
|
o 41aff6a42b75 (public): adding fruit
|
--- a/tests/test-unstable.t Sat Jan 20 12:38:10 2018 +0100
+++ b/tests/test-unstable.t Wed Mar 21 16:32:54 2018 +0100
@@ -68,154 +68,6 @@
$ cd ..
===============================================================================
-Test instability resolution for a merge changeset unstable because one
-of its parent is obsolete
-Not supported yet
-==============================================================================
-
- $ hg init test2
- $ cd test2
- $ mkcommit base
- $ mkcommits _a
- $ hg up "desc(base)"
- 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
- $ mkcommit _c
- created new head
- $ hg merge "desc(_a)" >/dev/null
- $ hg commit -m "merge"
- $ hg up "desc(_a)"
- 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
- $ hg amend -m "aprime"
- 1 new unstable changesets
- $ hg log -G
- @ 4:47127ea62e5f@default(draft) aprime
- |
- | o 3:6b4280e33286@default(draft) merge
- | |\
- +---o 2:474da87dd33b@default(draft) add _c
- | |
- | x 1:b3264cec9506@default(draft) add _a
- |/
- o 0:b4952fcf48cf@default(draft) add base
-
-
- $ hg evo --all --any --orphan
- move:[3] merge
- atop:[4] aprime
- working directory is now at 0bf3f3a59c8c
- $ hg log -G
- @ 5:0bf3f3a59c8c@default(draft) merge
- |\
- | o 4:47127ea62e5f@default(draft) aprime
- | |
- o | 2:474da87dd33b@default(draft) add _c
- |/
- o 0:b4952fcf48cf@default(draft) add base
-
-
- $ cd ..
-
-===============================================================================
-Test instability resolution for a merge changeset unstable because both
-of its parent are obsolete
-Not supported yet
-==============================================================================
-
- $ hg init test3
- $ cd test3
- $ mkcommit base
- $ mkcommits _a
- $ hg up "desc(base)"
- 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
- $ mkcommit _c
- created new head
- $ hg merge "desc(_a)" >/dev/null
- $ hg commit -m "merge"
- $ hg up "desc(_a)"
- 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
- $ hg amend -m "aprime"
- 1 new unstable changesets
- $ hg up "desc(_c)"
- 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
- $ hg amend -m "cprime"
- $ hg log -G
- @ 5:2db39fda7e2f@default(draft) cprime
- |
- | o 4:47127ea62e5f@default(draft) aprime
- |/
- | o 3:6b4280e33286@default(draft) merge
- | |\
- +---x 2:474da87dd33b@default(draft) add _c
- | |
- | x 1:b3264cec9506@default(draft) add _a
- |/
- o 0:b4952fcf48cf@default(draft) add base
-
-
- $ hg evo --all --any --orphan
- warning: no support for evolving merge changesets with two obsolete parents yet
- (Redo the merge (6b4280e33286) and use `hg prune <old> --succ <new>` to obsolete the old one)
- $ hg log -G
- @ 5:2db39fda7e2f@default(draft) cprime
- |
- | o 4:47127ea62e5f@default(draft) aprime
- |/
- | o 3:6b4280e33286@default(draft) merge
- | |\
- +---x 2:474da87dd33b@default(draft) add _c
- | |
- | x 1:b3264cec9506@default(draft) add _a
- |/
- o 0:b4952fcf48cf@default(draft) add base
-
-
- $ cd ..
-
-===============================================================================
-Test instability resolution for a changeset unstable because its parent
-is obsolete with multiple successors all in one chain (simple split)
-==============================================================================
-
- $ hg init test4
- $ cd test4
- $ mkcommits _a _b _c
- $ hg up "desc(_a)"
- 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
- $ mkcommits bprimesplit1 bprimesplit2
- created new head
- $ hg prune "desc(_b)" -s "desc(bprimesplit1) + desc(bprimesplit2)" --split
- 1 changesets pruned
- 1 new unstable changesets
- $ hg log -G
- @ 4:2a4ccc0bb20c@default(draft) add bprimesplit2
- |
- o 3:8b87864bd0f4@default(draft) add bprimesplit1
- |
- | o 2:102002290587@default(draft) add _c
- | |
- | x 1:37445b16603b@default(draft) add _b
- |/
- o 0:135f39f4bd78@default(draft) add _a
-
-
- $ hg evo --all --any --orphan
- move:[2] add _c
- atop:[4] add bprimesplit2
- working directory is now at 387cc1e837d7
- $ hg log -G
- @ 5:387cc1e837d7@default(draft) add _c
- |
- o 4:2a4ccc0bb20c@default(draft) add bprimesplit2
- |
- o 3:8b87864bd0f4@default(draft) add bprimesplit1
- |
- o 0:135f39f4bd78@default(draft) add _a
-
-
-
- $ cd ..
-
-===============================================================================
Test instability resolution for a changeset unstable because its parent
is obsolete with multiple successors on one branches but in reverse
order (cross-split).
@@ -266,55 +118,4 @@
o 0:135f39f4bd78@default(draft) add _a
-
$ cd ..
-
-===============================================================================
-Test instability resolution for a changeset unstable because its parent
-is obsolete with multiple successors on two branches.
-Not supported yet
-==============================================================================
-
- $ hg init test6
- $ cd test6
- $ mkcommits _a _b _c
- $ hg up "desc(_a)"
- 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
- $ mkcommit bprimesplit1
- created new head
- $ hg up "desc(_a)"
- 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
- $ mkcommit bprimesplit2
- created new head
- $ hg prune "desc(_b)" -s "desc(bprimesplit1) + desc(bprimesplit2)" --split
- 1 changesets pruned
- 1 new unstable changesets
- $ hg log -G
- @ 4:3c69ea6aa93e@default(draft) add bprimesplit2
- |
- | o 3:8b87864bd0f4@default(draft) add bprimesplit1
- |/
- | o 2:102002290587@default(draft) add _c
- | |
- | x 1:37445b16603b@default(draft) add _b
- |/
- o 0:135f39f4bd78@default(draft) add _a
-
-
- $ hg evo --all --any --orphan
- cannot solve split across two branches
- $ hg log -G
- @ 4:3c69ea6aa93e@default(draft) add bprimesplit2
- |
- | o 3:8b87864bd0f4@default(draft) add bprimesplit1
- |/
- | o 2:102002290587@default(draft) add _c
- | |
- | x 1:37445b16603b@default(draft) add _b
- |/
- o 0:135f39f4bd78@default(draft) add _a
-
-
-
- $ cd ..
-
--- a/tests/test-wireproto.t Sat Jan 20 12:38:10 2018 +0100
+++ b/tests/test-wireproto.t Wed Mar 21 16:32:54 2018 +0100
@@ -194,6 +194,7 @@
$ hg debugpushkey http://localhost:$HGPORT/ obsolete
abort: HTTP Error 410: won't exchange obsmarkers through pushkey
[255]
+ $ cat errors.log
$ hg debugpushkey ssh://user@dummy/server obsolete
remote: abort: won't exchange obsmarkers through pushkey
remote: (upgrade your client or server to use the bundle2 protocol)